dolibarr 23.0.3
blockedlog.class.php
1<?php
2/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr>
3 * Copyright (C) 2017-2020 Laurent Destailleur <eldy@destailleur.fr>
4 * Copyright (C) 2022 charlene benke <charlene@patas-monkey.com>
5 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
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 *
21 * See https://medium.com/@lhartikk/a-blockchain-in-200-lines-of-code-963cc1cc0e54
22 */
23
24
29{
33 public $db;
34
39 public $id;
40
45 public $entity;
46
51 public $picto = 'blockedlog';
52
56 public $error = '';
57
61 public $errors = array();
62
67 public $signature = '';
68
72 public $amounts = null;
73
77 public $amounts_taxexcl = null;
78
83 public $action = '';
84
89 public $module_source = '';
90
94 public $linktype = '';
95
99 public $linktoref = '';
100
105 public $element = '';
106
111 public $fk_object = 0;
112
117 public $certified = false;
118
123 public $fk_user = 0;
124
128 public $date_creation;
129
133 public $date_modification;
134
138 public $date_object = 0;
139
143 public $ref_object = '';
144
148 public $object_data = null;
149
153 public $object_version = '';
154
158 public $object_format = '';
159
163 public $user_fullname = '';
164
168 public $debuginfo;
169
174 public $trackedevents = array();
175
180 public $trackedmodules = array();
181
182
183
189 public function __construct(DoliDB $db)
190 {
191 $this->db = $db;
192 }
193
194
200 public function loadTrackedEvents()
201 {
202 global $langs;
203
204 $this->trackedevents = array();
205 $this->trackedmodules = array();
206
207 $sep = 0;
208
209 $this->trackedmodules[0] = 'None';
210 if (isModEnabled('takepos')) {
211 $this->trackedmodules['takepos'] = 'TakePOS';
212 }
213
214 // Customer Invoice/Facture / Payment (For most VAT antifraud laws)
215 if (isModEnabled('invoice')) {
216 $sep++;
217 $this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("Invoices").' | '.$langs->trans("Payments").'</span>', 'disabled' => 1);
218
219 $this->trackedevents['BILL_VALIDATE'] = array('id' => 'BILL_VALIDATE', 'label' => 'logBILL_VALIDATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_VALIDATE'));
220 //$this->trackedevents['BILL_UPDATE'] = array('id' => 'BILL_VALIDATE', 'label' => 'logBILL_UPDATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_UPDATE'));
221 $this->trackedevents['BILL_SENTBYMAIL'] = array('id' => 'BILL_SENTBYMAIL', 'label' => 'logBILL_SENTBYMAIL', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_SENTBYMAIL'));
222 $this->trackedevents['DOC_DOWNLOAD'] = array('id' => 'DOC_DOWNLOAD', 'label' => 'BlockedLogBillDownload', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('BlockedLogBillDownload'));
223 $this->trackedevents['DOC_PREVIEW'] = array('id' => 'DOC_PREVIEW', 'label' => 'BlockedLogBillPreview', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('BlockedLogBillPreview'));
224 $this->trackedevents['PAYMENT_CUSTOMER_CREATE'] = array('id' => 'PAYMENT_CUSTOMER_CREATE', 'label' => 'logPAYMENT_CUSTOMER_CREATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_CUSTOMER_CREATE'));
225 $this->trackedevents['PAYMENT_CUSTOMER_DELETE'] = array('id' => 'PAYMENT_CUSTOMER_DELETE', 'label' => 'logPAYMENT_CUSTOMER_DELETE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_CUSTOMER_DELETE'));
226 }
227
228 /* Supplier
229 // Supplier Invoice / Payment
230 if (isModEnabled("fournisseur")) {
231 $this->trackedevents['BILL_SUPPLIER_VALIDATE']='BlockedLogSupplierBillValidate';
232 $this->trackedevents['BILL_SUPPLIER_DELETE']='BlockedLogSupplierBillDelete';
233 $this->trackedevents['BILL_SUPPLIER_SENTBYMAIL']='BlockedLogSupplierBillSentByEmail'; // Trigger key does not exists, we want just into array to list it as done
234 $this->trackedevents['SUPPLIER_DOC_DOWNLOAD']='BlockedLogSupplierBillDownload'; // Trigger key does not exists, we want just into array to list it as done
235 $this->trackedevents['SUPPLIER_DOC_PREVIEW']='BlockedLogSupplierBillPreview'; // Trigger key does not exists, we want just into array to list it as done
236 $this->trackedevents['PAYMENT_SUPPLIER_CREATE']='BlockedLogSupplierBillPaymentCreate';
237 $this->trackedevents['PAYMENT_SUPPLIER_DELETE']='BlockedLogsupplierBillPaymentCreate';
238 }
239 */
240
241 // Donation
242 if (isModEnabled('don') && getDolGlobalString('BLOCKEDLOG_ENABLE_DONATION')) { // For countries that need unalterable logs for donations
243 if (!empty($this->trackedevents)) {
244 $sep++;
245 $this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("Donations").' | '.$langs->trans("Payments").'</span>', 'disabled' => 1);
246 }
247
248 $this->trackedevents['DON_VALIDATE'] = array('id' => 'DON_VALIDATE', 'label' => 'logDON_VALIDATE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDON_VALIDATE'));
249 $this->trackedevents['DON_DELETE'] = array('id' => 'DON_DELETE', 'label' => 'logDON_DELETE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDON_DELETE'));
250 //$this->trackedevents['DON_SENTBYMAIL'] = array('id' => 'BILL_VALIDATE', img_picto('', 'don', 'class="pictofixedwidth").').$langs->trans('labelhtml' => 'logDON_SENTBYMAIL');
251 $this->trackedevents['DONATION_PAYMENT_CREATE'] = array('id' => 'DONATION_PAYMENT_CREATE', 'label' => 'logDONATION_PAYMENT_CREATE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDONATION_PAYMENT_CREATE'));
252 $this->trackedevents['DONATION_PAYMENT_DELETE'] = array('id' => 'DONATION_PAYMENT_DELETE', 'label' => 'logDONATION_PAYMENT_DELETE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDONATION_PAYMENT_DELETE'));
253 }
254
255 /*
256 // Salary
257 if (isModEnabled('salary')) {
258 $this->trackedevents['PAYMENT_SALARY_CREATE'] = 'BlockedLogSalaryPaymentCreate';
259 $this->trackedevents['PAYMENT_SALARY_MODIFY'] = 'BlockedLogSalaryPaymentCreate';
260 $this->trackedevents['PAYMENT_SALARY_DELETE'] = 'BlockedLogSalaryPaymentCreate';
261 }
262 */
263
264 // Members
265 if (isModEnabled('member') && getDolGlobalString('BLOCKEDLOG_ENABLE_MEMBER')) { // For countries that need unalterable logs for membership management
266 if (!empty($this->trackedevents)) {
267 $sep++;
268 $this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("MenuMembers").'</span>', 'disabled' => 1);
269 }
270
271 $this->trackedevents['MEMBER_SUBSCRIPTION_CREATE'] = array('id' => 'MEMBER_SUBSCRIPTION_CREATE', 'label' => 'logMEMBER_SUBSCRIPTION_CREATE', 'labelhtml' => img_picto('', 'member', 'class="pictofixedwidth").').$langs->trans('logMEMBER_SUBSCRIPTION_CREATE'));
272 $this->trackedevents['MEMBER_SUBSCRIPTION_MODIFY'] = array('id' => 'MEMBER_SUBSCRIPTION_MODIFY', 'label' => 'logMEMBER_SUBSCRIPTION_MODIFY', 'labelhtml' => img_picto('', 'member', 'class="pictofixedwidth").').$langs->trans('logMEMBER_SUBSCRIPTION_MODIFY'));
273 $this->trackedevents['MEMBER_SUBSCRIPTION_DELETE'] = array('id' => 'MEMBER_SUBSCRIPTION_DELETE', 'label' => 'logMEMBER_SUBSCRIPTION_DELETE', 'labelhtml' => img_picto('', 'member', 'class="pictofixedwidth").').$langs->trans('logMEMBER_SUBSCRIPTION_DELETE'));
274 }
275
276 // Bank
277 /*
278 if (isModEnabled("bank")) {
279 if (!empty($this->trackedevents)) {
280 $sep++;
281 $this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("VariousPayment").'</span>', 'disabled' => 1);
282 }
283
284 $this->trackedevents['PAYMENT_VARIOUS_CREATE'] = array('id' => 'PAYMENT_VARIOUS_CREATE', 'label' => 'logPAYMENT_VARIOUS_CREATE', 'labelhtml' => img_picto('', 'bank', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_VARIOUS_CREATE'));
285 $this->trackedevents['PAYMENT_VARIOUS_MODIFY'] = array('id' => 'PAYMENT_VARIOUS_MODIFY', 'label' => 'logPAYMENT_VARIOUS_MODIFY', 'labelhtml' => img_picto('', 'bank', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_VARIOUS_MODIFY'));
286 $this->trackedevents['PAYMENT_VARIOUS_DELETE'] = array('id' => 'PAYMENT_VARIOUS_DELETE', 'label' => 'logPAYMENT_VARIOUS_DELETE', 'labelhtml' => img_picto('', 'bank', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_VARIOUS_DELETE'));
287 }
288 */
289
290 // Cash register closing
291 // $conf->global->BANK_ENABLE_POS_CASHCONTROL must be set to 1 by all external POS modules
292 $moduleposenabled = (isModEnabled('cashdesk') || isModEnabled('takepos') || getDolGlobalString('BANK_ENABLE_POS_CASHCONTROL'));
293 if ($moduleposenabled) {
294 if (!empty($this->trackedevents)) {
295 $sep++;
296 $this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("CashControl").'</span>', 'disabled' => 1);
297 }
298 if (getDolGlobalString('BLOCKEDLOG_ADD_OLD_CASHCONTROL_VALIDATE')) {
299 $this->trackedevents['CASHCONTROL_VALIDATE'] = array('id' => 'CASHCONTROL_VALIDATE', 'label' => 'logCASHCONTROL_VALIDATE', 'labelhtml' => img_picto('', 'pos', 'class="pictofixedwidth").').$langs->trans('logCASHCONTROL_VALIDATE'));
300 }
301 $this->trackedevents['CASHCONTROL_CLOSE'] = array('id' => 'CASHCONTROL_CLOSE', 'label' => 'logCASHCONTROL_CLOSE', 'labelhtml' => img_picto('', 'pos', 'class="pictofixedwidth").').$langs->trans('logCASHCONTROL_CLOSE'));
302 }
303
304 // Add more action to track from a conf variable. For the case we want to track other actions into the unalterable log.
305 // For example: STOCK_MOVEMENT, ...
306 if (getDolGlobalString('BLOCKEDLOG_ADD_ACTIONS_SUPPORTED')) {
307 if (!empty($this->trackedevents)) {
308 $sep++;
309 $this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----------</span>', 'disabled' => 1);
310 }
311
312 $tmparrayofmoresupportedevents = explode(',', getDolGlobalString('BLOCKEDLOG_ADD_ACTIONS_SUPPORTED'));
313 foreach ($tmparrayofmoresupportedevents as $val) {
314 $this->trackedevents[$val] = array('id' => $val, 'label' => 'log'.$val, 'labelhtml' => img_picto('', 'generic', 'class="pictofixedwidth").').$langs->trans('log'.$val));
315 }
316 }
317
318 if (!empty($this->trackedevents)) {
319 $sep++;
320 $this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("Other").'</span>', 'disabled' => 1);
321 }
322 $this->trackedevents['BLOCKEDLOG_EXPORT'] = array('id' => 'BLOCKEDLOG_EXPORT', 'label' => 'logBLOCKEDLOG_EXPORT', 'labelhtml' => img_picto('', $this->picto, 'class="pictofixedwidth").').$langs->trans('logBLOCKEDLOG_EXPORT'));
323
324 return 1;
325 }
326
332 public function getObjectLink()
333 {
334 global $langs;
335
336 if ($this->element === 'facture') {
337 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
338
339 $object = new Facture($this->db);
340 if ($object->fetch($this->fk_object) > 0) {
341 return $object->getNomUrl(1);
342 } else {
343 $this->error = (string) (((int) $this->error) + 1);
344 }
345 }
346 if ($this->element === 'invoice_supplier') {
347 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
348
349 $object = new FactureFournisseur($this->db);
350 if ($object->fetch($this->fk_object) > 0) {
351 return $object->getNomUrl(1);
352 } else {
353 $this->error = (string) (((int) $this->error) + 1);
354 }
355 } elseif ($this->element === 'payment') {
356 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
357
358 $object = new Paiement($this->db);
359 if ($object->fetch($this->fk_object) > 0) {
360 return $object->getNomUrl(1);
361 } else {
362 $this->error = (string) (((int) $this->error) + 1);
363 }
364 } elseif ($this->element === 'payment_supplier') {
365 require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php';
366
367 $object = new PaiementFourn($this->db);
368 if ($object->fetch($this->fk_object) > 0) {
369 return $object->getNomUrl(1);
370 } else {
371 $this->error = (string) (((int) $this->error) + 1);
372 }
373 } elseif ($this->element === 'payment_donation') {
374 require_once DOL_DOCUMENT_ROOT.'/don/class/paymentdonation.class.php';
375
376 $object = new PaymentDonation($this->db);
377 if ($object->fetch($this->fk_object) > 0) {
378 return $object->getNomUrl(1);
379 } else {
380 $this->error = (string) (((int) $this->error) + 1);
381 }
382 } elseif ($this->element === 'payment_various') {
383 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
384
385 $object = new PaymentVarious($this->db);
386 if ($object->fetch($this->fk_object) > 0) {
387 return $object->getNomUrl(1);
388 } else {
389 $this->error = (string) (((int) $this->error) + 1);
390 }
391 } elseif ($this->element === 'don' || $this->element === 'donation') {
392 require_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php';
393
394 $object = new Don($this->db);
395 if ($object->fetch($this->fk_object) > 0) {
396 return $object->getNomUrl(1);
397 } else {
398 $this->error = (string) (((int) $this->error) + 1);
399 }
400 } elseif ($this->element === 'subscription') {
401 require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php';
402
403 $object = new Subscription($this->db);
404 if ($object->fetch($this->fk_object) > 0) {
405 return $object->getNomUrl(1);
406 } else {
407 $this->error = (string) (((int) $this->error) + 1);
408 }
409 } elseif ($this->element === 'cashcontrol') {
410 require_once DOL_DOCUMENT_ROOT.'/compta/cashcontrol/class/cashcontrol.class.php';
411
412 $object = new CashControl($this->db);
413 if ($object->fetch($this->fk_object) > 0) {
414 return $object->getNomUrl(1);
415 } else {
416 $this->error = (string) (((int) $this->error) + 1);
417 }
418 } elseif ($this->element === 'stockmouvement') {
419 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
420
421 $object = new MouvementStock($this->db);
422 if ($object->fetch($this->fk_object) > 0) {
423 return $object->getNomUrl(1);
424 } else {
425 $this->error = (string) (((int) $this->error) + 1);
426 }
427 } elseif ($this->element === 'project') {
428 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
429
430 $object = new Project($this->db);
431 if ($object->fetch($this->fk_object) > 0) {
432 return $object->getNomUrl(1);
433 } else {
434 $this->error = (string) (((int) $this->error) + 1);
435 }
436 } elseif ($this->action == 'BLOCKEDLOG_EXPORT') {
437 return '<i class="opacitymedium">'.$langs->trans("logBLOCKEDLOG_EXPORT").'</i>';
438 } elseif ($this->action == 'MODULE_SET') {
439 return '<i class="opacitymedium">'.$langs->trans("BlockedLogEnabled").'</i>';
440 } elseif ($this->action == 'MODULE_RESET') {
441 if ($this->signature == '0000000000') {
442 return '<i class="opacitymedium">'.$langs->trans("BlockedLogDisabled").'</i>';
443 } else {
444 return '<i class="opacitymedium">'.$langs->trans("BlockedLogDisabledBis").'</i>';
445 }
446 }
447
448 return '<i class="opacitymedium">'.$langs->trans('ImpossibleToReloadObject', $this->element, $this->fk_object).'</i>';
449 }
450
456 public function getUser()
457 {
458 global $langs, $cachedUser;
459
460 if (empty($cachedUser)) {
461 $cachedUser = array();
462 }
463
464 if (empty($cachedUser[$this->fk_user])) {
465 $u = new User($this->db);
466 if ($u->fetch($this->fk_user) > 0) {
467 $cachedUser[$this->fk_user] = $u;
468 }
469 }
470
471 if (!empty($cachedUser[$this->fk_user])) {
472 return $cachedUser[$this->fk_user]->getNomUrl(1);
473 }
474
475 return $langs->trans('ImpossibleToRetrieveUser', $this->fk_user);
476 }
477
490 public function setObjectData(&$object, $action, $amounts, $fuser = null, $amounts_taxexcl = null)
491 {
492 global $langs, $user, $mysoc;
493
494 if (is_object($fuser)) {
495 $user = $fuser;
496 }
497
498 // Generic fields
499
500 // action
501 $this->action = $action;
502 // amount
503 $this->amounts_taxexcl = $amounts_taxexcl;
504 $this->amounts = $amounts;
505 // date
506 if ($object->element == 'payment' || $object->element == 'payment_supplier') {
507 '@phan-var-force Paiement|PaiementFourn $object';
508 $this->date_object = empty($object->datepaye) ? $object->date : $object->datepaye;
509 } elseif ($object->element == 'payment_salary') {
510 '@phan-var-force PaymentSalary $object';
511 $this->date_object = $object->datev;
512 } elseif ($object->element == 'payment_donation' || $object->element == 'payment_various') {
513 '@phan-var-force PaymentDonation $object';
514 $this->date_object = empty($object->datepaid) ? $object->datep : $object->datepaid;
515 } elseif ($object->element == 'subscription') {
516 '@phan-var-force Subscription $object';
517 $this->date_object = $object->dateh;
518 } elseif ($object->element == 'cashcontrol') {
520 '@phan-var-force CashControl $object';
521 $this->date_object = $object->date_creation;
522 } elseif (property_exists($object, 'date')) {
523 // Generic case
524 $this->date_object = $object->date; // @phan-suppress-current-line PhanUndeclaredProperty
525 } elseif (property_exists($object, 'datem')) {
526 // Generic case (second chance, for example for stock movement)
527 $this->date_object = $object->datem; // @phan-suppress-current-line PhanUndeclaredProperty
528 }
529
530 // In case of credit note, we add link to source invoice to have more tracking info when doing tracking later
531 if ($object->element == 'invoice_supplier') {
532 '@phan-var-force FactureFournisseur $object';
534 $invoice = new FactureFournisseur($this->db);
535 $invoice->fetch($object->fk_facture_source);
536 if ($invoice->id > 0) {
537 $this->linktype = 'credit_note_of';
538 $this->linktoref = $invoice->ref;
539 }
540 //$this->module_source = (string) $invoice->module_source;
541 }
542 }
543 if ($object->element == 'facture') {
544 '@phan-var-force Facture $object';
545 if ($object->type == Facture::TYPE_CREDIT_NOTE) {
546 $invoice = new Facture($this->db);
547 $invoice->fetch($object->fk_facture_source);
548 if ($invoice->id > 0) {
549 $this->linktype = 'credit_note_of';
550 $this->linktoref = $invoice->ref;
551 }
552 $this->module_source = (string) $invoice->module_source;
553 }
554 }
555
556 // ref object
557 $this->ref_object = ((!empty($object->newref)) ? $object->newref : $object->ref); // newref is set when validating a draft, ref is set in other cases
558 // type of object
559 $this->element = $object->element;
560 // id of object
561 $this->fk_object = $object->id;
562
563 // Add thirdparty info if not yet done
564 if (empty($object->thirdparty) && method_exists($object, 'fetch_thirdparty')) {
565 $object->fetch_thirdparty();
566 }
567
568
569 // Set object_data
570 $this->object_data = new stdClass();
571
572 // Add fields to exclude (this has become useless because we now use a list fields to keep later).
573 $arrayoffieldstoexclude = array(
574 'table_element', 'fields',
575 'ref_previous', 'ref_next',
576 'origin', 'origin_id',
577 'oldcopy', 'picto', 'error', 'errors',
578 'model_pdf', 'modelpdf', 'last_main_doc', 'civility_id', 'contact', 'contact_id',
579 'table_element_line', 'ismultientitymanaged', 'isextrafieldmanaged',
580 'array_languages',
581 'childtables',
582 'contact_ids',
583 'context',
584 'element',
585 'labelStatus',
586 'labelStatusShort',
587 'linkedObjectsIds',
588 'linkedObjects',
589 'fk_delivery_address',
590 'projet', // There is already ->fk_project
591 'restrictiononfksoc',
592 'specimen',
593 );
594
595 // Add more fields to exclude depending on object type
596 if ($this->element == 'cashcontrol') {
597 $arrayoffieldstoexclude = array_merge($arrayoffieldstoexclude, array(
598 'name', 'lastname', 'firstname', 'region', 'region_id', 'region_code', 'state', 'state_id', 'state_code', 'country', 'country_id', 'country_code',
599 'total_ht', 'total_tva', 'total_ttc', 'total_localtax1', 'total_localtax2',
600 'barcode_type', 'barcode_type_code', 'barcode_type_label', 'barcode_type_coder', 'mode_reglement_id', 'cond_reglement_id', 'mode_reglement', 'cond_reglement', 'shipping_method_id',
601 'extraparams', 'fk_incoterms', 'fk_user_creat', 'fk_user_valid', 'label_incoterms', 'location_incoterms', 'lines', 'nb', 'tms', 'comments', 'array_options', 'warnings',
602 'opening', 'status', 'date_valid'
603 )
604 );
605 }
606
607 // For customer payment and supplier payment, the thirdparty can be added in payment detail
608 $addthirdpartyatpaymentlevel = 0;
609 if (!empty($object->thirdparty) && !in_array($this->element, array('payment', 'payment_supplier'))) {
610 $addthirdpartyatpaymentlevel = 1;
611 }
612
613 if (!empty($object->thirdparty) && !$addthirdpartyatpaymentlevel) {
614 $this->object_data->thirdparty = new stdClass();
615
616 foreach ($object->thirdparty as $key => $value) {
617 if (in_array($key, $arrayoffieldstoexclude)) {
618 continue; // Discard some properties
619 }
620 // List of fields qualified
621 if (!in_array($key, array(
622 'name', 'name_alias', 'ref_ext', 'address', 'zip', 'town', 'state_code', 'country_code', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', 'phone', 'fax', 'email', 'barcode',
623 'tva_intra', 'tva_assuj', 'localtax1_assuj', 'localtax2_assuj', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur'
624 ))) {
625 continue; // Discard if not into this dedicated list
626 }
627
628 $valuequalifiedforstorage = false;
629 if (!is_object($value)) {
630 if (empty($value) && in_array($key, array('country_code', 'idprof1', 'idprof2', 'tva_intra'))) {
631 $valuequalifiedforstorage = true; // We accept '' value for some fields
632 $value = (string) $value;
633 }
634 if (!is_null($value) && empty($value) && in_array($key, array('tva_assuj', 'localtax1_assuj', 'localtax2_assuj'))) {
635 $valuequalifiedforstorage = true; // We accept zero value for amounts
636 }
637 if (!is_null($value) && (string) $value !== '') {
638 $valuequalifiedforstorage = true;
639 }
640 }
641
642 if ($valuequalifiedforstorage) {
643 $this->object_data->thirdparty->$key = $value;
644 }
645 }
646 }
647
648 // Add my company info
649 if (!empty($mysoc) && !in_array($object->element, array('cashcontrol'))) {
650 $this->object_data->mycompany = new stdClass();
651
652 foreach ($mysoc as $key => $value) {
653 if (in_array($key, $arrayoffieldstoexclude)) {
654 continue; // Discard some properties
655 }
656 // List of fields qualified to keep
657 if (!in_array($key, array(
658 'name', 'name_alias', 'ref_ext', 'address', 'zip', 'town', 'state_code', 'country_code', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', 'phone', 'fax', 'email', 'barcode',
659 'tva_assuj', 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur'
660 ))) {
661 continue; // Discard if not into this dedicated list
662 }
663
664 $valuequalifiedforstorage = false;
665 if (!is_object($value)) {
666 if (empty($value) && in_array($key, array('country_code', 'idprof1', 'idprof2', 'tva_intra'))) {
667 $valuequalifiedforstorage = true; // We accept '' value for some fields
668 $value = (string) $value;
669 }
670 if (!is_null($value) && empty($value) && in_array($key, array('tva_assuj', 'localtax1_assuj', 'localtax2_assuj'))) {
671 $valuequalifiedforstorage = true; // We accept zero value for amounts
672 }
673 if (!is_null($value) && (string) $value !== '') {
674 $valuequalifiedforstorage = true;
675 }
676 }
677
678 if ($valuequalifiedforstorage) {
679 $this->object_data->mycompany->$key = $value;
680 }
681 }
682 }
683
684 // Add user info
685 if (!empty($user)) {
686 $this->fk_user = $user->id;
687 $this->user_fullname = $user->getFullName($langs);
688 }
689
690 // Field specific to object
691 if ($this->element == 'facture') {
692 '@phan-var-force Facture $object';
693 $this->module_source = (string) $object->module_source;
694
695 foreach ($object as $key => $value) {
696 if (in_array($key, $arrayoffieldstoexclude)) {
697 continue; // Discard some properties
698 }
699 // List of fields qualified
700 if (!in_array($key, array(
701 'ref', 'ref_client', 'ref_supplier', 'date', 'datef', 'datev', 'type',
702 //'vat_src_code', 'tva_tx', 'localtax1_tx', 'localtax2_tx', There is no rate at full doc level
703 'total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2',
704 'revenuestamp', 'datepointoftax', 'note_public',
705 'lines',
706 'module_source', 'pos_source', 'pos_print_counter', 'email_sent_counter'
707 ))) {
708 continue; // Discarded if not into the dedicated list
709 }
710 if ($key == 'lines') {
711 $lineid = 0;
712 foreach ($value as $tmpline) { // $tmpline is object FactureLine
713 $lineid++;
714 foreach ($tmpline as $keyline => $valueline) {
715 if (!in_array($keyline, array(
716 'ref', 'product_type', 'product_label',
717 'qty', 'subprice',
718 'vat_src_code', 'tva_tx', 'localtax1_tx', 'localtax2_tx',
719 'total_ht', 'total_tva', 'total_ttc', 'total_localtax1', 'total_localtax2',
720 'multicurrency_code', 'multicurrency_total_ht', 'multicurrency_total_tva', 'multicurrency_total_ttc',
721 'info_bits', 'special_code',
722 ))) {
723 continue; // Discard if not into a dedicated list
724 }
725
726 if (empty($this->object_data->invoiceline[$lineid]) || !is_object($this->object_data->invoiceline[$lineid])) { // To avoid warning
727 $this->object_data->invoiceline[$lineid] = new stdClass();
728 }
729
730 $valuequalifiedforstorage = false;
731 if (!is_object($valueline)) {
732 if (!is_null($valueline) && empty($valueline) && in_array($key, array('tva_tx', 'localtax1_tx', 'localtax2_tx', 'total_ht', 'total_tva', 'total_ttc', 'total_localtax1', 'total_localtax2'))) {
733 $valuequalifiedforstorage = true; // We accept zero value for amounts
734 }
735 if (!is_null($valueline) && (string) $valueline !== '') {
736 $valuequalifiedforstorage = true;
737 }
738 }
739 if ($keyline == 'product_label' && empty($valueline)) {
740 $valueline = dol_trunc(dolGetFirstLineOfText($tmpline->desc)); // Fallback on description if label is empty
741 $valuequalifiedforstorage = true;
742 }
743
744 if ($valuequalifiedforstorage) {
745 $this->object_data->invoiceline[$lineid]->$keyline = $valueline;
746 }
747 }
748 }
749 } else {
750 $valuequalifiedforstorage = false;
751 if (!is_object($value)) {
752 if (empty($value) && in_array($key, array('pos_source', 'module_source'))) {
753 $valuequalifiedforstorage = true; // We accept '' value for some fields
754 $value = (string) $value;
755 }
756 if (!is_null($value) && empty($value) && in_array($key, array('total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'pos_print_counter', 'email_sent_counter'))) {
757 $valuequalifiedforstorage = true; // We accept zero value for amounts
758 }
759 if (!is_null($value) && (string) $value !== '') {
760 $valuequalifiedforstorage = true;
761 }
762 }
763
764 if ($valuequalifiedforstorage) {
765 $this->object_data->$key = $value;
766 }
767 }
768 }
769
770 if (!empty($object->newref)) {
771 $this->object_data->ref = $object->newref;
772 }
773
774 // Add data for action emails
775 if ($action == 'BILL_SENTBYMAIL') {
776 $this->object_data->action_email_sent = array(
777 "email_from" => $object->context['email_from'],
778 "email_to" => $object->context['email_to'],
779 "email_msgid" => $object->context['email_msgid']
780 );
781 }
782 } elseif ($this->element == 'invoice_supplier') {
783 '@phan-var-force FactureFournisseur $object';
784 foreach ($object as $key => $value) {
785 if (in_array($key, $arrayoffieldstoexclude)) {
786 continue; // Discard some properties
787 }
788 // List of fields qualified
789 if (!in_array($key, array(
790 'ref', 'ref_client', 'ref_supplier', 'date', 'datef', 'type', 'total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'revenuestamp', 'datepointoftax', 'note_public'
791 ))) {
792 continue; // Discard if not into a dedicated list
793 }
794
795 $valuequalifiedforstorage = false;
796 if (!is_object($value)) {
797 if (empty($value) && in_array($key, array('pos_source', 'module_source'))) {
798 $valuequalifiedforstorage = true; // We accept '' value for some fields
799 $value = (string) $value;
800 }
801 if (!is_null($value) && empty($value) && in_array($key, array('total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'pos_print_counter', 'email_sent_counter'))) {
802 $valuequalifiedforstorage = true; // We accept zero value for amounts
803 }
804 if (!is_null($value) && (string) $value !== '') {
805 $valuequalifiedforstorage = true;
806 }
807 }
808
809 if ($valuequalifiedforstorage) {
810 $this->object_data->$key = $value;
811 }
812 }
813
814 if (!empty($object->newref)) {
815 $this->object_data->ref = $object->newref;
816 }
817 } elseif ($this->element == 'payment' || $this->element == 'payment_supplier' || $this->element == 'payment_donation' || $this->element == 'payment_various') {
818 '@phan-var-force Paiement|PaiementFourn|PaymentDonation|PaymentVarious $object';
819 $datepayment = $object->datepaye ? $object->datepaye : ($object->datepaid ? $object->datepaid : $object->datep);
820 $paymenttypeid = $object->paiementid ? $object->paiementid : ($object->paymenttype ? $object->paymenttype : $object->type_payment);
821
822 $this->object_data->ref = $object->ref;
823 $this->object_data->date = $datepayment;
824 $this->object_data->type_code = dol_getIdFromCode($this->db, $paymenttypeid, 'c_paiement', 'id', 'code');
825
826 if (!empty($object->num_payment)) {
827 $this->object_data->payment_num = $object->num_payment;
828 }
829 if (!empty($object->note_private)) {
830 $this->object_data->note_private = $object->note_private;
831 }
832 //$this->object_data->fk_account = $object->fk_account;
833 //var_dump($this->object_data);exit;
834
835 $totalamount = 0;
836
837 $this->linktype = $this->element;
838 $this->linktoref = '';
839
840 // Loop on each invoice payment amount (the payment_part)
841 if (is_array($object->amounts) && !empty($object->amounts)) {
842 // Loop on each invoice the payment is part of to set the linktoref and the module_source
843 $originofpayment = null;
844 $paymentpartnumber = 0;
845 foreach ($object->amounts as $objid => $amount) {
846 if (empty($amount)) {
847 continue;
848 }
849
850 $totalamount += $amount;
851
852 $tmpobject = null;
853 if ($this->element == 'payment_supplier') {
854 include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
855 $tmpobject = new FactureFournisseur($this->db);
856 } elseif ($this->element == 'payment') {
857 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
858 $tmpobject = new Facture($this->db);
859 } elseif ($this->element == 'payment_donation') {
860 include_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php';
861 $tmpobject = new Don($this->db);
862 } elseif ($this->element == 'payment_various') {
863 include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
864 $tmpobject = new PaymentVarious($this->db);
865 }
866
867 if (!is_object($tmpobject)) {
868 continue;
869 }
870
871 $result = $tmpobject->fetch($objid);
872
873 if ($result <= 0) {
874 $this->error = $tmpobject->error;
875 $this->errors = $tmpobject->errors;
876 dol_syslog("Failed to fetch object with id ".$objid, LOG_ERR);
877 return -1;
878 }
879
880 $this->linktoref .= ($this->linktoref ? ',' : '').$tmpobject->ref;
881 // Set the ->module_source of payment from origin object if relevant
882 if (property_exists($tmpobject, 'module_source')) {
883 if (is_null($originofpayment)) {
884 $originofpayment = $tmpobject->module_source;
885 } elseif ($originofpayment != $tmpobject->module_source) {
886 $originofpayment = 'mix'; // the payment is on several invoices with different origins
887 } else {
888 $originofpayment = (string) $tmpobject->module_source;
889 }
890 }
891
892 $paymentpart = new stdClass();
893 $paymentpart->amount = $amount;
894
895 // If we want to add thirdparty on each payment level
896 // (seems not necessary as we have one thirdparty per payment on invoice level)
897 if ($addthirdpartyatpaymentlevel) {
898 $result = $tmpobject->fetch_thirdparty();
899 if ($result == 0) {
900 $this->error = 'Failed to fetch thirdparty for object with id '.$tmpobject->id;
901 $this->errors[] = $this->error;
902 dol_syslog("Failed to fetch thirdparty for object with id ".$tmpobject->id, LOG_ERR);
903 return -1;
904 } elseif ($result < 0) {
905 $this->error = $tmpobject->error;
906 $this->errors = $tmpobject->errors;
907 return -1;
908 }
909
910 $paymentpart->thirdparty = new stdClass();
911 foreach ($tmpobject->thirdparty as $key => $value) {
912 if (in_array($key, $arrayoffieldstoexclude)) {
913 continue; // Discard some properties
914 }
915 // List of thirdparty fields qualified
916 if (!in_array($key, array(
917 'name', 'name_alias', 'ref_ext', 'address', 'zip', 'town', 'state_code', 'country_code', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', 'phone', 'fax', 'email', 'barcode',
918 'tva_intra', 'tva_assuj', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur'
919 ))) {
920 continue; // Discard if not into a dedicated list
921 }
922 if (!is_object($value) && !is_null($value) && $value !== '') {
923 $paymentpart->thirdparty->$key = $value;
924 }
925 }
926 }
927
928 // Init object to avoid warnings
929 if ($this->element == 'payment_donation') {
930 $paymentpart->donation = new stdClass();
931 } elseif ($this->element == 'payment_various') {
932 $paymentpart->various = new stdClass();
933 } else {
934 $paymentpart->invoice = new stdClass();
935 }
936
937 if ($this->element != 'payment_various') {
938 foreach ($tmpobject as $key => $value) {
939 if (in_array($key, $arrayoffieldstoexclude)) {
940 continue; // Discard some properties
941 }
942 // List of fields qualified
943 if (!in_array($key, array(
944 'ref', 'ref_client', 'ref_supplier', 'date', 'datef', 'type', 'total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'revenuestamp', 'datepointoftax', 'note_public',
945 'pos_source', 'module_source', 'pos_print_counter', 'email_sent_counter'
946 ))) {
947 continue; // Discard if not into a dedicated list
948 }
949
950 $valuequalifiedforstorage = false;
951 if (!is_object($value)) {
952 if (empty($value) && in_array($key, array('pos_source', 'module_source'))) {
953 $valuequalifiedforstorage = true; // We accept '' value for some fields
954 $value = (string) $value;
955 }
956 if (!is_null($value) && empty($value) && in_array($key, array('total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'pos_print_counter', 'email_sent_counter'))) {
957 $valuequalifiedforstorage = true; // We accept zero value for amounts
958 }
959 if (!is_null($value) && (string) $value !== '') {
960 $valuequalifiedforstorage = true;
961 }
962 }
963
964 if ($valuequalifiedforstorage) {
965 if ($this->element == 'payment_donation') {
966 $paymentpart->donation->$key = $value;
967 } elseif ($this->element == 'payment_various') {
968 $paymentpart->various->$key = $value;
969 } else {
970 $paymentpart->invoice->$key = $value;
971 }
972 }
973 }
974
975 $paymentpartnumber++; // first payment will be 1
976 $this->object_data->payment_part[$paymentpartnumber] = $paymentpart;
977 }
978 }
979
980 $this->module_source = (string) $originofpayment;
981 } elseif (!empty($object->amount)) {
982 $totalamount = $object->amount;
983 }
984
985 $this->object_data->amount = $totalamount;
986
987 if (!empty($object->newref)) {
988 $this->object_data->ref = $object->newref;
989 }
990 } elseif ($this->element == 'payment_salary') {
991 '@phan-var-force PaymentSalary $object';
992 $this->object_data->amounts = array($object->amount);
993
994 if (!empty($object->newref)) {
995 $this->object_data->ref = $object->newref;
996 }
997 } elseif ($this->element == 'subscription') {
998 '@phan-var-force Subscription $object';
999 foreach ($object as $key => $value) {
1000 if (in_array($key, $arrayoffieldstoexclude)) {
1001 continue; // Discard some properties
1002 }
1003 if (!in_array($key, array(
1004 'id', 'datec', 'dateh', 'datef', 'fk_adherent', 'amount', 'import_key', 'statut', 'note'
1005 ))) {
1006 continue; // Discard if not into a dedicated list
1007 }
1008 if (!is_object($value) && !is_null($value) && $value !== '') {
1009 $this->object_data->$key = $value;
1010 }
1011 }
1012
1013 if (!empty($object->newref)) {
1014 $this->object_data->ref = $object->newref;
1015 }
1016 } elseif ($this->element == 'stockmouvement') {
1017 '@phan-var-force StockTransfer $object';
1018 foreach ($object as $key => $value) {
1019 if (in_array($key, $arrayoffieldstoexclude)) {
1020 continue; // Discard some properties
1021 }
1022 if (!is_object($value) && !is_null($value) && $value !== '') {
1023 $this->object_data->$key = $value;
1024 }
1025 }
1026 } else {
1027 if ($object->element == 'cashcontrol') {
1028 $this->module_source = (string) $object->posmodule; // Module
1029 //$this->pos_source = (string) $object->posnumber; // Terminal
1030 }
1031
1032 // Generic case
1033 foreach ($object as $key => $value) {
1034 if (in_array($key, $arrayoffieldstoexclude)) {
1035 continue; // Discard some properties
1036 }
1037 if (!is_object($value) && !is_null($value) && $value !== '') {
1038 $this->object_data->$key = $value;
1039 }
1040 }
1041
1042 if (!empty($object->newref)) {
1043 $this->object_data->ref = $object->newref;
1044 }
1045 }
1046
1047 // A trick to be sure all the object_data is an associative array
1048 // json_encode and json_decode are not able to manage mixed object (with array/object, only full arrays or full objects)
1049 $this->object_data = json_decode(json_encode($this->object_data, JSON_FORCE_OBJECT), false);
1050
1051 return 1;
1052 }
1053
1060 public function fetch($id)
1061 {
1062 global $langs;
1063
1064 if (empty($id)) {
1065 $this->error = 'BadParameter';
1066 return -1;
1067 }
1068
1069 $sql = "SELECT b.rowid, b.date_creation, b.action, b.module_source, b.amounts_taxexcl, b.amounts, b.element, b.fk_object, b.entity,";
1070 $sql .= " b.certified, b.tms, b.fk_user, b.user_fullname, b.date_object, b.ref_object, b.linktoref, b.linktype, b.object_data, b.object_version, b.object_format, b.signature";
1071 $sql .= " FROM ".MAIN_DB_PREFIX."blockedlog as b";
1072 if ($id) {
1073 $sql .= " WHERE b.rowid = ".((int) $id);
1074 }
1075
1076 $resql = $this->db->query($sql);
1077 if ($resql) {
1078 $obj = $this->db->fetch_object($resql);
1079 if ($obj) {
1080 $this->id = $obj->rowid;
1081 $this->entity = $obj->entity;
1082
1083 $this->date_creation = $this->db->jdate($obj->date_creation); // jdate(date_creation)is UTC
1084 $this->date_modification = $this->db->jdate($obj->tms); // jdate(tms) is UTC
1085
1086 $this->action = $obj->action;
1087 $this->module_source = $obj->module_source;
1088
1089 $this->amounts_taxexcl = (is_null($obj->amounts_taxexcl) ? null : (float) $obj->amounts);
1090 $this->amounts = (float) $obj->amounts;
1091
1092 $this->fk_object = $obj->fk_object;
1093 $this->date_object = $this->db->jdate($obj->date_object); // jdate(date_object) is UTC
1094 $this->ref_object = $obj->ref_object;
1095 $this->linktoref = $obj->linktoref;
1096 $this->linktype = $obj->linktype;
1097
1098 $this->fk_user = $obj->fk_user;
1099 $this->user_fullname = $obj->user_fullname;
1100
1101 $this->object_data = $this->dolDecodeBlockedData($obj->object_data);
1102 $this->object_version = $obj->object_version;
1103 $this->object_format = $obj->object_format;
1104
1105 $this->element = $obj->element;
1106
1107 $this->signature = $obj->signature;
1108 $this->certified = ($obj->certified == 1);
1109
1110 return 1;
1111 } else {
1112 $langs->load("errors");
1113 $this->error = $langs->trans("ErrorRecordNotFound");
1114 return 0;
1115 }
1116 } else {
1117 $this->error = $this->db->error();
1118 return -1;
1119 }
1120 }
1121
1122
1130 public function dolEncodeBlockedData($data, $mode = 0)
1131 {
1132 $aaa = '';
1133 try {
1134 $aaa = json_encode($data);
1135 } catch (Exception $e) {
1136 // print $e->getErrs);
1137 }
1138
1139 return $aaa;
1140 }
1141
1142
1150 public function dolDecodeBlockedData($data, $mode = 0)
1151 {
1152 $aaa = null;
1153 try {
1154 $aaa = (object) jsonOrUnserialize($data, false);
1155 } catch (Exception $e) {
1156 // print $e->getErrs);
1157 }
1158
1159 return $aaa;
1160 }
1161
1162
1168 public function setCertified()
1169 {
1170 $res = $this->db->query("UPDATE ".MAIN_DB_PREFIX."blockedlog SET certified = 1 WHERE rowid = ".((int) $this->id));
1171 if (!$res) {
1172 return false;
1173 }
1174
1175 return true;
1176 }
1177
1185 public function create($user, $forcesignature = '')
1186 {
1187 global $conf, $langs, $mysoc;
1188
1189 $langs->load('blockedlog');
1190
1191 // Clean data
1192 $this->amounts = (float) $this->amounts;
1193
1194 dol_syslog(get_class($this).'::create action='.$this->action.' fk_user='.$this->fk_user.' user_fullname='.$this->user_fullname, LOG_DEBUG);
1195
1196 // Check parameters/properties
1197 if (!isset($this->amounts)) { // amount can be 0 for some events (like when module is disabled)
1198 $langs->load("errors");
1199 $this->error = $langs->trans("ErrorBlockLogNeedAmountsValue");
1200 dol_syslog($this->error, LOG_WARNING);
1201 return -1;
1202 }
1203
1204 if (empty($this->element)) {
1205 $langs->load("errors");
1206 $this->error = $langs->trans("ErrorBlockLogNeedElement");
1207 dol_syslog($this->error, LOG_WARNING);
1208 return -2;
1209 }
1210
1211 if (empty($this->object_data)) {
1212 $langs->load("errors");
1213 $this->error = $langs->trans("ErrorBlockLogNeedObject");
1214 dol_syslog($this->error, LOG_WARNING);
1215 return -2;
1216 }
1217
1218 if (empty($this->action)) {
1219 $langs->load("errors");
1220 $this->error = $langs->trans("ErrorBadParameterWhenCallingCreateOfBlockedLog");
1221 dol_syslog($this->error, LOG_WARNING);
1222 return -3;
1223 }
1224 if (empty($this->fk_user)) {
1225 $this->user_fullname = '(Anonymous)';
1226 }
1227
1228 include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
1229
1230 $this->db->begin();
1231
1232 $this->date_creation = dol_now();
1233
1234 $this->object_version = DOL_VERSION;
1235 // The object_format define the formatting rules into buildKeyForSignature and buildFirstPartOfKeyForSignature and buildFinalSignatureHash
1236 $this->object_format = 'V1'; // TODO Switch to V2 when v2 support is complete
1237
1238 try {
1239 $previoushash = $this->getPreviousHash(1, 0); // This get last record and lock database until insert is done and transaction closed
1240
1241 $concatenateddata = $this->buildKeyForSignature(); // All the information for the hash (meta data + data saved)
1242
1243 $this->signature = $this->buildFinalSignatureHash($previoushash.$concatenateddata); // Build the hmac signature
1244
1245 // For debug info (we can clean this field later)
1246 if (getDolGlobalString('BLOCKEDLOG_ADD_DEBUG_INFO')) {
1247 $this->debuginfo = $this->buildFirstPartOfKeyForSignature(); // Not used
1248 }
1249 } catch (Exception $e) {
1250 $this->error = $e->getMessage();
1251
1252 dol_syslog($this->error, LOG_ERR);
1253
1254 $this->db->rollback();
1255 return -1;
1256 }
1257
1258 if ($forcesignature) {
1259 $this->signature = $forcesignature;
1260 }
1261 //var_dump($concatenateddata);var_dump($previoushash);var_dump($this->signature);
1262
1263 $sql = "INSERT INTO ".MAIN_DB_PREFIX."blockedlog (";
1264 $sql .= " date_creation,";
1265 $sql .= " action,";
1266 $sql .= " module_source,";
1267 $sql .= " amounts_taxexcl,";
1268 $sql .= " amounts,";
1269 $sql .= " signature,";
1270 $sql .= " element,";
1271 $sql .= " fk_object,";
1272 $sql .= " date_object,";
1273 $sql .= " ref_object,";
1274 $sql .= " linktoref,";
1275 $sql .= " linktype,";
1276 $sql .= " object_data,";
1277 $sql .= " object_version,";
1278 $sql .= " object_format,";
1279 $sql .= " certified,";
1280 $sql .= " fk_user,";
1281 $sql .= " user_fullname,";
1282 $sql .= " entity,";
1283 $sql .= " debuginfo"; // Only stored
1284 $sql .= ") VALUES (";
1285 $sql .= "'".$this->db->idate($this->date_creation)."',";
1286 $sql .= "'".$this->db->escape($this->action)."',";
1287 $sql .= "'".$this->db->escape((string) $this->module_source)."',";
1288 $sql .= (is_null($this->amounts_taxexcl) ? "null" : (float) $this->amounts_taxexcl).",";
1289 $sql .= (float) $this->amounts.",";
1290 $sql .= "'".$this->db->escape($this->signature)."',";
1291 $sql .= "'".$this->db->escape($this->element)."',";
1292 $sql .= (int) $this->fk_object.",";
1293 $sql .= "'".$this->db->idate($this->date_object)."',";
1294 $sql .= "'".$this->db->escape($this->ref_object)."',";
1295 $sql .= ($this->linktoref ? "'".$this->db->escape($this->linktoref)."'" : "null").",";
1296 $sql .= ($this->linktoref ? "'".$this->db->escape($this->linktype)."'" : "null").",";
1297 $sql .= "'".$this->db->escape($this->dolEncodeBlockedData($this->object_data))."',";
1298 $sql .= "'".$this->db->escape($this->object_version)."',";
1299 $sql .= "'".$this->db->escape($this->object_format)."',";
1300 $sql .= "0,";
1301 $sql .= $this->fk_user.",";
1302 $sql .= "'".$this->db->escape($this->user_fullname)."',";
1303 $sql .= ($this->entity ? $this->entity : $conf->entity).",";
1304 $sql .= "'".$this->db->escape($this->debuginfo)."'";
1305 $sql .= ")";
1306
1307 /*
1308 $a = serialize($this->object_data); $a2 = unserialize($a); $a4 = print_r($a2, true);
1309 $b = json_encode($this->object_data); $b2 = json_decode($b); $b4 = print_r($b2, true);
1310 var_dump($a4 == print_r($this->object_data, true) ? 'a=a' : 'a not = a');
1311 var_dump($b4 == print_r($this->object_data, true) ? 'b=b' : 'b not = b');
1312 exit;
1313 */
1314
1315 $res = $this->db->query($sql);
1316 if ($res) {
1317 $id = $this->db->last_insert_id(MAIN_DB_PREFIX."blockedlog");
1318
1319 if ($id > 0) {
1320 $this->id = $id;
1321
1322 $this->db->commit();
1323
1324 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
1325 if (isALNERunningVersion(1) && $mysoc->country_code == 'FR') {
1326 // TODO Push last rowid + signature to remote dolibarr server
1327 /*
1328 $remoteurl = '';
1329 $param = '';
1330 $addheaders = array();
1331 $timeoutconnect = 0;
1332 $timeoutresponse = 0;
1333
1334 $result = getURLContent($remoteurl, 'POSTALREADYFORMATED', $param, 1, $addheaders, 'https', 0, -1, $timeoutconnect, $timeoutresponse);
1335 */
1336 }
1337
1338 return $this->id;
1339 } else {
1340 $this->db->rollback();
1341 return -2;
1342 }
1343 } else {
1344 $this->error = $this->db->error();
1345 $this->db->rollback();
1346 return -1;
1347 }
1348
1349 // The commit or rollback will release the lock so app can insert other record now
1350 }
1351
1359 public function checkSignature($previoushash = '', $returnarray = 0)
1360 {
1361 if (empty($previoushash)) {
1362 $previoushash = $this->getPreviousHash(0, $this->id);
1363 }
1364
1365 $concatenateddata = '';
1366 $signature = '';
1367
1368 // Recalculate the signature
1369 try {
1370 // Build the string for the signature
1371 $concatenateddata = $this->buildKeyForSignature();
1372
1373 $signature = $this->buildFinalSignatureHash($previoushash.$concatenateddata);
1374 } catch (Exception $e) {
1375 $res = ($signature === $this->signature);
1376 $this->error = $e->getMessage();
1377
1378 dol_syslog($this->error, LOG_ERR);
1379
1380 if ($returnarray) {
1381 return array('checkresult' => $res, 'calculatedsignature' => $signature, 'previoushash' => $previoushash);
1382 } else {
1383 return false;
1384 }
1385 }
1386
1387 $res = ($signature === $this->signature);
1388
1389 if (!$res) {
1390 $this->error = 'Signature KO';
1391 }
1392
1393 if ($returnarray) {
1394 if ($returnarray == 1) {
1395 unset($concatenateddata);
1396 return array('checkresult' => $res, 'calculatedsignature' => $signature, 'previoushash' => $previoushash);
1397 } else { // Consume much memory ($concatenateddata is a large var)
1398 return array('checkresult' => $res, 'calculatedsignature' => $signature, 'previoushash' => $previoushash, 'keyforsignature' => $concatenateddata);
1399 }
1400 } else {
1401 unset($concatenateddata);
1402 return $res;
1403 }
1404 }
1405
1414 {
1415 // Note: $this->amounts can be '0', '1.1', '1.123'; // All 0 at end should have been removed already
1416 if ($this->object_format == '') {
1417 return $this->date_creation.'|'.$this->action.'|'.$this->amounts.'|'.$this->ref_object.'|'.$this->date_object.'|'.$this->user_fullname;
1418 } elseif ($this->object_format == 'V1') { // Note: $this->amounts can be '0', '1.1', '1.123'; // All 0 at end should have been removed already
1419 return $this->date_creation.'|'.$this->action.'|'.$this->amounts.'|'.$this->ref_object.'|'.$this->date_object.'|'.$this->user_fullname;
1420 } elseif ($this->object_format == 'V2') {
1421 $s = $this->entity;
1422 $s .= '|'.$this->date_creation.'|'.$this->action.'|'.$this->module_source.'|'.$this->amounts_taxexcl.'|'.$this->amounts.'|'.$this->ref_object.'|'.$this->date_object.'|'.$this->user_fullname;
1423 $s .= '|'.(string) $this->linktoref;
1424 $s .= '|'.(string) $this->linktype;
1425 return $s;
1426 } else {
1427 throw new Exception('Error bad value "'.$this->object_format.'" for object_format');
1428 }
1429 }
1430
1436 public function buildKeyForSignature()
1437 {
1438 //print_r($this->object_data);
1439 if ($this->object_format == '') {
1440 return $this->buildFirstPartOfKeyForSignature().'|'.print_r($this->object_data, true);
1441 } elseif ($this->object_format == 'V1') { // Note: $this->amounts can be '0', '1.1', '1.123'; // All 0 at end should have been removed already
1442 return $this->buildFirstPartOfKeyForSignature().'|'.json_encode($this->object_data, JSON_FORCE_OBJECT);
1443 } elseif ($this->object_format == 'V2') {
1444 return $this->buildFirstPartOfKeyForSignature().'|'.json_encode($this->object_data, JSON_FORCE_OBJECT);
1445 } else {
1446 throw new Exception('Error bad value "'.$this->object_format.'" for object_format');
1447 }
1448 }
1449
1456 private function buildFinalSignatureHash($clearstring)
1457 {
1458 if ($this->object_format == '') {
1459 return dol_hash($clearstring, '5');
1460 } elseif ($this->object_format == 'V1') {
1461 return dol_hash($clearstring, '5');
1462 } elseif ($this->object_format == 'V2') {
1463 // BLOCKEDLOG_HMAC_KEY is a HMAC key starting with 'BLOCKEDLOGHMAC....', but it is not stored as a clear data. It will be decrypted later.
1464 $hmac_encoded_secret_key = getDolGlobalString('BLOCKEDLOG_HMAC_KEY');
1465 if (empty($hmac_encoded_secret_key)) {
1466 throw new Exception('Error: BLOCKEDLOG_HMAC_KEY was not found. It should have been initialized to a value "BLOCKEDLOG_HMAC_...." during initialization of module BlockedLog or during migration of v23');
1467 }
1468 $hmac_secret_key = dolDecrypt($hmac_encoded_secret_key);
1469 if (!preg_match('/^BLOCKEDLOGHMAC/', $hmac_secret_key)) {
1470 throw new Exception('Error: Failed to decode the crypted value of the parameter BLOCKEDLOG_HMAC_KEY using the $dolibarr_main_crypt_key. A value was found but decoding failed. May be the database data were restored onto another environment and the coding/decoding key $dolibarr_main_dolcrypt_key or $dolibarr_main_instance_unique_id was not restored with the same value in conf.php file.');
1471 }
1472
1473 return hash_hmac('sha256', $clearstring, $hmac_secret_key);
1474 } else {
1475 throw new Exception('Error bad value "'.$this->object_format.'" for object_format');
1476 }
1477 }
1478
1486 public function getPreviousHash($withlock = 0, $beforeid = 0)
1487 {
1488 global $conf;
1489
1490 $previoussignature = '';
1491
1492 // Fast search of previous record by searching with beforeid - 1. This is very fast and will work 99% of time.
1493 if ($beforeid) {
1494 $sql = "SELECT rowid, signature FROM ".MAIN_DB_PREFIX."blockedlog";
1495 $sql .= " WHERE entity = ".((int) $conf->entity);
1496 $sql .= " AND rowid = ".((int) $beforeid - 1);
1497 $sql .= ($withlock ? " FOR UPDATE " : "");
1498
1499 $resql = $this->db->query($sql);
1500 if ($resql) {
1501 $obj = $this->db->fetch_object($resql);
1502 if ($obj) {
1503 $previoussignature = $obj->signature;
1504 }
1505 } else {
1506 dol_print_error($this->db);
1507 exit;
1508 }
1509 }
1510
1511 if (empty($previoussignature)) {
1512 $sql = "SELECT rowid, signature FROM ".MAIN_DB_PREFIX."blockedlog";
1513 if ($beforeid) {
1514 $sql .= $this->db->hintindex('entity_rowid', 1);
1515 }
1516 $sql .= " WHERE entity = ".((int) $conf->entity);
1517 if ($beforeid) {
1518 $sql .= " AND rowid < ".(int) $beforeid;
1519 }
1520 $sql .= " ORDER BY rowid DESC LIMIT 1";
1521 $sql .= ($withlock ? " FOR UPDATE " : "");
1522
1523 $resql = $this->db->query($sql);
1524 if ($resql) {
1525 $obj = $this->db->fetch_object($resql);
1526 if ($obj) {
1527 $previoussignature = $obj->signature;
1528 }
1529 } else {
1530 dol_print_error($this->db); // can happen after a deadlock when too many requests do create into blocked log happen at the same time.
1531 http_response_code(503);
1532 exit;
1533 }
1534 }
1535
1536 if (empty($previoussignature)) {
1537 // First signature line (line 0)
1538 $previoussignature = $this->getOrInitFirstSignature();
1539 }
1540
1541 return $previoussignature;
1542 }
1543
1562 public function getLog($element, $fk_object, $limit = 0, $sortfield = '', $sortorder = '', $search_fk_user = -1, $search_start = -1, $search_end = -1, $search_ref = '', $search_amount = '', $search_code = '', $search_signature = '', $search_module_source = '')
1563 {
1564 global $conf;
1565 //global $cachedlogs;
1566
1567 /* $cachedlogs allow fastest search */
1568 //if (empty($cachedlogs)) $cachedlogs = array();
1569
1570 if ($element == 'all') {
1571 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
1572 WHERE entity = ".$conf->entity;
1573 } elseif ($element == 'not_certified') {
1574 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
1575 WHERE entity = ".$conf->entity." AND certified = 0";
1576 } elseif ($element == 'just_certified') {
1577 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
1578 WHERE entity = ".$conf->entity." AND certified = 1";
1579 } else {
1580 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
1581 WHERE entity = ".$conf->entity." AND element = '".$this->db->escape($element)."'";
1582 }
1583
1584 if ($fk_object) {
1585 $sql .= natural_search("rowid", (string) $fk_object, 1);
1586 }
1587 if ($search_fk_user > 0) {
1588 $sql .= natural_search("fk_user", (string) $search_fk_user, 2);
1589 }
1590 if ($search_start > 0) {
1591 $sql .= " AND date_creation >= '".$this->db->idate($search_start)."'";
1592 }
1593 if ($search_end > 0) {
1594 $sql .= " AND date_creation <= '".$this->db->idate($search_end)."'";
1595 }
1596 if ($search_ref != '') {
1597 $sql .= " AND (".natural_search("ref_object", $search_ref, 0, 1);
1598 $sql .= " OR ".natural_search("linktoref", $search_ref, 0, 1).")";
1599 }
1600 if ($search_amount != '') {
1601 $sql .= natural_search("amounts", $search_amount, 1);
1602 }
1603 if ($search_signature != '') {
1604 $sql .= natural_search("signature", $search_signature, 0);
1605 }
1606 if (is_array($search_code)) {
1607 if (!empty($search_code)) {
1608 $sql .= natural_search("action", implode(',', $search_code), 3);
1609 }
1610 } else {
1611 if ($search_code != '' && $search_code != '-1') {
1612 $sql .= natural_search("action", $search_code, 3);
1613 }
1614 }
1615 if (is_array($search_module_source)) {
1616 if (!empty($search_module_source)) {
1617 $sql .= " AND (";
1618 if (in_array('0', $search_module_source)) {
1619 $sql .= "module_source = ''";
1620 unset($search_module_source[0]);
1621 if (!empty($search_module_source)) {
1622 $sql .= " OR ";
1623 }
1624 }
1625 if (!empty($search_module_source)) {
1626 $sql .= natural_search("module_source", implode(',', $search_module_source), 3, 1);
1627 }
1628 $sql .= " OR module_source = 'mix'"; // When a payment was reocrd and payment was on an invoice with different origins (pos and not pos)
1629 $sql .= ")";
1630 }
1631 } else {
1632 if ($search_module_source != '' && $search_module_source != '-1') {
1633 $sql .= natural_search("module_source", $search_module_source, 3);
1634 }
1635 }
1636
1637 $sql .= $this->db->order($sortfield, $sortorder);
1638 $sql .= $this->db->plimit($limit + 1); // We want more, because we will stop into loop later with error if we reach max
1639
1640 $res = $this->db->query($sql);
1641 if ($res) {
1642 $results = array();
1643
1644 $i = 0;
1645 while ($obj = $this->db->fetch_object($res)) {
1646 $i++;
1647 if ($i > $limit) {
1648 // Too many record, we will consume too much memory
1649 return -2;
1650 }
1651
1652 //if (!isset($cachedlogs[$obj->rowid]))
1653 //{
1654 $b = new BlockedLog($this->db);
1655 $b->fetch($obj->rowid);
1656 //$b->loadTrackedEvents();
1657 //$cachedlogs[$obj->rowid] = $b;
1658 //}
1659
1660 //$results[] = $cachedlogs[$obj->rowid];
1661 $results[] = $b;
1662 }
1663
1664 return $results;
1665 }
1666
1667 return -1;
1668 }
1669
1675 public function getOrInitFirstSignature()
1676 {
1677 global $db, $conf;
1678
1679 if (!getDolGlobalString('BLOCKEDLOG_ENTITY_FINGERPRINT')) { // creation of a unique fingerprint
1680 require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
1681 require_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
1682 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1683
1684 //$fingerprint = dol_hash(print_r($mysoc, true).getRandomPassword(true), '5');
1685 $fingerprint = bin2hex(random_bytes(32)); // 64 char hex
1686
1687 dolibarr_set_const($db, 'BLOCKEDLOG_ENTITY_FINGERPRINT', $fingerprint, 'chaine', 0, 'Numeric Unique Fingerprint', $conf->entity);
1688
1689 $conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT = $fingerprint;
1690 }
1691
1692 if (!getDolGlobalString('BLOCKEDLOG_LAST_RECORD_FINGERPRINT')) {
1693 dolibarr_set_const($db, 'BLOCKEDLOG_LAST_RECORD_FINGERPRINT', '0:none', 'chaine', 0, 'Last record fingerprint', $conf->entity);
1694 }
1695
1696 return getDolGlobalString('BLOCKEDLOG_ENTITY_FINGERPRINT');
1697 }
1698
1699
1706 public function alreadyUsed($ignoresystem = 0)
1707 {
1708 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
1709 return isBlockedLogUsed($ignoresystem);
1710 }
1711
1712
1718 public function canBeEnabled()
1719 {
1720 global $dolibarr_main_force_https;
1721
1722 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
1723
1724 if (isALNEQualifiedVersion(0, 1) && empty($dolibarr_main_force_https)) {
1725 return 'Error: The HTTPS must be forced by setting the $dolibarr_main_force_https into Dolibarr conf/conf.php file to allow the use of this module in France.';
1726 }
1727
1728 return '';
1729 }
1730
1731
1737 public function canBeDisabled()
1738 {
1739 global $mysoc;
1740
1741 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
1742
1743 $canbedisabled = 1;
1744 if (isALNEQualifiedVersion() && $mysoc->country_code == 'FR') {
1745 $canbedisabled = 0;
1746 }
1747
1748 return $canbedisabled;
1749 }
1750}
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
dolibarr_set_const($db, $name, $value, $type='chaine', $visible=0, $note='', $entity=1)
Insert a parameter (key,value) into database (delete old key then insert it again).
isALNEQualifiedVersion($ignoredev=0, $ignoremodule=0)
Return if the version is a candidate version to get the LNE certification and if the prerequisites ar...
isBlockedLogUsed($ignoresystem=0)
Return if the blocked log was already used to block some events.
isALNERunningVersion($blockedlogtestalreadydone=0)
Return if the application is executed with the LNE requirements on.
Class to manage Blocked Log.
canBeEnabled()
Check if module can be enabled.
getObjectLink()
Try to retrieve source object (it it still exists).
buildFinalSignatureHash($clearstring)
Return a hash that is the signature of a line (hash_hmac en SHA256 des données + clé secrète)
getOrInitFirstSignature()
Return the signature (hash) of the "genesis-block" (Block 0).
alreadyUsed($ignoresystem=0)
Check if module was already used or not for at least one recording.
canBeDisabled()
Check if module can be disabled.
buildKeyForSignature()
Return the string for signature (clear data).
create($user, $forcesignature='')
Create blocked log in database.
dolEncodeBlockedData($data, $mode=0)
Encode data.
loadTrackedEvents()
Load list of tracked events into $this->trackedevents.
getLog($element, $fk_object, $limit=0, $sortfield='', $sortorder='', $search_fk_user=-1, $search_start=-1, $search_end=-1, $search_ref='', $search_amount='', $search_code='', $search_signature='', $search_module_source='')
Return array of log objects (with criteria)
setObjectData(&$object, $action, $amounts, $fuser=null, $amounts_taxexcl=null)
Populate properties of an unalterable log entry from object data.
__construct(DoliDB $db)
Constructor.
getPreviousHash($withlock=0, $beforeid=0)
Get previous signature/hash in chain.
checkSignature($previoushash='', $returnarray=0)
Check if calculated signature still correct compared to the value in the chain.
dolDecodeBlockedData($data, $mode=0)
Decode data.
fetch($id)
Get object from database.
getUser()
Try to retrieve user author.
setCertified()
Set block certified by an external authority.
buildFirstPartOfKeyForSignature()
Return first part of string for signature (clear data) Note: rowid of line not included as it is not ...
Class to manage cash fence.
Class to manage Dolibarr database access.
Class to manage donations.
Definition don.class.php:41
Class to manage suppliers invoices.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage invoices.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage stock movements.
Class to manage payments for supplier invoices.
Class to manage payments of customer invoices.
Class to manage payments of donations.
Class to manage various payments.
Class to manage projects.
Class to manage subscriptions of foundation members.
Class to manage Dolibarr users.
global $mysoc
dol_now($mode='gmt')
Return date for now.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
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)
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
jsonOrUnserialize($stringtodecode, $assoc=true)
Decode an encoded string.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
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_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
dolDecrypt($chain, $key='')
Decode a string with a symmetric encryption.