dolibarr 24.0.0-beta
doc_generic_ticket_odt.modules.php
Go to the documentation of this file.
1<?php
2
3/* Copyright (C) 2010-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2012 Juanjo Menent <jmenent@2byte.es>
5 * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
7*
8* This program is free software; you can redistribute it and/or modify
9* it under the terms of the GNU General Public License as published by
10* the Free Software Foundation; either version 3 of the License, or
11* (at your option) any later version.
12*
13* This program is distributed in the hope that it will be useful,
14* but WITHOUT ANY WARRANTY; without even the implied warranty of
15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16* GNU General Public License for more details.
17*
18* You should have received a copy of the GNU General Public License
19* along with this program. If not, see <https://www.gnu.org/licenses/>.
20* or see https://www.gnu.org/
21*/
22
29require_once DOL_DOCUMENT_ROOT.'/core/modules/ticket/modules_ticket.php';
30require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
31require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
32require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
33require_once DOL_DOCUMENT_ROOT.'/core/lib/doc.lib.php';
34
35
40{
45 public $version = 'dolibarr';
46
47
53 public function __construct($db)
54 {
55 global $langs, $mysoc;
56
57 // Load translation files required by the page
58 $langs->loadLangs(array("main", "companies"));
59
60 $this->db = $db;
61 $this->name = "ODT templates";
62 $this->description = $langs->trans("DocumentModelOdt");
63 $this->scandir = 'TICKET_ADDON_PDF_ODT_PATH'; // Name of constant that is used to save list of directories to scan
64
65 // Page size for A4 format
66 $this->type = 'odt';
67 $this->page_largeur = 0;
68 $this->page_hauteur = 0;
69 $this->format = array($this->page_largeur, $this->page_hauteur);
70 $this->marge_gauche = 0;
71 $this->marge_droite = 0;
72 $this->marge_haute = 0;
73 $this->marge_basse = 0;
74
75 $this->option_logo = 1; // Display logo
76 $this->option_tva = 0; // Manage the vat option USER_TVAOPTION
77 $this->option_multilang = 1; // Available in several languages
78 $this->option_freetext = 0; // Support add of a personalised text
79 $this->option_draft_watermark = 0; // Support add of a watermark on drafts
80
81 if ($mysoc === null) {
82 dol_syslog(get_class($this).'::__construct() Global $mysoc should not be null.'. getCallerInfoString(), LOG_ERR);
83 return;
84 }
85
86 // Get source company
87 $this->emetteur = $mysoc;
88 if (!$this->emetteur->country_code) {
89 $this->emetteur->country_code = substr($langs->defaultlang, -2); // By default if not defined
90 }
91 }
92
93
100 public function info($langs)
101 {
102 global $langs;
103
104 // Load translation files required by the page
105 $langs->loadLangs(array('companies', 'errors'));
106
107 $form = new Form($this->db);
108
109 $texte = $this->description.".<br>\n";
110 $texte .= '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" enctype="multipart/form-data">';
111 $texte .= '<input type="hidden" name="token" value="'.newToken().'">';
112 $texte .= '<input type="hidden" name="page_y" value="">';
113 $texte .= '<input type="hidden" name="action" value="setModuleOptions">';
114 $texte .= '<input type="hidden" name="param1" value="TICKET_ADDON_PDF_ODT_PATH">';
115
116 $texte .= '<table class="nobordernopadding" width="100%">';
117
118 // List of directories area
119 $texte .= '<tr><td>';
120 $texttitle = $langs->trans("ListOfDirectories");
121 $listofdir = explode(',', preg_replace('/[\r\n]+/', ',', trim(getDolGlobalString('TICKET_ADDON_PDF_ODT_PATH'))));
122 $listoffiles = array();
123 foreach ($listofdir as $key => $tmpdir) {
124 $tmpdir = trim($tmpdir);
125 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
126 if (!$tmpdir) {
127 unset($listofdir[$key]);
128 continue;
129 }
130 if (!is_dir($tmpdir)) {
131 $texttitle .= img_warning($langs->trans("ErrorDirNotFound", $tmpdir), '');
132 } else {
133 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.(ods|odt)');
134 if (count($tmpfiles)) {
135 $listoffiles = array_merge($listoffiles, $tmpfiles);
136 }
137 }
138 }
139 $texthelp = $langs->trans("ListOfDirectoriesForModelGenODT");
140 $texthelp .= '<br><br><span class="opacitymedium">'.$langs->trans("ExampleOfDirectoriesForModelGen").'</span>';
141 // Add list of substitution keys
142 $texthelp .= '<br>'.$langs->trans("FollowingSubstitutionKeysCanBeUsed").'<br>';
143 $texthelp .= $langs->transnoentitiesnoconv("FullListOnOnlineDocumentation"); // This contains an url, we don't modify it
144
145 $texte .= $form->textwithpicto($texttitle, $texthelp, 1, 'help', '', 1, 3, $this->name);
146 $texte .= '<div><div style="display: inline-block; min-width: 100px; vertical-align: middle;">';
147 $texte .= '<textarea class="flat textareafordir" spellcheck="false" cols="60" name="value1">';
148 $texte .= getDolGlobalString('TICKET_ADDON_PDF_ODT_PATH');
149 $texte .= '</textarea>';
150 $texte .= '</div><div style="display: inline-block; vertical-align: middle;">';
151 $texte .= '<input type="submit" class="button button-edit reposition smallpaddingimp" name="modify" value="'.dol_escape_htmltag($langs->trans("Modify")).'">';
152 $texte .= '<br></div></div>';
153
154 // Scan directories
155 if (count($listofdir)) {
156 $texte .= $langs->trans("NumberOfModelFilesFound").': <b>'.count($listoffiles).'</b>';
157
158 $texte .= '<div id="div_'.get_class($this).'" class="hiddenx">';
159 // Show list of found files
160 foreach ($listoffiles as $file) {
161 $texte .= '- '.$file['name'].' <a href="'.DOL_URL_ROOT.'/document.php?modulepart=doctemplates&file=tickets/'.urlencode(basename($file['name'])).'">'.img_picto('', 'listlight').'</a>';
162 $texte .= ' &nbsp; <a class="reposition" href="'.$_SERVER["PHP_SELF"].'?modulepart=doctemplates&keyforuploaddir=TICKET_ADDON_PDF_ODT_PATH&action=deletefile&token='.newToken().'&file='.urlencode(basename($file['name'])).'">'.img_picto('', 'delete').'</a>';
163 $texte .= '<br>';
164 }
165 $texte .= '</div>';
166 }
167 // Add input to upload a new template file.
168 $texte .= '<div>'.$langs->trans("UploadNewTemplate");
169 $maxfilesizearray = getMaxFileSizeArray();
170 $maxmin = $maxfilesizearray['maxmin'];
171 if ($maxmin > 0) {
172 $texte .= '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">'; // MAX_FILE_SIZE must precede the field type=file
173 }
174 $texte .= ' <input type="file" name="uploadfile">';
175 $texte .= '<input type="hidden" value="TICKET_ADDON_PDF_ODT_PATH" name="keyforuploaddir">';
176 $texte .= '<input type="submit" class="button smallpaddingimp reposition" value="'.dol_escape_htmltag($langs->trans("Upload")).'" name="upload">';
177 $texte .= '</div>';
178
179 $texte .= '</td>';
180
181 $texte .= '</tr>';
182
183 $texte .= '</table>';
184 $texte .= '</form>';
185
186 return $texte;
187 }
188
189 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
201 public function write_file($object, $outputlangs, $srctemplatepath = '', $hidedetails = 0, $hidedesc = 0, $hideref = 0)
202 {
203 // phpcs:enable
204 global $user, $langs, $conf, $mysoc, $hookmanager;
205
206 if (empty($srctemplatepath)) {
207 dol_syslog("doc_generic_odt::write_file parameter srctemplatepath empty", LOG_WARNING);
208 return -1;
209 }
210
211 // Add odtgeneration hook
212 if (!is_object($hookmanager)) {
213 include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
214 $hookmanager = new HookManager($this->db);
215 }
216 $hookmanager->initHooks(array('odtgeneration'));
217 global $action;
218
219 if (!is_object($outputlangs)) {
220 $outputlangs = $langs;
221 }
222 $sav_charset_output = $outputlangs->charset_output;
223 $outputlangs->charset_output = 'UTF-8';
224
225 // Load translation files required by the page
226 $outputlangs->loadLangs(array("main", "companies", "bills", "dict"));
227
228 if ($conf->ticket->dir_output) {
229 // If $object is id instead of object
230 if (!is_object($object)) {
231 $id = $object;
232 $object = new User($this->db);
233 $result = $object->fetch($id);
234 if ($result < 0) {
235 dol_print_error($this->db, $object->error);
236 return -1;
237 }
238 }
239
240 $object->fetch_thirdparty();
241
242 $dir = $conf->ticket->dir_output;
243 $objectref = dol_sanitizeFileName($object->ref);
244 if (!preg_match('/specimen/i', $objectref)) {
245 $dir .= "/".$objectref;
246 }
247 $file = $dir."/".$objectref.".odt";
248
249 if (!file_exists($dir)) {
250 if (dol_mkdir($dir) < 0) {
251 $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
252 return -1;
253 }
254 }
255
256 if (file_exists($dir)) {
257 //print "srctemplatepath=".$srctemplatepath; // Src filename
258 $newfile = basename($srctemplatepath);
259 $newfiletmp = preg_replace('/\.od[ts]/i', '', $newfile);
260 $newfiletmp = preg_replace('/template_/i', '', $newfiletmp);
261 $newfiletmp = preg_replace('/modele_/i', '', $newfiletmp);
262
263 $newfiletmp = $objectref . '_' . $newfiletmp;
264
265 // Get extension (ods or odt)
266 $newfileformat = substr($newfile, strrpos($newfile, '.') + 1);
267 if (getDolGlobalString('MAIN_DOC_USE_TIMING')) {
268 $format = getDolGlobalString('MAIN_DOC_USE_TIMING');
269 if ($format == '1') {
270 $format = '%Y%m%d%H%M%S';
271 }
272 $filename = $newfiletmp . '-' . dol_print_date(dol_now(), $format) . '.' . $newfileformat;
273 } else {
274 $filename = $newfiletmp . '.' . $newfileformat;
275 }
276 $file = $dir . '/' . $filename;
277 //print "newdir=".$dir;
278 //print "newfile=".$newfile;
279 //print "file=".$file;
280 //print "conf->ticket->dir_temp=".$conf->ticket->dir_temp;
281
282 dol_mkdir($conf->ticket->dir_temp);
283 if (!is_writable($conf->ticket->dir_temp)) {
284 $this->error = $langs->transnoentities("ErrorFailedToWriteInTempDirectory", $conf->ticket->dir_temp);
285 dol_syslog('Error in write_file: ' . $this->error, LOG_ERR);
286 return -1;
287 }
288
289 // If CUSTOMER contact defined on user, we use it
290 $usecontact = false;
291 $arrayidcontact = $object->getIdContact('external', 'CUSTOMER');
292 if (count($arrayidcontact) > 0) {
293 $usecontact = true;
294 $result = $object->fetch_contact($arrayidcontact[0]);
295 }
296
297 $contactobject = null;
298 // Recipient name
299 if (!empty($usecontact)) {
300 // We can use the company of contact instead of thirdparty company
301 if ($object->contact->socid != $object->thirdparty->id && (!isset($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT) || getDolGlobalString('MAIN_USE_COMPANY_NAME_OF_CONTACT'))) {
302 $object->contact->fetch_thirdparty();
303 $socobject = $object->contact->thirdparty;
304 $contactobject = $object->contact;
305 } else {
306 $socobject = $object->thirdparty;
307 // if we have a CUSTOMER contact and we don't use it as thirdparty recipient we store the contact object for later use
308 $contactobject = $object->contact;
309 }
310 } else {
311 $socobject = $object->thirdparty;
312 }
313
314 // Open and load template
315 require_once ODTPHP_PATH.'odf.php';
316 try {
317 $odfHandler = new Odf(
318 $srctemplatepath,
319 array(
320 'PATH_TO_TMP' => $conf->ticket->dir_temp,
321 'ZIP_PROXY' => getDolGlobalString('MAIN_ODF_ZIP_PROXY', 'PclZipProxy'), // PhpZipProxy or PclZipProxy. Got "bad compression method" error when using PhpZipProxy.
322 'DELIMITER_LEFT' => '{',
323 'DELIMITER_RIGHT' => '}'
324 )
325 );
326 } catch (Exception $e) {
327 $this->error = $e->getMessage();
328 dol_syslog($e->getMessage(), LOG_WARNING);
329 return -1;
330 }
331
332 // Make substitutions into odt
333 $array_user = $this->get_substitutionarray_user($user, $outputlangs);
334 $array_soc = $this->get_substitutionarray_mysoc($mysoc, $outputlangs);
335 $array_thirdparty = $this->get_substitutionarray_thirdparty($socobject, $outputlangs);
336 $array_other = $this->get_substitutionarray_other($outputlangs);
337 // retrieve contact information for use in object as contact_xxx tags
338 $array_thirdparty_contact = array();
339 if ($usecontact && is_object($contactobject)) {
340 $array_thirdparty_contact = $this->get_substitutionarray_contact($contactobject, $outputlangs, 'contact');
341 }
342
343 $tmparray = array_merge($array_user, $array_soc, $array_thirdparty, $array_other, $array_thirdparty_contact);
344 complete_substitutions_array($tmparray, $outputlangs, $object);
345 $object->fetch_optionals();
346 // Call the ODTSubstitution hook
347 $parameters = array('file' => $file, 'object' => $object, 'outputlangs' => $outputlangs, 'substitutionarray' => &$tmparray);
348 $reshook = $hookmanager->executeHooks('ODTSubstitution', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
349
350 // retrieve the constant to apply a ratio for image size or set the ratio to 1
351 if (getDolGlobalString('MAIN_DOC_ODT_IMAGE_RATIO')) {
352 $ratio = (float) getDolGlobalString('MAIN_DOC_ODT_IMAGE_RATIO');
353 } else {
354 $ratio = 1;
355 }
356
357 foreach ($tmparray as $key => $value) {
358 try {
359 if (preg_match('/logo$/', $key)) { // Image
360 if (file_exists($value)) {
361 $odfHandler->setImage($key, $value, $ratio);
362 } else {
363 $odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8');
364 }
365 } else { // Text
366 $odfHandler->setVars($key, $value, true, 'UTF-8');
367 }
368 } catch (OdfException $e) {
369 dol_syslog($e->getMessage(), LOG_WARNING);
370 }
371 }
372
373 // Replace labels translated
374 $tmparray = $outputlangs->get_translations_for_substitutions();
375 foreach ($tmparray as $key => $value) {
376 try {
377 $odfHandler->setVars($key, $value, true, 'UTF-8');
378 } catch (OdfException $e) {
379 dol_syslog($e->getMessage(), LOG_WARNING);
380 }
381 }
382
383 // Call the beforeODTSave hook
384 $parameters = array('odfHandler' => &$odfHandler, 'file' => $file, 'object' => $object, 'outputlangs' => $outputlangs);
385 $reshook = $hookmanager->executeHooks('beforeODTSave', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
386
387 // Write new file
388 if (getDolGlobalString('MAIN_ODT_AS_PDF')) {
389 try {
390 $odfHandler->exportAsAttachedPDF($file);
391 } catch (Exception $e) {
392 $this->error = $e->getMessage();
393 dol_syslog($e->getMessage(), LOG_WARNING);
394 return -1;
395 }
396 } else {
397 try {
398 $odfHandler->saveToDisk($file);
399 } catch (Exception $e) {
400 $this->error = $e->getMessage();
401 dol_syslog($e->getMessage(), LOG_WARNING);
402 return -1;
403 }
404 }
405
406 $reshook = $hookmanager->executeHooks('afterODTCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
407
408 dolChmod($file);
409
410 $odfHandler = null; // Destroy object
411
412 $this->result = array('fullpath' => $file);
413
414 return 1; // Success
415 } else {
416 $this->error = $langs->transnoentities("ErrorCanNotCreateDir", $dir);
417 return -1;
418 }
419 }
420
421 return -1;
422 }
423
424 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
433 public function get_substitutionarray_object($object, $outputlangs, $array_key = 'object')
434 {
435 // phpcs:enable
436 if (!$object instanceof User) {
437 dol_syslog("Expected Ticket object, got ".gettype($object), LOG_ERR);
438 return array();
439 }
440
441 $array_other = array();
442 foreach ($object as $key => $value) {
443 if (!is_array($value) && !is_object($value)) {
444 $array_other[$array_key.'_'.$key] = $value;
445 }
446 }
447
448 return $array_other;
449 }
450}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
get_substitutionarray_mysoc($mysoc, $outputlangs)
Define array with couple substitution key => substitution value.
get_substitutionarray_contact($object, $outputlangs, $array_key='object')
Define array with couple substitution key => substitution value.
get_substitutionarray_other($outputlangs)
Define array with couple substitution key => substitution value.
get_substitutionarray_thirdparty($object, $outputlangs, $array_key='company')
Define array with couple substitution key => substitution value For example {company_name}...
get_substitutionarray_user($user, $outputlangs)
Define array with couple substitution key => substitution value.
Class to manage generation of HTML components Only common components must be here.
Class to manage hooks.
Parent class for documents models.
Class to manage Dolibarr users.
Class to build documents using ODF templates generator.
write_file($object, $outputlangs, $srctemplatepath='', $hidedetails=0, $hidedesc=0, $hideref=0)
Function to build a document on disk using the generic odt module.
info($langs)
Return description of a module.
get_substitutionarray_object($object, $outputlangs, $array_key='object')
get substitution array for object
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
dol_now($mode='gmt')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dolChmod($filepath, $newmask='')
Change mod of a file.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133
getMaxFileSizeArray()
Return the max allowed for file upload.