dolibarr 24.0.0-beta
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-2025 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 httponly_accessforbidden('Bad value for securitykey. Value provided ' . dol_escape_htmltag($SECUREKEY) . ' does not match expected value for ref=' . dol_escape_htmltag($ref), 403);
100}
101
102// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
103$hookmanager->initHooks(array('ajaxonlinesign'));
104
105
106/*
107 * Actions
108 */
109
110// None
111
112
113/*
114 * View
115 */
116
118
119if ($action == "importSignature") {
120 $issignatureok = (!empty($signature) && $signature[0] == "image/png;base64");
121 if ($issignatureok) {
122 $signature = $signature[1];
123 $data = base64_decode($signature);
124
125 if ($mode == "propale" || $mode == 'proposal') {
126 require_once DOL_DOCUMENT_ROOT . '/comm/propal/class/propal.class.php';
127 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
128 $object = new Propal($db);
129 $object->fetch(0, $ref);
130
131 $upload_dir = !empty($conf->propal->multidir_output[$object->entity ?? $conf->entity]) ? $conf->propal->multidir_output[$object->entity ?? $conf->entity] : $conf->propal->dir_output;
132 $upload_dir .= '/' . dol_sanitizeFileName($object->ref) . '/';
133
134 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
135 $default_font = pdf_getPDFFont($langs); // Must be after pdf_getInstance
136 $langs->loadLangs(array("main", "companies"));
137
138 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
139 $filename = "signatures/" . $date . "_signature.png";
140 if (!is_dir($upload_dir . "signatures/")) {
141 if (!dol_mkdir($upload_dir . "signatures/")) {
142 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
143 $error++;
144 }
145 }
146
147 if (!$error) {
148 $return = file_put_contents($upload_dir.$filename, $data);
149 if ($return === false) {
150 $error++;
151 $response = 'Error file_put_content: failed to create signature file.';
152 } else {
153 dolChmod($upload_dir.$filename);
154 }
155 }
156
157 if (!$error) {
158 // Defined modele of doc
159 $last_main_doc_file = $object->last_main_doc;
160 $directdownloadlink = $object->getLastMainDocLink('proposal'); // url to download the $object->last_main_doc
161
162 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
163 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
164 $ref_pdf = preg_replace('/_signed-(\d+)/', '', $ref_pdf);
165
166 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
167 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
168
169 if (dol_is_file($sourcefile)) {
170 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
171 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
172 if ($reshook < 0) {
173 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
174 }
175
176 if (empty($reshook)) {
177 // We build the new PDF
178 $pdf = pdf_getInstance();
179 if (class_exists('TCPDF')) {
180 $pdf->setPrintHeader(false);
181 $pdf->setPrintFooter(false);
182 }
183 $pdf->SetFont(pdf_getPDFFont($langs));
184
185 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
186 $pdf->SetCompression(false);
187 }
188
189 //$pdf->Open();
190 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
191
192 $param = array();
193 $param['online_sign_name'] = $online_sign_name;
194 $param['pathtoimage'] = $upload_dir . $filename;
195
196 $propalsignonspecificpage = getDolGlobalInt("PROPAL_SIGNATURE_ON_SPECIFIC_PAGE");
197
198 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
199 for ($i = 1; $i < ($pagecount + 1); $i++) {
200 try {
201 $tppl = $pdf->importPage($i);
202 $s = $pdf->getTemplatesize($tppl);
203 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
204 $pdf->useTemplate($tppl);
205 if ($propalsignonspecificpage < 0) {
206 $propalsignonspecificpage = $pagecount - abs($propalsignonspecificpage);
207 }
208
209 if (empty($propalsignonspecificpage)) {
210 // Now we get the metadata keywords from the $sourcefile PDF (by parsing the binary PDF file) and use it to extract
211 // the page x in PAGESIGN=x into $propalsignonspecificpage
212 $keywords = pdfExtractMetadata($sourcefile, 'Keywords');
213 $reg = array();
214 if (preg_match('/PAGESIGN=(\d+)/', $keywords, $reg)) {
215 $propalsignonspecificpage = (int) $reg[1];
216 }
217 }
218
219 if (getDolGlobalString("PROPAL_SIGNATURE_ON_ALL_PAGES") || $propalsignonspecificpage == $i) {
220 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
221 // TODO Get position of box from PDF template
222
223 if (getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART")) {
224 $param['xforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART");
225 } else {
226 $param['xforimgstart'] = (empty($s['w']) ? 120 : round($s['w'] / 2) + 15);
227 }
228 if (getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART")) {
229 $param['yforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART");
230 } else {
231 $param['yforimgstart'] = (empty($s['h']) ? 240 : $s['h'] - 60);
232 }
233 if (getDolGlobalString("PROPAL_SIGNATURE_WFORIMG")) {
234 $param['wforimg'] = getDolGlobalString("PROPAL_SIGNATURE_WFORIMG");
235 } else {
236 $param['wforimg'] = $s['w'] - 20 - $param['xforimgstart'];
237 }
238
239 dolPrintSignatureImage($pdf, $langs, $param);
240 }
241 } catch (Exception $e) {
242 dol_syslog("Error when manipulating the PDF " . $sourcefile . " by onlineSign: " . $e->getMessage(), LOG_ERR);
243 $response = $e->getMessage();
244 $error++;
245 }
246 }
247
248 if (!getDolGlobalString("PROPAL_SIGNATURE_ON_ALL_PAGES") && !$propalsignonspecificpage) {
249 // We do not found specific instruction or page for the signature, so we add it now we are on the last page.
250 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
251 if (getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART")) {
252 $param['xforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_XFORIMGSTART");
253 } else {
254 $param['xforimgstart'] = (empty($s['w']) ? 120 : round($s['w'] / 2) + 15);
255 }
256 if (getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART")) {
257 $param['yforimgstart'] = getDolGlobalString("PROPAL_SIGNATURE_YFORIMGSTART");
258 } else {
259 $param['yforimgstart'] = (empty($s['h']) ? 240 : $s['h'] - 60);
260 }
261 if (getDolGlobalString("PROPAL_SIGNATURE_WFORIMG")) {
262 $param['wforimg'] = getDolGlobalString("PROPAL_SIGNATURE_WFORIMG");
263 } else {
264 $param['wforimg'] = $s['w'] - 20 - $param['xforimgstart'];
265 }
266
267 dolPrintSignatureImage($pdf, $langs, $param);
268 }
269
270 //$pdf->Close();
271 $pdf->Output($newpdffilename, "F");
272
273 // Index the new file and update the last_main_doc property of object.
274 $object->indexFile($newpdffilename, 1);
275 }
276 }
277 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
278 // Adding signature on .ODT not yet supported
279 // TODO
280 } else {
281 // Document format not supported to insert online signature.
282 // We should just create an image file with the signature.
283 }
284 }
285
286 if (!$error) {
287 $db->begin();
288
289 $online_sign_ip = getUserRemoteIP();
290
291 $sql = "UPDATE " . MAIN_DB_PREFIX . "propal";
292 $sql .= " SET fk_statut = " . ((int) $object::STATUS_SIGNED) . ", note_private = '" . $db->escape($object->note_private) . "',";
293 $sql .= " date_signature = '" . $db->idate(dol_now()) . "',";
294 $sql .= " online_sign_ip = '" . $db->escape($online_sign_ip) . "'";
295 if ($online_sign_name) {
296 $sql .= ", online_sign_name = '" . $db->escape($online_sign_name) . "'";
297 }
298 $sql .= " WHERE rowid = " . ((int) $object->id);
299
300 dol_syslog(__FILE__, LOG_DEBUG);
301 $resql = $db->query($sql);
302 if (!$resql) {
303 $error++;
304 } else {
305 $num = $db->affected_rows($resql);
306 }
307
308 if (!$error) {
309 if (method_exists($object, 'call_trigger')) {
310 $object->context = array('closedfromonlinesignature' => 'closedfromonlinesignature');
311
312 $result = $object->call_trigger('PROPAL_CLOSE_SIGNED', $user);
313 if ($result < 0) {
314 $error++;
315 $response = "error in trigger " . $object->error;
316 } else {
317 $soc = new Societe($db);
318 $soc->id = $object->socid;
319 $result = $soc->setAsCustomer();
320 if ($result < 0) {
321 $error++;
322 $response = $db->lasterror();
323 } else {
324 $response = "success";
325 }
326 }
327 } else {
328 $response = "success";
329 }
330 } else {
331 $error++;
332 $response = "error sql";
333 }
334
335 if (!$error) {
336 $db->commit();
337 $response = "success";
338 setEventMessages("PropalSigned", null, 'warnings');
339 } else {
340 $db->rollback();
341 }
342 }
343 } elseif ($mode == 'contract') {
344 require_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php';
345 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
346 $object = new Contrat($db);
347 $object->fetch(0, $ref);
348
349 $upload_dir = !empty($conf->contract->multidir_output[$object->entity ?? $conf->entity]) ? $conf->contract->multidir_output[$object->entity ?? $conf->entity] : $conf->contrat->dir_output;
350 $upload_dir .= '/' . dol_sanitizeFileName($object->ref) . '/';
351
352 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
353 $filename = "signatures/" . $date . "_signature.png";
354 if (!is_dir($upload_dir . "signatures/")) {
355 if (!dol_mkdir($upload_dir . "signatures/")) {
356 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
357 $error++;
358 }
359 }
360
361 if (!$error) {
362 $return = file_put_contents($upload_dir . $filename, $data);
363 if ($return === false) {
364 $error++;
365 $response = 'Error file_put_content: failed to create signature file.';
366 } else {
367 dolChmod($upload_dir.$filename);
368 }
369 }
370
371 if (!$error) {
372 // Defined modele of doc
373 $last_main_doc_file = $object->last_main_doc;
374 $directdownloadlink = $object->getLastMainDocLink('contrat'); // url to download the $object->last_main_doc
375
376 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
377 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
378
379 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
380 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
381
382 if (dol_is_file($sourcefile)) {
383 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
384 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
385 if ($reshook < 0) {
386 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
387 }
388
389 if (empty($reshook)) {
390 // We build the new PDF
391 $pdf = pdf_getInstance();
392 if (class_exists('TCPDF')) {
393 $pdf->setPrintHeader(false);
394 $pdf->setPrintFooter(false);
395 }
396 $pdf->SetFont(pdf_getPDFFont($langs));
397
398 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
399 $pdf->SetCompression(false);
400 }
401
402 //$pdf->Open();
403 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
404
405 $param = array();
406 $param['online_sign_name'] = $online_sign_name;
407 $param['pathtoimage'] = $upload_dir . $filename;
408
409 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
410 for ($i = 1; $i < ($pagecount + 1); $i++) {
411 try {
412 $tppl = $pdf->importPage($i);
413 $s = $pdf->getTemplatesize($tppl);
414 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
415 $pdf->useTemplate($tppl);
416
417 if (getDolGlobalString("CONTRACT_SIGNATURE_ON_ALL_PAGES")) {
418 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
419 // TODO Get position of box from PDF template
420
421 if (getDolGlobalString("CONTRACT_SIGNATURE_XFORIMGSTART")) {
422 $param['xforimgstart'] = getDolGlobalString("CONTRACT_SIGNATURE_XFORIMGSTART");
423 } else {
424 $param['xforimgstart'] = (empty($s['w']) ? 110 : $s['w'] / 2 - 0);
425 }
426 if (getDolGlobalString("CONTRACT_SIGNATURE_YFORIMGSTART")) {
427 $param['yforimgstart'] = getDolGlobalString("CONTRACT_SIGNATURE_YFORIMGSTART");
428 } else {
429 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 62);
430 }
431 if (getDolGlobalString("CONTRACT_SIGNATURE_WFORIMG")) {
432 $param['wforimg'] = getDolGlobalString("CONTRACT_SIGNATURE_WFORIMG");
433 } else {
434 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
435 }
436
437 dolPrintSignatureImage($pdf, $langs, $param);
438 }
439 } catch (Exception $e) {
440 dol_syslog("Error when manipulating some PDF by onlineSign: " . $e->getMessage(), LOG_ERR);
441 $response = $e->getMessage();
442 $error++;
443 }
444 }
445
446 if (!getDolGlobalString("CONTRACT_SIGNATURE_ON_ALL_PAGES")) {
447 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
448 // TODO Get position of box from PDF template
449
450 $param['xforimgstart'] = (empty($s['w']) ? 110 : $s['w'] / 2 - 0);
451 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 62);
452 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
453
454 dolPrintSignatureImage($pdf, $langs, $param);
455 }
456
457 //$pdf->Close();
458 $pdf->Output($newpdffilename, "F");
459
460 // Index the new file and update the last_main_doc property of object.
461 $object->indexFile($newpdffilename, 1);
462 }
463 }
464 if (!$error) {
465 $response = "success";
466 }
467 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
468 // Adding signature on .ODT not yet supported
469 // TODO
470 } else {
471 // Document format not supported to insert online signature.
472 // We should just create an image file with the signature.
473 }
474 $user = new User($db);
475 $object->setSignedStatus($user, Contrat::$SIGNED_STATUSES['STATUS_SIGNED_RECEIVER_ONLINE'], 0, 'CONTRACT_MODIFY');
476 }
477 } elseif ($mode == 'fichinter') {
478 require_once DOL_DOCUMENT_ROOT . '/fichinter/class/fichinter.class.php';
479 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
480 $object = new Fichinter($db);
481 $object->fetch(0, $ref);
482
483 $upload_dir = !empty($conf->ficheinter->multidir_output[$object->entity ?? $conf->entity]) ? $conf->ficheinter->multidir_output[$object->entity ?? $conf->entity] : $conf->ficheinter->dir_output;
484 $upload_dir .= '/'.dol_sanitizeFileName($object->ref).'/';
485
486 $langs->loadLangs(array("main", "companies"));
487
488 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
489 $default_font = pdf_getPDFFont($langs); // Must be
490
491 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
492 $filename = "signatures/" . $date . "_signature.png";
493 if (!is_dir($upload_dir . "signatures/")) {
494 if (!dol_mkdir($upload_dir . "signatures/")) {
495 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
496 $error++;
497 }
498 }
499
500 if (!$error) {
501 $return = file_put_contents($upload_dir . $filename, $data);
502 if ($return === false) {
503 $error++;
504 $response = 'Error file_put_content: failed to create signature file.';
505 } else {
506 dolChmod($upload_dir.$filename);
507 }
508 }
509
510 if (!$error) {
511 // Defined modele of doc
512 $last_main_doc_file = $object->last_main_doc;
513 $directdownloadlink = $object->getLastMainDocLink('fichinter'); // url to download the $object->last_main_doc
514
515 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
516 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
517
518 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
519 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
520
521 if (dol_is_file($sourcefile)) {
522 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
523 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
524 if ($reshook < 0) {
525 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
526 }
527
528 if (empty($reshook)) {
529 // We build the new PDF
530 $pdf = pdf_getInstance();
531 if (class_exists('TCPDF')) {
532 $pdf->setPrintHeader(false);
533 $pdf->setPrintFooter(false);
534 }
535 $pdf->SetFont(pdf_getPDFFont($langs));
536
537 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
538 $pdf->SetCompression(false);
539 }
540
541 //$pdf->Open();
542 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
543
544 $param = array();
545 $param['online_sign_name'] = $online_sign_name;
546 $param['pathtoimage'] = $upload_dir . $filename;
547
548 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
549 for ($i = 1; $i < ($pagecount + 1); $i++) {
550 try {
551 $tppl = $pdf->importPage($i);
552 $s = $pdf->getTemplatesize($tppl);
553 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
554 $pdf->useTemplate($tppl);
555
556 if (getDolGlobalString("FICHINTER_SIGNATURE_ON_ALL_PAGES")) {
557 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
558 // TODO Get position of box from PDF template
559
560 if (getDolGlobalString("FICHINTER_SIGNATURE_XFORIMGSTART")) {
561 $param['xforimgstart'] = getDolGlobalString("FICHINTER_SIGNATURE_XFORIMGSTART");
562 } else {
563 $param['xforimgstart'] = (empty($s['w']) ? 110 : $s['w'] / 2 - 2);
564 }
565 if (getDolGlobalString("FICHINTER_SIGNATURE_YFORIMGSTART")) {
566 $param['yforimgstart'] = getDolGlobalString("FICHINTER_SIGNATURE_YFORIMGSTART");
567 } else {
568 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 62);
569 }
570 if (getDolGlobalString("FICHINTER_SIGNATURE_WFORIMG")) {
571 $param['wforimg'] = getDolGlobalString("FICHINTER_SIGNATURE_WFORIMG");
572 } else {
573 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 20);
574 }
575
576 dolPrintSignatureImage($pdf, $langs, $param);
577 }
578 } catch (Exception $e) {
579 dol_syslog("Error when manipulating some PDF by onlineSign: " . $e->getMessage(), LOG_ERR);
580 $response = $e->getMessage();
581 $error++;
582 }
583 }
584
585 if (!getDolGlobalString("FICHINTER_SIGNATURE_ON_ALL_PAGES")) {
586 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
587 // TODO Get position of box from PDF template
588
589 $param['xforimgstart'] = (empty($s['w']) ? 110 : $s['w'] / 2 - 2);
590 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 62);
591 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 20);
592
593 dolPrintSignatureImage($pdf, $langs, $param);
594 }
595
596 //$pdf->Close();
597 $pdf->Output($newpdffilename, "F");
598
599 // Index the new file and update the last_main_doc property of object.
600 $object->indexFile($newpdffilename, 1);
601 }
602 }
603 if (!$error) {
604 $response = "success";
605 }
606 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
607 // Adding signature on .ODT not yet supported
608 // TODO
609 } else {
610 // Document format not supported to insert online signature.
611 // We should just create an image file with the signature.
612 }
613 $user = new User($db);
614 $object->setSignedStatus($user, Fichinter::$SIGNED_STATUSES['STATUS_SIGNED_RECEIVER_ONLINE'], 0, 'FICHINTER_MODIFY');
615 }
616 } elseif ($mode == "societe_rib") {
617 $langs->load('withdrawals');
618 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
619 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
620 $modelpath = "core/modules/bank/doc/";
622 $object->fetch(0, $ref);
623 if (!empty($object->id)) {
624 $object->fetch_thirdparty();
625
626 $upload_dir = $conf->societe->multidir_output[$object->thirdparty->entity] . '/' . dol_sanitizeFileName((string) $object->thirdparty->id) . '/';
627
628 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
629 $default_font = pdf_getPDFFont($langs); // Must be after pdf_getInstance
630 $langs->loadLangs(array("main", "companies"));
631
632 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
633 $filename = "signatures/" . $date . "_signature.png";
634 if (!dol_is_dir($upload_dir . "signatures/")) {
635 if (!dol_mkdir($upload_dir . "signatures/")) {
636 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
637 $error++;
638 }
639 }
640 if (!dol_is_writable($upload_dir . "signatures/")) {
641 $response = "Error directory " . $upload_dir . "signatures/ is not writable";
642 $error++;
643 }
644 if (!dol_is_writable(DOL_DATA_ROOT.'/admin/temp/')) { // This is used by TCPDF as working directory
645 $response = "Error directory " . DOL_DATA_ROOT."/admin/temp/ is not writable";
646 $error++;
647 }
648
649 if (!$error) {
650 $return = file_put_contents($upload_dir . $filename, $data);
651 if ($return === false) {
652 $error++;
653 $response = 'Error file_put_content: failed to create signature file.';
654 } else {
655 dolChmod($upload_dir.$filename);
656 }
657 }
658
659 if (!$error) {
660 // Defined modele of doc
661 $last_main_doc_file = $object->last_main_doc;
662 $last_modelpdf = $object->model_pdf;
663 $directdownloadlink = $object->getLastMainDocLink('company'); // url to download the $object->last_main_doc
664
665 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
666 $sourcefile = '';
667 $newpdffilename = '';
668 if ($last_modelpdf == 'sepamandate') {
669 $newpdffilename = $upload_dir . $langs->transnoentitiesnoconv("SepaMandateShort") . ' ' . dol_sanitizeFileName($object->ref) . "-" . dol_sanitizeFileName($object->rum) . "_signed-" . $date . ".pdf";
670 $sourcefile = $upload_dir . $langs->transnoentitiesnoconv("SepaMandateShort") . ' ' . dol_sanitizeFileName($object->ref) . "-" . dol_sanitizeFileName($object->rum) . ".pdf";
671 } else {
672 // Fallback for setups using a non-default bank PDF model (eg. "ban"): take the last
673 // generated main document as source and append "_signed-<date>" before the extension.
674 // Without this the signed PDF is never built and the download link keeps pointing at
675 // the unsigned original.
676 $sourcefile = DOL_DATA_ROOT . '/' . $last_main_doc_file;
677 $newpdffilename = preg_replace('/\.pdf$/i', '_signed-' . $date . '.pdf', $sourcefile);
678 }
679 if (dol_is_file($sourcefile)) {
680 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
681 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
682 if ($reshook < 0) {
683 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
684 }
685
686 if (empty($reshook)) {
687 // We build the new PDF
688 $pdf = pdf_getInstance();
689 if (class_exists('TCPDF')) {
690 $pdf->setPrintHeader(false);
691 $pdf->setPrintFooter(false);
692 }
693 $pdf->SetFont(pdf_getPDFFont($langs));
694
695 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
696 $pdf->SetCompression(false);
697 }
698
699 //$pdf->Open();
700 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
701
702 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
703 for ($i = 1; $i < ($pagecount + 1); $i++) {
704 try {
705 $tppl = $pdf->importPage($i);
706 $s = $pdf->getTemplatesize($tppl);
707 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
708 $pdf->useTemplate($tppl);
709 } catch (Exception $e) {
710 dol_syslog("Error when manipulating the PDF " . $sourcefile . " by onlineSign: " . $e->getMessage(), LOG_ERR);
711 $response = $e->getMessage();
712 $error++;
713 }
714 }
715
716
717 // Get position of box from PDF template
718 $file = '';
719 $classname = '';
720 $filefound = '';
721 $dirmodels = array('/');
722 if (is_array($conf->modules_parts['models'])) {
723 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
724 }
725 foreach ($dirmodels as $reldir) {
726 $file = "pdf_" . $last_modelpdf . ".modules.php";
727 // On vérifie l'emplacement du modele
728 $file = dol_buildpath($reldir . $modelpath . $file, 0);
729 if (file_exists($file)) {
730 $filefound = $file;
731 $classname = 'pdf_' . $last_modelpdf;
732 break;
733 }
734 }
735
736 if ($filefound === '') {
737 $response = $langs->trans("Error") . ' Failed to load doc generator with modelpaths=' . $modelpath . ' - modele=' . $last_modelpdf;
738 dol_syslog($response, LOG_ERR);
739 $error++;
740 }
741
742 if (!$error && $classname !== '') {
743 // If PDF template class was found
744 require_once $file;
745
746 $objPDF = new $classname($db);
747
748 $pdf->SetFont($default_font, '', $default_font_size - 1);
749
750 $xForDate = $objPDF->marge_gauche;
751 $yForDate = $objPDF->page_hauteur - $objPDF->heightforinfotot - $objPDF->heightforfreetext - $objPDF->heightforfooter + 10;
752 $pdf->SetXY($xForDate, $yForDate);
753 $pdf->MultiCell(100, 4, dol_print_date(dol_now(), "daytext", false, $langs, true), 0, 'L');
754
755 $xforimgstart = $objPDF->xPosSignArea;
756 $yforimgstart = $yForDate - 5;
757 $wforimg = $s['w'] - 20 - $xforimgstart;
758
759 $param = array();
760 $param['online_sign_name'] = $online_sign_name;
761 $param['pathtoimage'] = $upload_dir . $filename;
762
763 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
764 // TODO Get position of box from PDF template
765
766 $param['xforimgstart'] = $xforimgstart;
767 $param['yforimgstart'] = $yforimgstart;
768 $param['wforimg'] = $wforimg;
769
770 dolPrintSignatureImage($pdf, $langs, $param);
771 }
772 //$pdf->Close();
773 $pdf->Output($newpdffilename, "F");
774
775 // Index the new file and update the last_main_doc property of object.
776 $object->indexFile($newpdffilename, 1);
777 }
778 }
779 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
780 // Adding signature on .ODT not yet supported
781 // TODO
782 } else {
783 // Document format not supported to insert online signature.
784 // We should just create an image file with the signature.
785 }
786 }
787 } else {
788 $error++;
789 $response = "cannot find BAN/RIB";
790 }
791
792 if (!$error) {
793 $db->begin();
794
795 $online_sign_ip = getUserRemoteIP();
796
797 $sql = "UPDATE " . MAIN_DB_PREFIX . $db->sanitize($object->table_element);
798 $sql .= " SET ";
799 $sql .= " date_signature = '" . $db->idate(dol_now()) . "',";
800 $sql .= " online_sign_ip = '" . $db->escape($online_sign_ip) . "'";
801 if ($online_sign_name) {
802 $sql .= ", online_sign_name = '" . $db->escape($online_sign_name) . "'";
803 }
804 //$sql .= ", last_main_doc = '" . $db->escape($object->element'..') . "'";
805
806 $sql .= " WHERE rowid = " . ((int) $object->id);
807
808 dol_syslog(__FILE__, LOG_DEBUG);
809 $resql = $db->query($sql);
810 if (!$resql) {
811 $error++;
812 } else {
813 $num = $db->affected_rows($resql);
814 }
815
816 if (!$error) {
817 $response = "success";
818 } else {
819 $error++;
820 $response = "error sql";
821 }
822
823 if (!$error) {
824 $db->commit();
825 $response = "success";
826 setEventMessages(dol_ucfirst($mode)."Signed", null, 'warnings');
827 } else {
828 $db->rollback();
829 }
830 }
831 } elseif ($mode == 'expedition') {
832 require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
833 require_once DOL_DOCUMENT_ROOT . '/core/lib/pdf.lib.php';
834
835 $object = new Expedition($db);
836 $object->fetch(0, $ref);
837
838 $upload_dir = $conf->expedition->dir_output."/sending/";
839 $upload_dir .= '/'.dol_sanitizeFileName($object->ref).'/';
840
841 $langs->loadLangs(array("main", "companies"));
842
843 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
844 $default_font = pdf_getPDFFont($langs); // Must be
845
846 $date = dol_print_date(dol_now(), "%Y%m%d%H%M%S");
847 $filename = "signatures/" . $date . "_signature.png";
848 if (!is_dir($upload_dir . "signatures/")) {
849 if (!dol_mkdir($upload_dir . "signatures/")) {
850 $response = "Error mkdir. Failed to create dir " . $upload_dir . "signatures/";
851 $error++;
852 }
853 }
854
855 if (!$error) {
856 $return = file_put_contents($upload_dir . $filename, $data);
857 if ($return === false) {
858 $error++;
859 $response = 'Error file_put_content: failed to create signature file.';
860 } else {
861 dolChmod($upload_dir.$filename);
862 }
863 }
864
865 if (!$error) {
866 $last_main_doc_file = $object->last_main_doc;
867 // Defined modele of doc
868 if (empty($last_main_doc_file) || !dol_is_file(DOL_DATA_ROOT.'/'.$object->last_main_doc)) {
869 // It seems document has never been generated, or was generated and then deleted.
870 // So we try to regenerate it with its default template.
871 $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.
872 $object->generateDocument($defaulttemplate, $langs);
873 }
874 $last_main_doc_file = $object->last_main_doc;
875 $directdownloadlink = $object->getLastMainDocLink('expedition'); // url to download the $object->last_main_doc
876
877 if (preg_match('/\.pdf/i', $last_main_doc_file)) {
878 $ref_pdf = pathinfo($last_main_doc_file, PATHINFO_FILENAME); // Retrieves the name of external or internal PDF
879
880 $newpdffilename = $upload_dir . $ref_pdf . "_signed-" . $date . ".pdf";
881 $sourcefile = $upload_dir . $ref_pdf . ".pdf";
882
883 if (dol_is_file($sourcefile)) {
884 $parameters = array('sourcefile' => $sourcefile, 'newpdffilename' => $newpdffilename);
885 $reshook = $hookmanager->executeHooks('AddSignature', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
886 if ($reshook < 0) {
887 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
888 }
889
890 if (empty($reshook)) {
891 // We build the new PDF
892 $pdf = pdf_getInstance();
893 if (class_exists('TCPDF')) {
894 $pdf->setPrintHeader(false);
895 $pdf->setPrintFooter(false);
896 }
897 $pdf->SetFont(pdf_getPDFFont($langs));
898
899 if (getDolGlobalString('MAIN_DISABLE_PDF_COMPRESSION')) {
900 $pdf->SetCompression(false);
901 }
902
903 //$pdf->Open();
904 $pagecount = $pdf->setSourceFile($sourcefile); // original PDF
905
906 $param = array();
907 $param['online_sign_name'] = $online_sign_name;
908 $param['pathtoimage'] = $upload_dir . $filename;
909
910 $s = array(); // Array with size of each page. Example array(w'=>210, 'h'=>297);
911 for ($i = 1; $i < ($pagecount + 1); $i++) {
912 try {
913 $tppl = $pdf->importPage($i);
914 $s = $pdf->getTemplatesize($tppl);
915 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
916 $pdf->useTemplate($tppl);
917
918 if (getDolGlobalString("SHIPMENT_SIGNATURE_ON_ALL_PAGES")) {
919 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
920 // TODO Get position of box from PDF template
921
922 $param['xforimgstart'] = 111;
923 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 60);
924 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
925
926 dolPrintSignatureImage($pdf, $langs, $param);
927 }
928 } catch (Exception $e) {
929 dol_syslog("Error when manipulating some PDF by onlineSign: " . $e->getMessage(), LOG_ERR);
930 $response = $e->getMessage();
931 $error++;
932 }
933 }
934
935 if (!getDolGlobalString("SHIPMENT_SIGNATURE_ON_ALL_PAGES")) {
936 // A signature image file is 720 x 180 (ratio 1/4) but we use only the size into PDF
937 // TODO Get position of box from PDF template
938
939 $param['xforimgstart'] = 111;
940 $param['yforimgstart'] = (empty($s['h']) ? 250 : $s['h'] - 60);
941 $param['wforimg'] = $s['w'] - ($param['xforimgstart'] + 16);
942
943 dolPrintSignatureImage($pdf, $langs, $param);
944 }
945
946 //$pdf->Close();
947 $pdf->Output($newpdffilename, "F");
948
949 // Index the new file and update the last_main_doc property of object.
950 $object->indexFile($newpdffilename, 1);
951 }
952 }
953 if (!$error) {
954 $response = "success";
955 }
956 } elseif (preg_match('/\.odt/i', $last_main_doc_file)) {
957 // Adding signature on .ODT not yet supported
958 // TODO
959 } else {
960 // Document format not supported to insert online signature.
961 // We should just create an image file with the signature.
962 }
963 }
964 $user = new User($db);
965 $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_SIGNED_RECEIVER_ONLINE'], 0, 'SHIPPING_MODIFY');
966 }
967 } else {
968 $error++;
969 $response = 'error signature_not_found';
970 }
971}
972
973if ($error) {
974 http_response_code(501);
975}
976
977echo $response;
978
979
988function dolPrintSignatureImage(TCPDF $pdf, $langs, $params)
989{
990 $default_font_size = pdf_getPDFFontSize($langs); // Must be after pdf_getInstance
991 $default_font = pdf_getPDFFont($langs); // Must be
992 $xforimgstart = $params['xforimgstart'];
993 $yforimgstart = $params['yforimgstart'];
994 $wforimg = $params['wforimg'];
995
996 $pdf->SetXY($xforimgstart, $yforimgstart + round($wforimg / 4) - 4);
997 $pdf->SetFont($default_font, '', $default_font_size - 1);
998 $pdf->SetTextColor(80, 80, 80);
999 $pdf->MultiCell($wforimg, 4, $langs->transnoentities("Signature") . ': ' . dol_print_date(dol_now(), "day", false, $langs, true). ' - '.$params['online_sign_name'], 0, 'L');
1000 //$pdf->SetXY($xforimgstart, $yforimgstart + round($wforimg / 4));
1001 //$pdf->MultiCell($wforimg, 4, $langs->trans("Lastname") . ': ' . $online_sign_name, 0, 'L');
1002
1003 $pdf->Image($params['pathtoimage'], $xforimgstart, $yforimgstart, $wforimg, round($wforimg / 4));
1004
1005 return;
1006}
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)
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_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:3247
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,...