dolibarr 24.0.2
onlineSign.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
3 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
25if (!defined('NOTOKENRENEWAL')) {
26 define('NOTOKENRENEWAL', '1'); // Disables token renewal
27}
28if (!defined('NOREQUIREHTML')) {
29 define('NOREQUIREHTML', '1');
30}
31if (!defined('NOREQUIREAJAX')) {
32 define('NOREQUIREAJAX', '1');
33}
34// Needed to create other objects with workflow
35/*if (!defined('NOREQUIRESOC')) {
36 define('NOREQUIRESOC', '1');
37}*/
38// Do not check anti CSRF attack test
39if (!defined('NOREQUIREMENU')) {
40 define('NOREQUIREMENU', '1');
41}
42// If there is no need to load and show top and left menu
43if (!defined("NOLOGIN")) {
44 define("NOLOGIN", '1');
45}
46if (!defined('NOIPCHECK')) {
47 define('NOIPCHECK', '1'); // Do not check IP defined into conf $dolibarr_main_restrict_ip
48}
49if (!defined('NOBROWSERNOTIF')) {
50 define('NOBROWSERNOTIF', '1');
51}
52$entity = (!empty($_GET['entity']) ? (int) $_GET['entity'] : (!empty($_POST['entity']) ? (int) $_POST['entity'] : 1)); // Keep $_GET and $_POST here. GETPOST not yet defined.
53if (is_numeric($entity)) {
54 define("DOLENTITY", $entity);
55}
56include '../../main.inc.php';
57require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
66$action = GETPOST('action', 'aZ09');
67
68$signature = GETPOST('signaturebase64');
69// Match the filter the producer uses in newonlinesign.php:97 ('alpha').
70// Refs such as PR2601-0003 contain a dash, which aZ09 strips. After stripping
71// the dash, dol_verifyHash fails because the security key was built from the
72// full reference, so onlineSign answered 403 even on valid submissions (#31464).
73$ref = GETPOST('ref', 'alpha');
74$mode = GETPOST('mode', 'aZ09'); // 'proposal', ...
75$SECUREKEY = GETPOST("securekey"); // Secure key
76$online_sign_name = GETPOST("onlinesignname");
77
78$error = 0;
79$response = "";
80
81$type = $mode;
82
83global $dolibarr_main_instance_unique_id;
84$defaultsalt = substr(dol_hash('dolibarr'.$dolibarr_main_instance_unique_id, 'sha256'), 0, 32); // Fallback if no specific salt was set
85
86// Security check
87$securekeyseed = '';
88if ($type == 'proposal') {
89 $securekeyseed = getDolGlobalString('PROPOSAL_ONLINE_SIGNATURE_SECURITY_TOKEN', $defaultsalt);
90} elseif ($type == 'contract') {
91 $securekeyseed = getDolGlobalString('CONTRACT_ONLINE_SIGNATURE_SECURITY_TOKEN', $defaultsalt);
92} elseif ($type == 'fichinter') {
93 $securekeyseed = getDolGlobalString('FICHINTER_ONLINE_SIGNATURE_SECURITY_TOKEN', $defaultsalt);
94} else {
95 $securekeyseed = getDolGlobalString(strtoupper($type).'_ONLINE_SIGNATURE_SECURITY_TOKEN', $defaultsalt);
96}
97
98if (empty($SECUREKEY) || !dol_verifyHash($securekeyseed . $type . $ref . (!isModEnabled('multicompany') ? '' : $entity), $SECUREKEY, '0')) {
99 // Link may have expired because of a change into the keys used to forge the signature.
100 httponly_accessforbidden('Bad value for securitykey. Value provided ' . dol_escape_htmltag($SECUREKEY) . ' does not match expected value for ref=' . dol_escape_htmltag($ref), 403);
101}
102
103// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
104$hookmanager->initHooks(array('ajaxonlinesign'));
105
106
107/*
108 * Actions
109 */
110
111// None
112
113
114/*
115 * View
116 */
117
119
120if ($action == "importSignature") {
121 $issignatureok = (!empty($signature) && $signature[0] == "image/png;base64");
122 if ($issignatureok) {
123 $signature = $signature[1];
124 $data = base64_decode($signature);
125
126 if ($mode == "propale" || $mode == 'proposal') {
127 require_once DOL_DOCUMENT_ROOT . '/comm/propal/class/propal.class.php';
128 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
129 $object = new Propal($db);
130 $object->fetch(0, $ref);
131
132 if ($object->status != $object::STATUS_VALIDATED) {
133 // Protection so we can't sign a proposal that is no more with status validated.
134 // The same test exists on the UPDATE request below, but it runs after the signed PDF has been generated.
135 $langs->load("errors");
136 print $langs->transnoentitiesnoconv("ErrorCantSignDocument"); // Must be a print that is shown by a javascript alert().
137 $error++;
138 }
139
140 $upload_dir = !empty($conf->propal->multidir_output[$object->entity ?? $conf->entity]) ? $conf->propal->multidir_output[$object->entity ?? $conf->entity] : $conf->propal->dir_output;
141 $upload_dir .= '/' . dol_sanitizeFileName($object->ref) . '/';
142
143 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
144 $default_font = pdf_getPDFFont($langs); // Must be after pdf_getInstance
145 $langs->loadLangs(array("main", "companies"));
146
147 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
148 $filename = "signatures/" . $date . "_signature.png";
149 if (!$error && !is_dir($upload_dir . "signatures/")) {
150 if (!dol_mkdir($upload_dir . "signatures/")) {
151 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
152 $error++;
153 }
154 }
155
156 if (!$error) {
157 $return = file_put_contents($upload_dir.$filename, $data);
158 if ($return === false) {
159 $error++;
160 $response = 'Error file_put_content: failed to create signature file.';
161 } else {
162 dolChmod($upload_dir.$filename);
163 }
164 }
165
166 if (!$error) {
167 // Defined modele of doc
168 $last_main_doc_file = $object->last_main_doc;
169 $directdownloadlink = $object->getLastMainDocLink('proposal'); // url to download the $object->last_main_doc
170
171 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
172 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
173 $ref_pdf = preg_replace('/_signed-(\d+)/', '', $ref_pdf);
174
175 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
176 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
177
178 if (dol_is_file($sourcefile)) {
179 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
180 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
181 if ($reshook < 0) {
182 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
183 }
184
185 if (empty($reshook)) {
186 // We build the new PDF
187 $formatarray = pdf_getFormat();
188 $page_largeur = $formatarray['width'];
189 $page_hauteur = $formatarray['height'];
190 $format = array($page_largeur, $page_hauteur);
191
192 $pdf = pdf_getInstance($format);
193 if (class_exists('TCPDF')) {
194 $pdf->setPrintHeader(false);
195 $pdf->setPrintFooter(false);
196 }
197 $pdf->SetFont(pdf_getPDFFont($langs));
198
199 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
200 $pdf->SetCompression(false);
201 }
202
203 //$pdf->Open();
204 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
205
206 $param = array();
207 $param['online_sign_name'] = $online_sign_name;
208 $param['pathtoimage'] = $upload_dir . $filename;
209
210 $propalsignonspecificpage = getDolGlobalInt("PROPAL_SIGNATURE_ON_SPECIFIC_PAGE");
211
212 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
213 for ($i = 1; $i < ($pagecount + 1); $i++) {
214 try {
215 $tppl = $pdf->importPage($i);
216 $s = $pdf->getTemplatesize($tppl);
217 $format = array($s['w'], $s['h']);
218 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L', $format);
219 $pdf->useTemplate($tppl);
220
221 if ($propalsignonspecificpage < 0) {
222 $propalsignonspecificpage = $pagecount - abs($propalsignonspecificpage);
223 }
224
225 if (empty($propalsignonspecificpage)) {
226 // Now we get the metadata keywords from the $sourcefile PDF (by parsing the binary PDF file) and use it to extract
227 // the page x in PAGESIGN=x into $propalsignonspecificpage
228 $keywords = pdfExtractMetadata($sourcefile, 'Keywords');
229 $reg = array();
230 if (preg_match('/PAGESIGN=(\d+)/', $keywords, $reg)) {
231 $propalsignonspecificpage = (int) $reg[1];
232 }
233 }
234
235 if (getDolGlobalString("PROPAL_SIGNATURE_ON_ALL_PAGES") || $propalsignonspecificpage == $i) {
236 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
237 // TODO Get position of box from PDF template
238
239 if (getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART")) {
240 $param['xforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART");
241 } else {
242 $param['xforimgstart'] = (empty($s['w']) ? 120 : round($s['w'] / 2) + 15);
243 }
244 if (getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART")) {
245 $param['yforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART");
246 } else {
247 $param['yforimgstart'] = (empty($s['h']) ? 240 : $s['h'] - 60);
248 }
249 if (getDolGlobalString("PROPAL_SIGNATURE_WFORIMG")) {
250 $param['wforimg'] = getDolGlobalString("PROPAL_SIGNATURE_WFORIMG");
251 } else {
252 $param['wforimg'] = $s['w'] - 20 - $param['xforimgstart'];
253 }
254
255 dolPrintSignatureImage($pdf, $langs, $param);
256 }
257 } catch (Exception $e) {
258 dol_syslog("Error when manipulating the PDF " . $sourcefile . " by onlineSign: " . $e->getMessage(), LOG_ERR);
259 $response = $e->getMessage();
260 $error++;
261 }
262 }
263
264 if (!getDolGlobalString("PROPAL_SIGNATURE_ON_ALL_PAGES") && !$propalsignonspecificpage) {
265 // We do not found specific instruction or page for the signature, so we add it now we are on the last page.
266 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
267 if (getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART")) {
268 $param['xforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART");
269 } else {
270 $param['xforimgstart'] = (empty($s['w']) ? 120 : round($s['w'] / 2) + 15);
271 }
272 if (getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART")) {
273 $param['yforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART");
274 } else {
275 $param['yforimgstart'] = (empty($s['h']) ? 240 : $s['h'] - 60);
276 }
277 if (getDolGlobalString("PROPAL_SIGNATURE_WFORIMG")) {
278 $param['wforimg'] = getDolGlobalString("PROPAL_SIGNATURE_WFORIMG");
279 } else {
280 $param['wforimg'] = $s['w'] - 20 - $param['xforimgstart'];
281 }
282
283 dolPrintSignatureImage($pdf, $langs, $param);
284 }
285
286 //$pdf->Close();
287 $pdf->Output($newpdffilename, "F");
288
289 // Index the new file and update the last_main_doc property of object.
290 $object->indexFile($newpdffilename, 1);
291 }
292 }
293 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
294 // Adding signature on .ODT not yet supported
295 // TODO
296 } else {
297 // Document format not supported to insert online signature.
298 // We should just create an image file with the signature.
299 }
300 }
301
302 if (!$error) {
303 $db->begin();
304
305 $online_sign_ip = getUserRemoteIP();
306
307 $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
308 $sql .= " SET fk_statut = " . ((int) $object::STATUS_SIGNED) . ", note_private = '" . $db->escape($object->note_private) . "',";
309 $sql .= " date_signature = '" . $db->idate(dol_now()) . "',";
310 $sql .= " online_sign_ip = '" . $db->escape($online_sign_ip) . "'";
311 if ($online_sign_name) {
312 $sql .= ", online_sign_name = '" . $db->escape($online_sign_name) . "'";
313 }
314 $sql .= " WHERE rowid = " . ((int) $object->id);
315 $sql .= " AND fk_statut = ".((int) $object::STATUS_VALIDATED); // Protection so we can't sign a document that is no more with status validated.
316
317 dol_syslog(__FILE__, LOG_DEBUG);
318 $resql = $db->query($sql);
319 if (!$resql) {
320 $error++;
321 $response = "error sql";
322 } else {
323 $num = $db->affected_rows($resql);
324 if ($num <= 0) {
325 $error++;
326 $langs->load("errors");
327 //setEventMessages($langs->trans("ErrorCantSignDocument"), null, 'errors');
328 print $langs->transnoentitiesnoconv("ErrorCantSignDocument"); // Must be a print that is shown by ajavascript alert().
329 }
330 }
331
332 if (!$error) {
333 if (method_exists($object, 'call_trigger')) {
334 $object->context = array('closedfromonlinesignature' => 'closedfromonlinesignature');
335
336 $result = $object->call_trigger('PROPAL_CLOSE_SIGNED', $user);
337 if ($result < 0) {
338 $error++;
339 $response = "error in trigger " . $object->error;
340 } else {
341 $soc = new Societe($db);
342 $soc->id = $object->socid;
343 $result = $soc->setAsCustomer();
344 if ($result < 0) {
345 $error++;
346 $response = $db->lasterror();
347 } else {
348 $response = "success";
349 }
350 }
351 } else {
352 $response = "success";
353 }
354 }
355
356 if (!$error) {
357 $db->commit();
358 $response = "success";
359 setEventMessages("PropalSigned", null, 'mesgs');
360 } else {
361 $db->rollback();
362 }
363 }
364 } elseif ($mode == 'contract') {
365 require_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php';
366 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
367 $object = new Contrat($db);
368 $object->fetch(0, $ref);
369
370 $upload_dir = !empty($conf->contract->multidir_output[$object->entity ?? $conf->entity]) ? $conf->contract->multidir_output[$object->entity ?? $conf->entity] : $conf->contrat->dir_output;
371 $upload_dir .= '/' . dol_sanitizeFileName($object->ref) . '/';
372
373 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
374 $filename = "signatures/" . $date . "_signature.png";
375 if (!is_dir($upload_dir . "signatures/")) {
376 if (!dol_mkdir($upload_dir . "signatures/")) {
377 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
378 $error++;
379 }
380 }
381
382 if (!$error) {
383 $return = file_put_contents($upload_dir . $filename, $data);
384 if ($return === false) {
385 $error++;
386 $response = 'Error file_put_content: failed to create signature file.';
387 } else {
388 dolChmod($upload_dir.$filename);
389 }
390 }
391
392 if (!$error) {
393 // Defined modele of doc
394 $last_main_doc_file = $object->last_main_doc;
395 $directdownloadlink = $object->getLastMainDocLink('contrat'); // url to download the $object->last_main_doc
396
397 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
398 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
399
400 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
401 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
402
403 if (dol_is_file($sourcefile)) {
404 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
405 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
406 if ($reshook < 0) {
407 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
408 }
409
410 if (empty($reshook)) {
411 // We build the new PDF
412 $pdf = pdf_getInstance();
413 if (class_exists('TCPDF')) {
414 $pdf->setPrintHeader(false);
415 $pdf->setPrintFooter(false);
416 }
417 $pdf->SetFont(pdf_getPDFFont($langs));
418
419 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
420 $pdf->SetCompression(false);
421 }
422
423 //$pdf->Open();
424 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
425
426 $param = array();
427 $param['online_sign_name'] = $online_sign_name;
428 $param['pathtoimage'] = $upload_dir . $filename;
429
430 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
431 for ($i = 1; $i < ($pagecount + 1); $i++) {
432 try {
433 $tppl = $pdf->importPage($i);
434 $s = $pdf->getTemplatesize($tppl);
435 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
436 $pdf->useTemplate($tppl);
437
438 if (getDolGlobalString("CONTRACT_SIGNATURE_ON_ALL_PAGES")) {
439 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
440 // TODO Get position of box from PDF template
441
442 if (getDolGlobalString("CONTRACT_SIGNATURE_XFORIMGSTART")) {
443 $param['xforimgstart'] = getDolGlobalString("CONTRACT_SIGNATURE_XFORIMGSTART");
444 } else {
445 $param['xforimgstart'] = (empty($s['w']) ? 110 : $s['w'] / 2 - 0);
446 }
447 if (getDolGlobalString("CONTRACT_SIGNATURE_YFORIMGSTART")) {
448 $param['yforimgstart'] = getDolGlobalString("CONTRACT_SIGNATURE_YFORIMGSTART");
449 } else {
450 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 62);
451 }
452 if (getDolGlobalString("CONTRACT_SIGNATURE_WFORIMG")) {
453 $param['wforimg'] = getDolGlobalString("CONTRACT_SIGNATURE_WFORIMG");
454 } else {
455 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
456 }
457
458 dolPrintSignatureImage($pdf, $langs, $param);
459 }
460 } catch (Exception $e) {
461 dol_syslog("Error when manipulating some PDF by onlineSign: " . $e->getMessage(), LOG_ERR);
462 $response = $e->getMessage();
463 $error++;
464 }
465 }
466
467 if (!getDolGlobalString("CONTRACT_SIGNATURE_ON_ALL_PAGES")) {
468 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
469 // TODO Get position of box from PDF template
470
471 $param['xforimgstart'] = (empty($s['w']) ? 110 : $s['w'] / 2 - 0);
472 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 62);
473 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
474
475 dolPrintSignatureImage($pdf, $langs, $param);
476 }
477
478 //$pdf->Close();
479 $pdf->Output($newpdffilename, "F");
480
481 // Index the new file and update the last_main_doc property of object.
482 $object->indexFile($newpdffilename, 1);
483 }
484 }
485 if (!$error) {
486 $response = "success";
487 }
488 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
489 // Adding signature on .ODT not yet supported
490 // TODO
491 } else {
492 // Document format not supported to insert online signature.
493 // We should just create an image file with the signature.
494 }
495 $user = new User($db);
496 $object->setSignedStatus($user, Contrat::$SIGNED_STATUSES['STATUS_SIGNED_RECEIVER_ONLINE'], 0, 'CONTRACT_MODIFY');
497 }
498 } elseif ($mode == 'fichinter') {
499 require_once DOL_DOCUMENT_ROOT . '/fichinter/class/fichinter.class.php';
500 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
501 $object = new Fichinter($db);
502 $object->fetch(0, $ref);
503
504 $upload_dir = !empty($conf->ficheinter->multidir_output[$object->entity ?? $conf->entity]) ? $conf->ficheinter->multidir_output[$object->entity ?? $conf->entity] : $conf->ficheinter->dir_output;
505 $upload_dir .= '/'.dol_sanitizeFileName($object->ref).'/';
506
507 $langs->loadLangs(array("main", "companies"));
508
509 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
510 $default_font = pdf_getPDFFont($langs); // Must be
511
512 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
513 $filename = "signatures/" . $date . "_signature.png";
514 if (!is_dir($upload_dir . "signatures/")) {
515 if (!dol_mkdir($upload_dir . "signatures/")) {
516 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
517 $error++;
518 }
519 }
520
521 if (!$error) {
522 $return = file_put_contents($upload_dir . $filename, $data);
523 if ($return === false) {
524 $error++;
525 $response = 'Error file_put_content: failed to create signature file.';
526 } else {
527 dolChmod($upload_dir.$filename);
528 }
529 }
530
531 if (!$error) {
532 // Defined modele of doc
533 $last_main_doc_file = $object->last_main_doc;
534 $directdownloadlink = $object->getLastMainDocLink('fichinter'); // url to download the $object->last_main_doc
535
536 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
537 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
538
539 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
540 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
541
542 if (dol_is_file($sourcefile)) {
543 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
544 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
545 if ($reshook < 0) {
546 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
547 }
548
549 if (empty($reshook)) {
550 // We build the new PDF
551 $pdf = pdf_getInstance();
552 if (class_exists('TCPDF')) {
553 $pdf->setPrintHeader(false);
554 $pdf->setPrintFooter(false);
555 }
556 $pdf->SetFont(pdf_getPDFFont($langs));
557
558 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
559 $pdf->SetCompression(false);
560 }
561
562 //$pdf->Open();
563 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
564
565 $param = array();
566 $param['online_sign_name'] = $online_sign_name;
567 $param['pathtoimage'] = $upload_dir . $filename;
568
569 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
570 for ($i = 1; $i < ($pagecount + 1); $i++) {
571 try {
572 $tppl = $pdf->importPage($i);
573 $s = $pdf->getTemplatesize($tppl);
574 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
575 $pdf->useTemplate($tppl);
576
577 if (getDolGlobalString("FICHINTER_SIGNATURE_ON_ALL_PAGES")) {
578 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
579 // TODO Get position of box from PDF template
580
581 if (getDolGlobalString("FICHINTER_SIGNATURE_XFORIMGSTART")) {
582 $param['xforimgstart'] = getDolGlobalString("FICHINTER_SIGNATURE_XFORIMGSTART");
583 } else {
584 $param['xforimgstart'] = (empty($s['w']) ? 110 : round($s['w'] * 0.53));
585 }
586 if (getDolGlobalString("FICHINTER_SIGNATURE_YFORIMGSTART")) {
587 $param['yforimgstart'] = getDolGlobalString("FICHINTER_SIGNATURE_YFORIMGSTART");
588 } else {
589 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 60);
590 }
591 if (getDolGlobalString("FICHINTER_SIGNATURE_WFORIMG")) {
592 $param['wforimg'] = getDolGlobalString("FICHINTER_SIGNATURE_WFORIMG");
593 } else {
594 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
595 }
596
597 dolPrintSignatureImage($pdf, $langs, $param);
598 }
599 } catch (Exception $e) {
600 dol_syslog("Error when manipulating some PDF by onlineSign: " . $e->getMessage(), LOG_ERR);
601 $response = $e->getMessage();
602 $error++;
603 }
604 }
605
606 if (!getDolGlobalString("FICHINTER_SIGNATURE_ON_ALL_PAGES")) {
607 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
608 // TODO Get position of box from PDF template
609
610 $param['xforimgstart'] = (empty($s['w']) ? 110 : round($s['w'] * 0.53));
611 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 60);
612 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
613
614 dolPrintSignatureImage($pdf, $langs, $param);
615 }
616
617 //$pdf->Close();
618 $pdf->Output($newpdffilename, "F");
619
620 // Index the new file and update the last_main_doc property of object.
621 $object->indexFile($newpdffilename, 1);
622 }
623 }
624 if (!$error) {
625 $response = "success";
626 }
627 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
628 // Adding signature on .ODT not yet supported
629 // TODO
630 } else {
631 // Document format not supported to insert online signature.
632 // We should just create an image file with the signature.
633 }
634 $user = new User($db);
635 $object->setSignedStatus($user, Fichinter::$SIGNED_STATUSES['STATUS_SIGNED_RECEIVER_ONLINE'], 0, 'FICHINTER_MODIFY');
636 }
637 } elseif ($mode == "societe_rib") {
638 $langs->load('withdrawals');
639 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
640 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
641 $modelpath = "core/modules/bank/doc/";
643 $object->fetch(0, $ref);
644 if (!empty($object->id)) {
645 $object->fetch_thirdparty();
646
647 $upload_dir = $conf->societe->multidir_output[$object->thirdparty->entity] . '/' . dol_sanitizeFileName((string) $object->thirdparty->id) . '/';
648
649 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
650 $default_font = pdf_getPDFFont($langs); // Must be after pdf_getInstance
651 $langs->loadLangs(array("main", "companies"));
652
653 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
654 $filename = "signatures/" . $date . "_signature.png";
655 if (!dol_is_dir($upload_dir . "signatures/")) {
656 if (!dol_mkdir($upload_dir . "signatures/")) {
657 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
658 $error++;
659 }
660 }
661 if (!dol_is_writable($upload_dir . "signatures/")) {
662 $response = "Error directory " . $upload_dir . "signatures/ is not writable";
663 $error++;
664 }
665 if (!dol_is_writable(DOL_DATA_ROOT.'/admin/temp/')) { // This is used by TCPDF as working directory
666 $response = "Error directory " . DOL_DATA_ROOT."/admin/temp/ is not writable";
667 $error++;
668 }
669
670 if (!$error) {
671 $return = file_put_contents($upload_dir . $filename, $data);
672 if ($return === false) {
673 $error++;
674 $response = 'Error file_put_content: failed to create signature file.';
675 } else {
676 dolChmod($upload_dir.$filename);
677 }
678 }
679
680 if (!$error) {
681 // Defined modele of doc
682 $last_main_doc_file = $object->last_main_doc;
683 $last_modelpdf = $object->model_pdf;
684 $directdownloadlink = $object->getLastMainDocLink('company'); // url to download the $object->last_main_doc
685
686 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
687 $sourcefile = '';
688 $newpdffilename = '';
689 if ($last_modelpdf == 'sepamandate') {
690 $newpdffilename = $upload_dir . $langs->transnoentitiesnoconv("SepaMandateShort") . ' ' . dol_sanitizeFileName($object->ref) . "-" . dol_sanitizeFileName($object->rum) . "_signed-" . $date . ".pdf";
691 $sourcefile = $upload_dir . $langs->transnoentitiesnoconv("SepaMandateShort") . ' ' . dol_sanitizeFileName($object->ref) . "-" . dol_sanitizeFileName($object->rum) . ".pdf";
692 } else {
693 // Fallback for setups using a non-default bank PDF model (eg. "ban"): take the last
694 // generated main document as source and append "_signed-<date>" before the extension.
695 // Without this the signed PDF is never built and the download link keeps pointing at
696 // the unsigned original.
697 $sourcefile = DOL_DATA_ROOT . '/' . $last_main_doc_file;
698 $newpdffilename = preg_replace('/\.pdf$/i', '_signed-' . $date . '.pdf', $sourcefile);
699 }
700 if (dol_is_file($sourcefile)) {
701 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
702 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
703 if ($reshook < 0) {
704 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
705 }
706
707 if (empty($reshook)) {
708 // We build the new PDF
709 $pdf = pdf_getInstance();
710 if (class_exists('TCPDF')) {
711 $pdf->setPrintHeader(false);
712 $pdf->setPrintFooter(false);
713 }
714 $pdf->SetFont(pdf_getPDFFont($langs));
715
716 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
717 $pdf->SetCompression(false);
718 }
719
720 //$pdf->Open();
721 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
722
723 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
724 for ($i = 1; $i < ($pagecount + 1); $i++) {
725 try {
726 $tppl = $pdf->importPage($i);
727 $s = $pdf->getTemplatesize($tppl);
728 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
729 $pdf->useTemplate($tppl);
730 } catch (Exception $e) {
731 dol_syslog("Error when manipulating the PDF " . $sourcefile . " by onlineSign: " . $e->getMessage(), LOG_ERR);
732 $response = $e->getMessage();
733 $error++;
734 }
735 }
736
737
738 // Get position of box from PDF template
739 $file = '';
740 $classname = '';
741 $filefound = '';
742 $dirmodels = array('/');
743 if (is_array($conf->modules_parts['models'])) {
744 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
745 }
746 foreach ($dirmodels as $reldir) {
747 $file = "pdf_" . $last_modelpdf . ".modules.php";
748 // Check the template location
749 $file = dol_buildpath($reldir . $modelpath . $file, 0);
750 if (file_exists($file)) {
751 $filefound = $file;
752 $classname = 'pdf_' . $last_modelpdf;
753 break;
754 }
755 }
756
757 if ($filefound === '') {
758 $response = $langs->trans("Error") . ' Failed to load doc generator with modelpaths=' . $modelpath . ' - modele=' . $last_modelpdf;
759 dol_syslog($response, LOG_ERR);
760 $error++;
761 }
762
763 if (!$error && $classname !== '') {
764 // If PDF template class was found
765 require_once $file;
766
767 $objPDF = new $classname($db);
768
769 $pdf->SetFont($default_font, '', $default_font_size - 1);
770
771 $xForDate = $objPDF->marge_gauche;
772 $yForDate = $objPDF->page_hauteur - $objPDF->heightforinfotot - $objPDF->heightforfreetext - $objPDF->heightforfooter + 10;
773 $pdf->SetXY($xForDate, $yForDate);
774 $pdf->MultiCell(100, 4, dol_print_date(dol_now(), "daytext", false, $langs, true), 0, 'L');
775
776 $xforimgstart = $objPDF->xPosSignArea;
777 $yforimgstart = $yForDate - 5;
778 $wforimg = $s['w'] - 20 - $xforimgstart;
779
780 $param = array();
781 $param['online_sign_name'] = $online_sign_name;
782 $param['pathtoimage'] = $upload_dir . $filename;
783
784 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
785 // TODO Get position of box from PDF template
786
787 $param['xforimgstart'] = $xforimgstart;
788 $param['yforimgstart'] = $yforimgstart;
789 $param['wforimg'] = $wforimg;
790
791 dolPrintSignatureImage($pdf, $langs, $param);
792 }
793 //$pdf->Close();
794 $pdf->Output($newpdffilename, "F");
795
796 // Index the new file and update the last_main_doc property of object.
797 $object->indexFile($newpdffilename, 1);
798 }
799 }
800 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
801 // Adding signature on .ODT not yet supported
802 // TODO
803 } else {
804 // Document format not supported to insert online signature.
805 // We should just create an image file with the signature.
806 }
807 }
808 } else {
809 $error++;
810 $response = "cannot find BAN/RIB";
811 }
812
813 if (!$error) {
814 $db->begin();
815
816 $online_sign_ip = getUserRemoteIP();
817
818 $sql = "UPDATE " . MAIN_DB_PREFIX . $db->sanitize($object->table_element);
819 $sql .= " SET ";
820 $sql .= " date_signature = '" . $db->idate(dol_now()) . "',";
821 $sql .= " online_sign_ip = '" . $db->escape($online_sign_ip) . "'";
822 if ($online_sign_name) {
823 $sql .= ", online_sign_name = '" . $db->escape($online_sign_name) . "'";
824 }
825 //$sql .= ", last_main_doc = '" . $db->escape($object->element'..') . "'";
826
827 $sql .= " WHERE rowid = " . ((int) $object->id);
828
829 dol_syslog(__FILE__, LOG_DEBUG);
830 $resql = $db->query($sql);
831 if (!$resql) {
832 $error++;
833 } else {
834 $num = $db->affected_rows($resql);
835 }
836
837 if (!$error) {
838 $response = "success";
839 } else {
840 $error++;
841 $response = "error sql";
842 }
843
844 if (!$error) {
845 $db->commit();
846 $response = "success";
847 setEventMessages(dol_ucfirst($mode)."Signed", null, 'warnings');
848 } else {
849 $db->rollback();
850 }
851 }
852 } elseif ($mode == 'expedition') {
853 require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
854 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
855
856 $object = new Expedition($db);
857 $object->fetch(0, $ref);
858
859 $upload_dir = $conf->expedition->dir_output."/sending/";
860 $upload_dir .= '/'.dol_sanitizeFileName($object->ref).'/';
861
862 $langs->loadLangs(array("main", "companies"));
863
864 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
865 $default_font = pdf_getPDFFont($langs); // Must be
866
867 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
868 $filename = "signatures/" . $date . "_signature.png";
869 if (!is_dir($upload_dir . "signatures/")) {
870 if (!dol_mkdir($upload_dir . "signatures/")) {
871 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
872 $error++;
873 }
874 }
875
876 if (!$error) {
877 $return = file_put_contents($upload_dir . $filename, $data);
878 if ($return === false) {
879 $error++;
880 $response = 'Error file_put_content: failed to create signature file.';
881 } else {
882 dolChmod($upload_dir.$filename);
883 }
884 }
885
886 if (!$error) {
887 $last_main_doc_file = $object->last_main_doc;
888 // Defined modele of doc
889 if (empty($last_main_doc_file) || !dol_is_file(DOL_DATA_ROOT.'/'.$object->last_main_doc)) {
890 // It seems document has never been generated, or was generated and then deleted.
891 // So we try to regenerate it with its default template.
892 $defaulttemplate = ''; // We force the use an empty string instead of $object->model_pdf to be sure to use a "main" default template and not the last one used.
893 $object->generateDocument($defaulttemplate, $langs);
894 }
895 $last_main_doc_file = $object->last_main_doc;
896 $directdownloadlink = $object->getLastMainDocLink('expedition'); // url to download the $object->last_main_doc
897
898 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
899 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
900
901 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
902 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
903
904 if (dol_is_file($sourcefile)) {
905 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
906 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
907 if ($reshook < 0) {
908 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
909 }
910
911 if (empty($reshook)) {
912 // We build the new PDF
913 $pdf = pdf_getInstance();
914 if (class_exists('TCPDF')) {
915 $pdf->setPrintHeader(false);
916 $pdf->setPrintFooter(false);
917 }
918 $pdf->SetFont(pdf_getPDFFont($langs));
919
920 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
921 $pdf->SetCompression(false);
922 }
923
924 //$pdf->Open();
925 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
926
927 $param = array();
928 $param['online_sign_name'] = $online_sign_name;
929 $param['pathtoimage'] = $upload_dir . $filename;
930
931 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
932 for ($i = 1; $i < ($pagecount + 1); $i++) {
933 try {
934 $tppl = $pdf->importPage($i);
935 $s = $pdf->getTemplatesize($tppl);
936 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
937 $pdf->useTemplate($tppl);
938
939 if (getDolGlobalString("SHIPMENT_SIGNATURE_ON_ALL_PAGES")) {
940 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
941 // TODO Get position of box from PDF template
942
943 $param['xforimgstart'] = (empty($s['w']) ? 110 : round($s['w'] * 0.53));
944 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 60);
945 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
946
947 dolPrintSignatureImage($pdf, $langs, $param);
948 }
949 } catch (Exception $e) {
950 dol_syslog("Error when manipulating some PDF by onlineSign: " . $e->getMessage(), LOG_ERR);
951 $response = $e->getMessage();
952 $error++;
953 }
954 }
955
956 if (!getDolGlobalString("SHIPMENT_SIGNATURE_ON_ALL_PAGES")) {
957 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
958 // TODO Get position of box from PDF template
959
960 $param['xforimgstart'] = (empty($s['w']) ? 110 : round($s['w'] * 0.53));
961 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 60);
962 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
963
964 dolPrintSignatureImage($pdf, $langs, $param);
965 }
966
967 //$pdf->Close();
968 $pdf->Output($newpdffilename, "F");
969
970 // Index the new file and update the last_main_doc property of object.
971 $object->indexFile($newpdffilename, 1);
972 }
973 }
974 if (!$error) {
975 $response = "success";
976 }
977 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
978 // Adding signature on .ODT not yet supported
979 // TODO
980 } else {
981 // Document format not supported to insert online signature.
982 // We should just create an image file with the signature.
983 }
984 }
985 $user = new User($db);
986 $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_SIGNED_RECEIVER_ONLINE'], 0, 'SHIPPING_MODIFY');
987 }
988 } else {
989 $error++;
990 $response = 'error signature_not_found';
991 }
992}
993
994if ($error) {
995 http_response_code(501);
996}
997
998echo $response;
999
1000
1009function dolPrintSignatureImage(TCPDF $pdf, $langs, $params)
1010{
1011 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
1012 $default_font = pdf_getPDFFont($langs); // Must be
1013 $xforimgstart = $params['xforimgstart'];
1014 $yforimgstart = $params['yforimgstart'];
1015 $wforimg = $params['wforimg'];
1016
1017 $pdf->SetXY($xforimgstart, $yforimgstart + round($wforimg / 4) - 4);
1018 $pdf->SetFont($default_font, '', $default_font_size - 1);
1019 $pdf->SetTextColor(80, 80, 80);
1020 $pdf->MultiCell($wforimg, 4, $langs->transnoentities("Signature") . ': ' . dol_print_date(dol_now(), "day", false, $langs, true). ' - '.$params['online_sign_name'], 0, 'L');
1021 //$pdf->SetXY($xforimgstart, $yforimgstart + round($wforimg / 4));
1022 //$pdf->MultiCell($wforimg, 4, $langs->trans("Lastname") . ': ' . $online_sign_name, 0, 'L');
1023
1024 $pdf->Image($params['pathtoimage'], $xforimgstart, $yforimgstart, $wforimg, round($wforimg / 4));
1025
1026 return;
1027}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Class to manage bank accounts description of third parties.
Class to manage proposals.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_is_writable($folderorfile)
Test if directory or filename is writable.
dol_is_file($pathoffile)
Return if path is a file.
dol_is_dir($folder)
Test if filename is a directory.
dol_now($mode='gmt')
Return date for now.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
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.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
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).
getUserRemoteIP($trusted=0)
Return the real IP of remote user.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
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)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
dolPrintSignatureImage(TCPDF $pdf, $langs, $params)
Output the signature file into the PDF object.
pdf_getFormat($outputlangs=null, $mode='setup')
Return array with format properties of default PDF format.
Definition pdf.lib.php:87
pdf_getPDFFontSize($outputlangs)
Return font size to use for PDF generation.
Definition pdf.lib.php:294
pdfExtractMetadata($file, $field='Keywords')
Function to extract metadata from a PDF file by doing a binary parsing of the PDF file.
Definition pdf.lib.php:3281
pdf_getPDFFont($outputlangs)
Return font name to use for PDF generation.
Definition pdf.lib.php:273
pdf_getInstance($format='', $metric='mm', $pagetype='P')
Return a PDF instance object.
Definition pdf.lib.php:129
httponly_accessforbidden($message='1', $http_response_code=403, $stringalreadysanitized=0)
Show a message to say access is forbidden and stop program.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
dol_verifyHash($chain, $hash, $type='0')
Compute a hash and compare it to the given one For backward compatibility reasons,...