dolibarr 24.0.0-beta
blockedlog_archives.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 ATM Consulting <contact@atm-consulting.fr>
3 * Copyright (C) 2017-2018 Laurent Destailleur <eldy@destailleur.fr>
4 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
28// Load Dolibarr environment
29require '../../main.inc.php';
40require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
41require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
42require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
43require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
44require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
45require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
46require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
47
48// Load translation files required by the page
49$langs->loadLangs(array('admin', 'banks', 'bills', 'blockedlog', 'cashdesk', 'compta', 'other'));
50
51// Get Parameters
52$action = GETPOST('action', 'aZ09');
53$confirm = GETPOST('confirm', 'aZ09'); // Used by the actions_linkedfiles.inc.php
54$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : getDolDefaultContextPage(__FILE__); // To manage different context of search
55$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
56$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print')
57
58$withtab = GETPOSTISSET('withtab') ? GETPOSTINT('withtab') : 1;
59
60$search_showonlyerrors = GETPOSTINT('search_showonlyerrors');
61if ($search_showonlyerrors < 0) {
62 $search_showonlyerrors = 0;
63}
64
65$search_startyear = GETPOSTINT('search_startyear');
66$search_startmonth = GETPOSTINT('search_startmonth');
67$search_startday = GETPOSTINT('search_startday');
68$search_endyear = GETPOSTINT('search_endyear');
69$search_endmonth = GETPOSTINT('search_endmonth');
70$search_endday = GETPOSTINT('search_endday');
71$search_id = GETPOST('search_id', 'alpha');
72$search_fk_user = GETPOST('search_fk_user', 'intcomma');
73$search_start = -1;
74if (GETPOST('search_startyear') != '') {
75 $search_start = dol_mktime(0, 0, 0, $search_startmonth, $search_startday, $search_startyear);
76}
77$search_end = -1;
78if (GETPOST('search_endyear') != '') {
79 $search_end = dol_mktime(23, 59, 59, $search_endmonth, $search_endday, $search_endyear);
80}
81$search_code = GETPOST('search_code', 'array:alpha');
82$search_ref = GETPOST('search_ref', 'alpha');
83$search_amount = GETPOST('search_amount', 'alpha');
84$search_signature = GETPOST('search_signature', 'alpha');
85
86if (($search_start == -1 || empty($search_start)) && !GETPOSTISSET('search_startmonth') && !GETPOSTISSET('begin')) {
87 $search_start = dol_time_plus_duree(dol_now(), -1, 'w');
88 $tmparray = dol_getdate($search_start);
89 $search_startday = $tmparray['mday'];
90 $search_startmonth = $tmparray['mon'];
91 $search_startyear = $tmparray['year'];
92}
93
94// Load variable for pagination
95$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
96$sortfield = GETPOST('sortfield', 'aZ09comma');
97$sortorder = GETPOST('sortorder', 'aZ09comma');
98$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
99if (empty($page) || $page == -1) {
100 $page = 0;
101} // If $page is not defined, or '' or -1
102$offset = $limit * $page;
103$pageprev = $page - 1;
104$pagenext = $page + 1;
105
106if (empty($sortfield)) {
107 $sortfield = 'rowid';
108}
109if (empty($sortorder)) {
110 $sortorder = 'DESC';
111}
112
113$block_static = new BlockedLog($db);
114$block_static->loadTrackedEvents();
115
116// Access Control
117if (((!$user->admin && !$user->hasRight('blockedlog', 'read')) || !isModEnabled('blockedlog')) && !userIsTaxAuditor()) {
119}
120
121// We force also permission to write because it does not exists and we need it to upload a file
122$user->rights->blockedlog->create = 1;
123
124$result = restrictedArea($user, 'blockedlog', 0, '');
125
126// Execution Time
127$max_execution_time_for_importexport = getDolGlobalInt('EXPORT_MAX_EXECUTION_TIME', 300); // 5mn if not defined
128$max_time = @ini_get("max_execution_time");
129if ($max_time && $max_time < $max_execution_time_for_importexport) {
130 dol_syslog("max_execution_time=".$max_time." is lower than max_execution_time_for_importexport=".$max_execution_time_for_importexport.". We try to increase it dynamically.");
131 @ini_set("max_execution_time", $max_execution_time_for_importexport); // This work only if safe mode is off. also web servers has timeout of 300
132}
133
134$MAXLINES = getDolGlobalInt('BLOCKEDLOG_MAX_LINES', 10000);
135$MAXFORSHOWNLINKS = getDolGlobalInt('BLOCKEDLOG_MAX_FOR_SHOWN_LINKS', 100);
136
137$permission = $user->hasRight('blockedlog', 'read');
138$permissiontoadd = $user->hasRight('blockedlog', 'read'); // Permission is to upload new files to scan them
139$permtoedit = $permissiontoadd;
140
141$upload_dir = getMultidirOutput($block_static, 'blockedlog').'/archives';
142
143dol_mkdir($upload_dir);
144
145$fh = null;
146
147
148/*
149 * Actions
150 */
151
152// Purge search criteria
153if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
154 $search_id = '';
155 $search_fk_user = '';
156 $search_start = -1;
157 $search_end = -1;
158 $search_code = array();
159 $search_ref = '';
160 $search_amount = '';
161 $search_signature = '';
162 $search_showonlyerrors = 0;
163 $search_startyear = '';
164 $search_startmonth = '';
165 $search_startday = '';
166 $search_endyear = '';
167 $search_endmonth = '';
168 $search_endday = '';
169 $toselect = array();
170 $search_array_options = array();
171}
172
173include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php';
174
175if ($action == 'export' && $user->hasRight('blockedlog', 'read')) { // read is read/export for blockedlog
176 $error = 0;
177
178 $previoushash = '';
179 $firstid = '';
180 $periodnotcomplete = 0;
181
182 if (! (GETPOSTINT('yeartoexport') > 0)) {
183 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Year")), null, "errors");
184 $action = '';
185 $error++;
186 }
187 /*
188 if (empty($hmacexportkey)) {
189 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Password")), null, "errors");
190 $error++;
191 }
192 */
193
194 if (!$error) {
195 // Refuse and cancel any trigger event if we are running a certified version without forcing https.
196 // This is a security requirement for certification. We do this check before any other to avoid any risk of logging an event that should be blocked because of non respect of certification rules.
197 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
198 include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/securitycore.lib.php';
199
200 $isqualified = isALNERunningVersion(1);
201 if ($isqualified && (defined('CERTIF_LNE') && (int) constant('CERTIF_LNE') == 1) && !isHTTPS() && !in_array($action, array('DOC_PREVIEW', 'DOC_DOWNLOAD'))) {
202 $errmsg = 'Error: You are using Dolibarr with the module to be compliant with the French Law Finance certification. In this version, the HTTPS is mandatory to be allowed to record any event (Your hosting does not match the install requirements).';
203 dol_syslog($errmsg, LOG_ERR);
204
205 setEventMessages($errmsg, null, 'errors');
206 $error++;
207 }
208 }
209
210 $dates = dol_get_first_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 1);
211 $datee = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 12);
212
213 if ($datee >= dol_now()) {
214 $periodnotcomplete = 1;
215 }
216
217 $suffixperiod = ($periodnotcomplete ? 'INCOMPLETE' : 'DONOTMODIFY');
218
219 if (!$error) {
220 // Get the ID of the first line qualified
221 $sql = "SELECT rowid";
222 $sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
223 $sql .= " WHERE entity = ".((int) $conf->entity);
224 // For unalterable log, we are using the date of creation of the log. Note that a bookkeeper may decide to dispatch an invoice
225 // on different periods for example to manage depreciation.
226 $sql .= " AND date_creation BETWEEN '".$db->idate($dates, 'gmt')."' AND '".$db->idate($datee, 'gmt')."'";
227 $sql .= " ORDER BY date_creation ASC, rowid ASC"; // Required so we get the first one
228 $sql .= $db->plimit(1);
229
230 $res = $db->query($sql);
231 if ($res) {
232 // Make the first fetch to get first line and then get the previous hash.
233 $obj = $db->fetch_object($res);
234 if ($obj) {
235 $firstid = $obj->rowid;
236 $tmparray = $block_static->getPreviousHash(0, $firstid);
237 $previoushash = $tmparray['previoushash'];
238 } else { // If not data found for filter, we do not need previoushash neither firstid
239 $firstid = '';
240 $previoushash = 'nodata';
241 }
242 } else {
243 $error++;
244 setEventMessages($db->lasterror, null, 'errors');
245 }
246 }
247
248 // Define file name
249 $registrationnumber = getHashUniqueIdOfRegistration();
250 $secretkey = $registrationnumber;
251
252 $yearmonthtoexport = GETPOSTINT('yeartoexport').'-'.(GETPOSTINT('monthtoexport') > 0 ? sprintf("%02d", GETPOSTINT('monthtoexport')) : '');
253 $yearmonthdateofexport = dol_print_date(dol_now(), 'dayhourrfc', 'gmt');
254 $yearmonthdateofexportstandard = dol_print_date(dol_now(), 'dayhourlog', 'gmt');
255
256 $nameofdownoadedfile = "unalterable-log-archive-".$dolibarr_main_db_name."-".str_replace('-', '', $yearmonthtoexport).'-'.$yearmonthdateofexportstandard.'UTC-'.$suffixperiod.'.csv';
257
258 //$tmpfile = $conf->admin->dir_temp.'/unalterable-log-archive-tmp-'.$user->id.'.csv';
259 $tmpfileshort = 'blockedlog/archives/'.$nameofdownoadedfile;
260 $tmpfile = getMultidirOutput($block_static, 'blockedlog').'/archives/'.$nameofdownoadedfile;
261
262 $formatexport = 'VE2';
263
264 if (!$error) {
265 $fh = fopen($tmpfile, 'w');
266 if (empty($fh)) {
267 $error++;
268 setEventMessages('Failed to open file '.$tmpfileshort.' for writing.', null, 'errors');
269 }
270 }
271
272 if (!$error && $fh) {
273 $refinvoicefound = array();
274 $totalhtamount = array();
275 $totalvatamount = array();
276 $totalamount = array();
277
278 // Check we can validate the status of each line by getting the HMAC key in memory
279 $remoteobfuscationkey = '';
280 if (isALNERunningVersion(1) && $mysoc->country_code == 'FR') {
281 try {
282 $remoteobfuscationkey = $block_static->getObfuscationKey();
283 // Note: To emulate a pb in getting the obfuscation key, there is some code to uncomment into the method
284 } catch (Exception $e) {
285 $error++;
286
287 setEventMessages($e->getMessage(), null, 'errors');
288 setEventMessages('<a class="" href="'.$_SERVER["PHP_SELF"].'?clearcache=1">'.$langs->trans("Retry").'</a>', null, 'errors');
289 }
290 }
291
292 // Now restart request with all data, so without the limit(1) in sql request
293 $sql = "SELECT rowid, entity, date_creation, tms, user_fullname, action, module_source, pos_source, amounts_taxexcl, amounts, element, fk_object, date_object, ref_object,";
294 $sql .= " linktoref, linktype, signature, fk_user, object_data, object_version, object_format, debuginfo, note";
295 $sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
296 $sql .= " WHERE entity = ".((int) $conf->entity);
297 // For unalterable log, we are using the date of creation of the log. Note that a bookkeeper may decide to dispatch an invoice
298 // or payment on different periods for example to manage depreciation, but what we want here is not accountancy but payment data.
299 $sql .= " AND date_creation BETWEEN '".$db->idate($dates, 'gmt')."' AND '".$db->idate($datee, 'gmt')."'";
300 $sql .= " ORDER BY date_creation ASC, rowid ASC"; // Required so later we can use the parameter $previoushash of checkSignature()
301
302 $resql = $db->query($sql);
303 if ($resql) {
304 // Print line with title
305 fwrite($fh, "BEGIN - regnumber=".dol_trunc($registrationnumber, 10)." - date=".$yearmonthdateofexport." - period=".$yearmonthtoexport.($periodnotcomplete ? '-'.$suffixperiod : '')." - entity=".((int) $conf->entity)." - formatexport=".$formatexport." - user=".$user->getFullName($langs)
306 .';'.$langs->transnoentities('Id')
307 .';'.$langs->transnoentities('DateCreation')
308 .';'.$langs->transnoentities('Action')
309 .';'.$langs->transnoentities('Origin')
310 .';'.$langs->transnoentities('Terminal')
311 .';'.$langs->transnoentities('AmountHT')
312 .';'.$langs->transnoentities('AmountTTC')
313 .';'.$langs->transnoentities('Ref')
314 .';'.$langs->transnoentities('Date')
315 .';'.$langs->transnoentities('User')
316 .';'.$langs->transnoentities('LinkTo')
317 .';'.$langs->transnoentities('LinkType')
318 .';'.$langs->transnoentities('FullData')
319 .';'.$langs->transnoentities('Version') // Version Dolibarr, example 22.0.0
320 .';'.$langs->transnoentities('VersionSignature') // Rule used for fingerprint calculation
321 .';'.$langs->transnoentities('FingerprintDatabase') // Signature
322 .';'.$langs->transnoentities('Status')
323 //.';'.$langs->transnoentities('FingerprintExport')
324 );
325
326 $loweridinerror = 0;
327 $lastrowid = 0;
328 $i = 0;
329
330 while ($obj = $db->fetch_object($resql)) {
331 // We set here all data used into signature calculation (see checkSignature method) and more
332
333 // IMPORTANT: We must have here, the same rule for transformation of data than into
334 // the blockedlog->fetch() method (db->jdate for date, ...)
335
336 $block_static->id = $obj->rowid;
337 $block_static->entity = $obj->entity;
338
339 if ($i == 0) {
340 $tmparray = $block_static->getPreviousHash(0, $block_static->id);
341 $previoushash = $tmparray['previoushash'];
342
343 fwrite($fh, ";NOTE - previoushash=".$previoushash."\n");
344 }
345
346 $tz = 'gmt';
347 if (empty($obj->object_format) || $obj->object_format == 'V1') {
348 $tz = 'tzserver';
349 }
350
351 $block_static->date_creation = $db->jdate($obj->date_creation, $tz); // jdate(date_creation) is UTC
352 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
353 $block_static->date_modification = $db->jdate($obj->tms, $tz); // jdate(tms) is UTC
354
355 $block_static->action = $obj->action;
356 $block_static->module_source = $obj->module_source;
357 $block_static->pos_source = $obj->pos_source;
358
359 $block_static->amounts_taxexcl = is_null($obj->amounts_taxexcl) ? null : (float) $obj->amounts_taxexcl; // Database store value with 8 digits, we cut ending 0 them with (flow)
360 $block_static->amounts = (float) $obj->amounts; // Database store value with 8 digits, we cut ending 0 them with (flow)
361
362 $block_static->fk_object = $obj->fk_object; // Not in signature
363 $block_static->date_object = $db->jdate($obj->date_object, $tz); // jdate(date_object) is UTC
364 $block_static->ref_object = $obj->ref_object;
365
366 $block_static->linktoref = $obj->linktoref;
367 $block_static->linktype = $obj->linktype;
368
369 $block_static->fk_user = $obj->fk_user; // Not in signature
370 $block_static->user_fullname = $obj->user_fullname;
371
372 $block_static->object_data = $block_static->dolDecodeBlockedData($obj->object_data);
373
374 $block_static->note = $obj->note;
375
376 // Old hash + Previous fields concatenated = signature
377 $block_static->signature = $obj->signature;
378
379 $block_static->element = $obj->element; // Not in signature
380
381 $block_static->object_version = $obj->object_version; // Not in signature
382 $block_static->object_format = $obj->object_format; // Not in signature
383
384 $block_static->certified = ($obj->certified == 1); // Not in signature
385
386 //$block_static->debuginfo = $obj->debuginfo;
387
388 //var_dump($block->id.' '.$block->signature, $block->object_data);
389
390 $checksignature = $block_static->checkSignature($previoushash); // If $previoushash is not defined, checkSignature will search it from $block_static->id
391
392 // To see detail to get signature
393 /*
394 if ($block_static->id == 411) {
395 $concatenateddata = $block_static->buildKeyForSignature($block_static->object_format);
396
397 $tmparray = $block_static->checkSignature($previoushash, 2);
398 var_dump($previoushash, $concatenateddata, $tmparray);
399 exit;
400 }
401 */
402
403 if ($checksignature) {
404 if (!empty($block_static->note)) { // signature ok but end of chain deletion detected
405 $statusofrecord = 'KO';
406 $statusofrecordnote = $block_static->note;
407 } else {
408 $statusofrecord = $langs->transnoentitiesnoconv('StatusValid');
409 }
410 if ($loweridinerror > 0) {
411 $statusofrecordnote = 'ValidButFoundAPreviousKO';
412 } else {
413 $statusofrecordnote = '';
414 }
415 } else {
416 $statusofrecord = 'KO';
417 $statusofrecordnote = 'LineCorruptedOrNotMatchingPreviousOne';
418 $loweridinerror = $obj->rowid;
419 }
420
421 if ($i == 0) {
422 $statusofrecordnote = $langs->trans("PreviousFingerprint").': '.$previoushash.($statusofrecordnote ? ' - '.$statusofrecordnote : '');
423 }
424
425 //$concatenateddata = $block_static->buildKeyForSignature();
426
427 $lastrowid = $block_static->id;
428
429 // Define $totalhtamount, $totalvatamount, $totalamount for $block->action event / $block->module_source
430 $total_ht = $total_vat = $total_ttc = 0;
431 sumAmountsForUnalterableEvent($block_static, $refinvoicefound, $totalhtamount, $totalvatamount, $totalamount, $total_ht, $total_vat, $total_ttc);
432
433 fwrite($fh, ";"
434 .csvClean($block_static->id).';'
435 .csvClean(dol_print_date($block_static->date_creation, 'standard', 'gmt')).';'
436 .csvClean($block_static->action).';'
437 .csvClean($block_static->module_source).';'
438 .csvClean($block_static->pos_source).';'
439 .csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
440 .csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
441 .csvClean($block_static->ref_object).';'
442 .csvClean(dol_print_date($block_static->date_object, 'standard', 'gmt')).';'
443 .csvClean($block_static->user_fullname).';'
444 .csvClean($block_static->linktoref).';'
445 .csvClean($block_static->linktype).';'
446 .csvClean($obj->object_data).';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
447 .csvClean($block_static->object_version).';'
448 .csvClean($block_static->object_format).';'
449 .csvClean($block_static->signature).';'
450 .csvClean($statusofrecord).';'
451 .csvClean($block_static->note).';'
452 ."\n");
453
454 // Set new previous hash for next fetch
455 $previoushash = $obj->signature;
456
457 $i++;
458 } // end of loop on each record found
459
460 if ($i == 0) {
461 fwrite($fh, ";\n");
462 }
463
464 // Get the last record of the chain (may be used later).
465 $lastrecord = $block_static->getLastRecord();
466
467 // Check if $lastrowid is the last rowid of table blockedlog
468 $isLastRecord = false;
469 if ($lastrecord['id'] == $lastrowid) {
470 $isLastRecord = true;
471 }
472
473 if (!$periodnotcomplete) { // If period is complete
474 if (!$isLastRecord) { // There is another record in database that follow the last one in period. So we use it to see if no deletion were done at end of period.
475 // We check that next record after the last one is ok.
476 $block_static_after = new BlockedLog($db);
477 $nextrecord = $block_static_after->getNextRecord($lastrowid);
478 $nextrecordid = $nextrecord['id'];
479
480 $block_static_after->fetch($nextrecordid);
481
482 $checksignature = $block_static_after->checkSignature();
483
484 if (!$checksignature) { // If the record just after is not valid, it means we removed one or more records inside the chain (at end of period)
485 fwrite($fh, 'ERROR '.$langs->trans("ErrorEndOfChainRecordWasRemoved", str_replace(array('T', 'Z'), ' ', dol_print_date($block_static->date_creation, 'dayhourrfc', 'gmt')), str_replace(array('T', 'Z'), ' ', dol_print_date($block_static_after->date_creation, 'dayhourrfc', 'gmt')))."\n");
486 }
487 } else { // If last output record is the last one in chain, we must use the end of chain protection file to check that no deletion were done in database before export.
488 $lockfile = $block_static->getEndOfChainFlagFile();
489 $lockline = '';
490
491 // Note: We can find a similar code into the blockedlog_list.php page to make the report on screen.
492 if (defined('BLOCKEDLOG_END_FLAG_IN_A_FILE')) {
493 if (!file_exists($lockfile)) {
494 //$error++;
495
496 if ($mysoc->country_code == 'FR') {
497 fwrite($fh, 'ERROR '.$langs->trans("ErrorEndOfChainFlagWasRemoved")."\n");
498 } else {
499 fwrite($fh, 'WARNING '.$langs->trans("WarningNoProtectionOnEndOfChain")."\n");
500 }
501 } else {
502 $lockline = trim(file_get_contents($lockfile));
503 }
504 } else {
505 $sql = "SELECT value from ".MAIN_DB_PREFIX."const";
506 $sql .= " WHERE name = '".$db->escape(basename($lockfile))."' AND entity = ".((int) $conf->entity);
507 $resql = $db->query($sql);
508 if ($resql) {
509 $obj = $db->fetch_object($resql);
510 if ($obj) {
511 $lockline = $obj->value;
512 } else {
513 //$error++;
514
515 if ($mysoc->country_code == 'FR') {
516 fwrite($fh, 'ERROR '.$langs->trans("ErrorEndOfChainFlagWasRemoved")."\n");
517 } else {
518 fwrite($fh, 'WARNING '.$langs->trans("WarningNoProtectionOnEndOfChain")."\n");
519 }
520 }
521 }
522 }
523
524 if (! $error) {
525 $headstring = '';
526 if (preg_match('/^dolcrypt/', $lockline)) {
527 $headstring = dolDecrypt($lockline, '', 'BLOCKEDLOGHEAD');
528 } elseif (preg_match('/^dolobfuscation/', $lockline)) {
529 try {
530 $remoteobfuscationkey = $block_static->getObfuscationKey();
531 if (empty($remoteobfuscationkey)) {
532 throw new Exception('Remote obfuscation key is empty');
533 }
534 } catch (Exception $e) {
535 $error++;
536
537 $url_for_ping = getDolGlobalString('MAIN_URL_FOR_PING', "https://ping.dolibarr.org/");
538 setEventMessages($langs->trans("FailedToGetRemoteObfuscationKeyReTryLater", $url_for_ping), null, 'errors');
539 setEventMessages($langs->trans("CantValidateEndOfChain"), null, 'errors');
540 }
541 $headstring = dolDecrypt($lockline, $remoteobfuscationkey, 'BLOCKEDLOGHEAD');
542 }
543
544 $reg = array();
545 if (preg_match('/^BLOCKEDLOGHEAD (\d+) ([^\s]+) ([a-zA-Z0-9\-]+)/', (string) $headstring, $reg)) { // Failed to decypt the head
546 // Compare with last line
547 $lastrecordid = $lastrecord['id'];
548 $lastrecorddate = $lastrecord['date'];
549 $lastrecordsignature = $lastrecord['signature'];
550
551 if ($reg[1] > $lastrecordid || $reg[3] != $lastrecordsignature) {
552 //$error++;
553
554 // Check that last line is the one declared into the head flag. If not, it means some record were deleted at end of chain.
555 fwrite($fh, $langs->trans("ErrorEndOfChainRecordWasRemoved", str_replace(array('T', 'Z'), ' ', dol_print_date($lastrecorddate, 'dayhourrfc', 'gmt')), str_replace(array('T', 'Z'), ' ', $reg[2]))."\n");
556 }
557 } else {
558 //$error++;
559
560 fwrite($fh, $langs->trans("FailedToDecodeTheHeadFlagEndOfChainIsNotReliable")."\n");
561 }
562 }
563 }
564 }
565 } else {
566 $error++;
567 setEventMessages($db->lasterror, null, 'errors');
568 }
569
570
571 // Define which source we want to show
572 $showtotalfor = array('' => 1);
573 $sql = "SELECT DISTINCT module_source FROM ".MAIN_DB_PREFIX."blockedlog WHERE entity = ".((int) $conf->entity);
574 $resql = $db->query($sql);
575 if ($resql) {
576 while ($obj = $db->fetch_object($resql)) {
577 $showtotalfor[$obj->module_source] = 1;
578 }
579 }
580 if (isModEnabled('takepos')) {
581 $showtotalfor['takepos'] = 1;
582 }
583
584 ksort($totalamount);
585 krsort($showtotalfor);
586
587 // Now calculate cumulative total of all invoices validated
588 foreach ($totalamount as $key => $totalamountofcodepersource) {
589 foreach ($totalamountofcodepersource as $source => $tmpval) {
590 $totalhtamountalllines = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER' => array());
591 $totalvatamountalllines = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER' => array());
592 $totalamountalllines = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER' => array());
593 }
594 }
595 //var_dump($totalamount, $totalhtamount, $totalvatamount); exit;
596
597 $countsource = 0;
598 foreach ($showtotalfor as $source => $tmpval) {
599 $countsource++;
600
601 // Line of title for total for period for $source
602 if ($countsource == 1) {
603 fwrite($fh, "\n");
604 }
605 fwrite($fh, '----- ');
606 fwrite($fh, $langs->transnoentitiesnoconv("TotalForThePeriod"));
607 fwrite($fh, ' - '.($source ? $langs->transnoentitiesnoconv("PointOfSale").' '.ucfirst($source) : $langs->transnoentitiesnoconv("BackOffice")));
608 fwrite($fh, ' ('.$yearmonthtoexport.($periodnotcomplete ? '-'.$suffixperiod : '').')');
609 fwrite($fh, ' -----');
610 fwrite($fh, "\n");
611
612 foreach ($totalamount as $actioncode => $totalamountofcodepersource) {
613 $amountstoshow = '';
614 $s = $actioncode;
615 if ($actioncode == 'BILL_VALIDATE') {
616 $s = 'BILLED = '.$langs->transnoentitiesnoconv("Turnover");
617 $amountstoshow = (float) $totalhtamount['BILL_VALIDATE'][$source].' '.$langs->transnoentitiesnoconv("HT").' - '.(float) $totalvatamount['BILL_VALIDATE'][$source].' '.$langs->transnoentitiesnoconv("VAT").' - '.(float) $totalamount['BILL_VALIDATE'][$source].' '.$langs->transnoentitiesnoconv("TTC");
618 } elseif ($actioncode == 'PAYMENT_CUSTOMER') {
619 $s = 'PAID = '.$langs->transnoentitiesnoconv("TurnoverCollected");
620 $amountstoshow = (float) $totalamount['PAYMENT_CUSTOMER'][$source];
621 }
622
623 fwrite($fh, 'SUMMARY PERIOD '.$s.' = '.$amountstoshow."\n");
624 }
625 }
626
627
628 // Get lifetime amount of all invoices validated and payments created/deleted.
629 // We do not use $totalamountalllines because it is only for the period, but we want lifetime amount since the first record to now.
630
631 $totalamountlifetime = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER_CREATE' => array(), 'PAYMENT_CUSTOMER_DELETE' => array());
632 $totalhtamountlifetime = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER_CREATE' => array(), 'PAYMENT_CUSTOMER_DELETE' => array());
633
634 $foundoldformat = 0;
635 $firstrecorddate = 0;
636 global $foundoldformat, $firstrecorddate;
637 include DOL_DOCUMENT_ROOT.'/blockedlog/admin/lifetimeamount.inc.php';
638 '@phan-var-force array<string,array<string,float>> $totalamountlifetime';
639 '@phan-var-force array<string,array<string,float>> $totalhtamountlifetime';
640
641 $countsource = 0;
642 foreach ($showtotalfor as $source => $tmpval) {
643 $countsource++;
644
645 // Line of title for lifetime total for $source
646 if ($countsource == 1) {
647 fwrite($fh, "\n");
648 }
649 fwrite($fh, '----- ');
650 fwrite($fh, $langs->transnoentitiesnoconv("TotalForLifetime"));
651 fwrite($fh, ' - '.($source ? $langs->transnoentitiesnoconv("PointOfSale").' '.ucfirst($source) : $langs->transnoentitiesnoconv("BackOffice")));
652 fwrite($fh, ' (>='.dol_print_date($firstrecorddate, 'standard').')');
653 fwrite($fh, "\n");
654
655 foreach ($totalamount as $actioncode => $totalamountofcodepersource) {
656 $amountstoshow = '';
657 $s = '';
658 if ($actioncode == 'BILL_VALIDATE') {
659 $s = 'BILLED = '.$langs->transnoentitiesnoconv("Turnover");
660 $amountstoshow = $totalhtamountlifetime['BILL_VALIDATE'][$source].' '.$langs->transnoentitiesnoconv("HT")." - ".($foundoldformat ? '' : ((float) $totalamountlifetime['BILL_VALIDATE'][$source] - (float) $totalhtamountlifetime['BILL_VALIDATE'][$source]).' '.$langs->transnoentitiesnoconv("VAT")).' - '.$totalamountlifetime['BILL_VALIDATE'][$source].' '.$langs->transnoentitiesnoconv("TTC");
661 } elseif ($actioncode == 'PAYMENT_CUSTOMER') {
662 $s = 'PAID = '.$langs->transnoentitiesnoconv("TurnoverCollected");
663 $amountstoshow = ((float) $totalamountlifetime['PAYMENT_CUSTOMER_CREATE'][$source] + (float) $totalamountlifetime['PAYMENT_CUSTOMER_DELETE'][$source]);
664 }
665
666 // Add a final line with perpetual total for invoice validations
667 fwrite($fh, 'SUMMARY LIFETIME '.$s.' = '.$amountstoshow."\n");
668 }
669 }
670
671 fwrite($fh, "\n");
672
673 // End of file, we will calculate global signature on it now, before adding last line.
674 fclose($fh);
675
676 // Calculate the signature of the file (the last line has a return line)
677 $algo = 'sha256';
678 $sha256 = hash_file($algo, $tmpfile); // For integrity only
679 $hmacsha256 = hash_hmac_file($algo, $tmpfile, $secretkey); // For integrity + authenticity
680
681 // Now add a signature to check integrity at end of file
682 file_put_contents($tmpfile, 'END - sha256='.$sha256.' - hmac_sha256='.$hmacsha256, FILE_APPEND);
683 dolChmod($tmpfile);
684
685
686 if (!$error) {
687 if ($periodnotcomplete) {
688 setEventMessages($langs->trans("ErrorPeriodMustBePastToAllowExport"), null, "warnings");
689 } else {
690 // We record the export as a new line into the unalterable logs
691 require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
692 $b = new BlockedLog($db);
693
694 $object = new stdClass();
695 $object->id = 0;
696 $object->element = 'module';
697 $object->ref = 'systemevent';
698 $object->entity = $conf->entity;
699 $object->date = dol_now();
700 $object->fullname = $user->getFullName($langs);
701
702 $object->label = 'Export unalterable logs';
703
704 /*
705 $object->total_billed = $totalhtamountalllines['BILL_VALIDATE'].' '.$langs->trans("HT").' - '.$totalvatamountalllines['BILL_VALIDATE'].' '.$langs->trans("VAT").' - '.$totalamountalllines['BILL_VALIDATE'].' '.$langs->trans("HT");
706 $object->total_collected = $totalamountalllines['PAYMENT_CUSTOMER'];
707 $object->totallifetime_billed = $totalhtamountlifetime['BILL_VALIDATE'].' '.$langs->trans("HT")." - ".($foundoldformat ? '' : ($totalamountlifetime['BILL_VALIDATE'] - $totalhtamountlifetime['BILL_VALIDATE']).' '.$langs->transnoentitiesnoconv("VAT")).' - '.$totalamountlifetime['BILL_VALIDATE'].' '.$langs->trans("HT");
708 $object->totallifetime_collected = ($totalamountlifetime['PAYMENT_CUSTOMER_CREATE'] + $totalamountlifetime['PAYMENT_CUSTOMER_DELETE']);
709 */
710 // TODO: Add total_billed, total_collected, totallifetime_billed, totallifetime_collected
711
712
713 $object->period = 'year='.GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') ? ' month='.GETPOSTINT('monthtoexport') : '');
714
715 // There is no trigger for export of archive files, so we force the action code here
716
717 $action = 'BLOCKEDLOG_EXPORT';
718
719 $result = $b->setObjectData($object, $action, 0, $user, 0);
720
721 if ($result < 0) {
722 setEventMessages('Failed to insert the export into the unalterable log. Export canceled: '.$b->error, null, 'errors');
723 dol_delete_file($tmpfile);
724 $error++;
725 }
726
727 $res = $b->create($user);
728
729 if ($res < 0) {
730 setEventMessages('Failed to insert the export into the unalterable log. Export canceled: '.$b->error, null, 'errors');
731 dol_delete_file($tmpfile);
732 $error++;
733 }
734 }
735 } else {
736 dol_delete_file($tmpfile);
737 }
738 }
739
740 if (!$error && $fh) {
741 setEventMessages($langs->trans("FileGenerated"), null);
742 }
743
744 $action = '';
745}
746
747
748/*
749 * View
750 */
751
752$form = new Form($db);
753$formother = new FormOther($db);
754
755if ($withtab && !userIsTaxAuditor()) {
756 $title = $langs->trans("ModuleSetup").' '.$langs->trans('BlockedLog');
757} else {
758 $title = $langs->trans("BrowseBlockedLog");
759}
760$help_url = "EN:Module_Unalterable_Archives_-_Logs|FR:Module_Archives_-_Logs_Inaltérable";
761
762llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'bodyforlist mod-blockedlog page-admin_blockedlog_list');
763
764$blocks = $block_static->getLog('all', (int) $search_id, $MAXLINES, $sortfield, $sortorder, (int) $search_fk_user, $search_start, $search_end, $search_ref, $search_amount, $search_code, $search_signature);
765if (!is_array($blocks)) {
766 if ($blocks == -2) {
767 setEventMessages($langs->trans("TooManyRecordToScanRestrictFilters", $MAXLINES), null, 'errors');
768 } else {
769 dol_print_error($block_static->db, $block_static->error, $block_static->errors);
770 exit;
771 }
772}
773
774$linkback = '';
775if ($withtab && !userIsTaxAuditor()) {
776 $linkback = '<a href="'.dolBuildUrl($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php', ['restore_lastsearch_values' => 1]).'">'.img_picto($langs->trans("BackToModuleList"), 'back', 'class="pictofixedwidth"').'<span class="hideonsmartphone">'.$langs->trans("BackToModuleList").'</span></a>';
777}
778
779$morehtmlcenter = '';
780$texttop = '';
781
782$registrationnumber = getHashUniqueIdOfRegistration();
783if (!userIsTaxAuditor()) {
784 $texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
786 $texttop = '';
787 }
788}
789
790print load_fiche_titre($title.'<br>'.$texttop, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
791
792$head = blockedlogadmin_prepare_head($withtab);
793
794print dol_get_fiche_head($head, 'archives', '', -1);
795
796//print $texttop;
797//print '<br><br>';
798
799print '<div class="opacitymedium hideonsmartphone justify">';
800if (!userIsTaxAuditor()) {
801 print $langs->trans("ArchivesDesc")."<br>";
802} else {
803 print $langs->trans("ArchivesAuditorDesc")."<br>";
804}
805print "</div>\n";
806
807
808if ($action == 'check' || $action == 'checkconfirmed') {
809 print '<br>';
810
811 print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
812 print '<input type="hidden" name="urlfile" value="'.GETPOST('urlfile').'">';
813 print '<input type="hidden" name="action" value="checkconfirmed">';
814 print '<input type="hidden" name="token" value="'.newToken().'">';
815
816 print '<div class="neutral">';
817
818 print '<b>'.$langs->trans("File").'</b> : '.GETPOST('urlfile').'<br>';
819
820 $fullpath = $upload_dir.'/'.GETPOST('urlfile');
821
822 $handle = fopen($fullpath, "r");
823 $line = fgets($handle);
824 fclose($handle);
825
826 $reg = array();
827 $period = '';
828 $regnumber = '';
829 $formatexport = '';
830 $fileentity = 1;
831 if (preg_match('/\speriod=([^\s]+)/', $line, $reg)) {
832 $period = $reg[1]; // Get period on first line
833 }
834 if (preg_match('/\sregnumber=([^\s]+)/', $line, $reg)) {
835 $regnumber = str_replace(array('.', '…'), '', $reg[1]); // Get period on first line
836 }
837 if (preg_match('/\sformatexport=([^\s]+)/', $line, $reg)) {
838 $formatexport = $reg[1]; // Get export format (VE1, VE2...)
839 }
840 if (preg_match('/\sentity=([^\s]+)/', $line, $reg)) {
841 $fileentity = $reg[1]; // Get entity of file (usually 1)
842 }
843 print '<b>'.$langs->trans("Period").'</b> : '.$period.'<br>';
844
845 print '<br>';
846
847
848 $registrationnumber = getHashUniqueIdOfRegistration();
849 $secretkey = $registrationnumber;
850 $inputregistrationnumber = '';
851
852 // Prepare to create a temporary file
853 $fullpathtmp = $upload_dir.'/temp/'.GETPOST('urlfile').'.tmp';
854
855 dol_mkdir($upload_dir.'/temp');
856 $result = dol_copy($fullpath, $fullpathtmp);
857
858 // Remove the last line of text file
859 removeLastLine($fullpathtmp);
860
861 print $langs->trans("FileHasBeenEncodedWithASecretKeyStartingWith").' : '.$regnumber.'...<br>';
862 if (preg_match('/^'.$regnumber.'/', $secretkey) && !userIsTaxAuditor()) {
863 print 'As this matches the 10 first characters of the full registration number of this instance, we will use the full registration number to control the archive file...';
864 } else {
865 $secretkey = ''; // The local registration number does not match the one of the archive file, so we won't use the local number to check authenticity.
866
867 if (GETPOST('inputregistrationnumber')) {
868 $inputregistrationnumber = GETPOST('inputregistrationnumber');
869 print $langs->trans("WeWillUseThisValueAsNumber");
870 print '<input type="text" name="inputregistrationnumber" class="width300" placeholder="'.$langs->trans("FullRegistrationNumber").'" value="'.$inputregistrationnumber.'" spellcheck="false">';
871
872 $secretkey = $inputregistrationnumber; // We will use the entered value to check authenticity
873 } else {
874 if (!userIsTaxAuditor() || !isModEnabled('captureserver')) { // @phan-suppress-current-line UnknownModuleName
875 print 'This archive file was not generated by this instance.';
876 print 'The control of authenticity is possible only if you know the full registration number. ';
877 $inputregistrationnumber = '';
878
879 print $langs->trans("PleaseEnterFullRegistrationNumber").' ';
880 } else {
881 // Here module "captureserver" is on. We can search the full registration number and prefill value
882 $sql = "SELECT registerid from ".MAIN_DB_PREFIX."captureserver_captureserver";
883 $sql .= " WHERE registerid LIKE '".$db->escape($regnumber)."%'";
884 $sql .= " AND type = 'dolibarrregistration'";
885 $sql .= " LIMIT 1";
886
887 $resql = $db->query($sql);
888 if ($resql) {
889 $obj = $db->fetch_object($resql);
890 if ($obj) {
891 $inputregistrationnumber = $obj->registerid;
892 }
893 }
894
895 if ($inputregistrationnumber) {
896 print $langs->trans("WeFoundThisFullRegistrationNumberForThisKey").' ';
897 } else {
898 print $langs->trans("WeDidNotFindThisFullRegistrationNumberForThisKey").'.<br>';
899 print $langs->trans("PleaseEnterFullRegistrationNumber").' ';
900 }
901 }
902 print '<input type="text" name="inputregistrationnumber" class="width300" placeholder="'.$langs->trans("FullRegistrationNumber").'" value="'.$inputregistrationnumber.'" spellcheck="false">';
903 }
904 }
905 print '<br><br>';
906
907 print '<center>';
908 print '<input type="submit" class="button small" name="submit" value="'.$langs->trans("ControlFile").'">';
909 print '</center>';
910
911 print '</div>';
912
913 print '</form>';
914
915
916 if ($action == 'checkconfirmed') {
917 $totalhtamountforaction = $totalvatamountforaction = $totalamountforaction = array(
918 'BILL_VALIDATE' => 0,
919 'PAYMENT_CUSTOMER' => 0 // PAYMENT_CUSTOMER_CREATE + PAYMENT_CUSTOMER_DELETE
920 );
921 $reg = array();
922 $refinvoicefound = array();
923 $recalculatedhashsign = '';
924 $recalculatedhashauth = '';
925 $hashsign = '';
926 $hashauth = '';
927 $algosign = '';
928 $algoauth = '';
929 $previoushash = '';
930 $nbLinesModifiedInExportButKo = 0;
931 $nbLinesModifiedBeforeExport = 0;
932
933 $amounthtlifetime = array();
934 $amountvatlifetime = array();
935 $amountttclifetime = array();
936 $amounthtlifetime['BILL_VALIDATE'] = $amounthtlifetime['PAYMENT_CUSTOMER'] = null;
937 $amountvatlifetime['BILL_VALIDATE'] = $amountvatlifetime['PAYMENT_CUSTOMER'] = null;
938 $amountttclifetime['BILL_VALIDATE'] = $amountttclifetime['PAYMENT_CUSTOMER'] = null;
939
940 $footer = '';
941 $errorlines = '';
942
943 $handle = fopen($fullpath, "r");
944 if ($handle) {
945 $numline = 0;
946
947 $block_static = new BlockedLog($db);
948
949 while ($line = fgetcsv($handle, 100000, ';', '"', '')) {
950 $numline++;
951 $lineanalyzed = 0;
952
953 $linetech = $lineactioncode = '';
954 $lineamountht = $lineamountttc = 0;
955 $lineref = '';
956 $statusline = '';
957
958 if ($numline < 2) {
959 // First line, we continue
960 continue;
961 }
962
963 $block_static->id = 0; // reset tmp record
964
965 // Test if line is an empty line
966 if (trim((string) $line[0]) == '' && trim((string) $line[1]) == '') {
967 $lineanalyzed = 2;
968 }
969
970 // Test if line is an comment line
971 if (preg_match('/^-----/', (string) $line[0])) {
972 $lineanalyzed = 2;
973 }
974
975 // Test if line is an error alert line
976 if (preg_match('/^ERROR/', (string) $line[0])) {
977 $lineanalyzed = 3;
978 }
979
980 if (empty($lineanalyzed) && $formatexport == 'VE1' && !empty($line[1])) { // Format Blockedlog V1-
981 $lineanalyzed = 1;
982 $linetech = $line[0];
983
984 $block_static->id = (int) $line[1];
985 $block_static->entity = (int) $fileentity;
986 $block_static->date_creation = (string) $line[2];
987 $block_static->action = $lineactioncode = (string) $line[3];
988 $block_static->module_source = (string) $line[4];
989 $block_static->pos_source = '';
990 $block_static->amounts_taxexcl = $lineamountht = ($line[5] === '' ? null : (float) $line[5]);
991 $block_static->amounts = $lineamountttc = (float) $line[6];
992 $block_static->ref_object = $lineref = (string) $line[7];
993 $block_static->date_object = (int) $line[8];
994 $block_static->user_fullname = (string) $line[9];
995 $block_static->linktoref = (string) $line[10];
996 $block_static->linktype = (string) $line[11];
997 $block_static->object_data = json_decode((string) $line[12]);
998 $block_static->object_version = (string) $line[13];
999 $block_static->object_format = (string) $line[14];
1000 $block_static->signature = (string) $line[15];
1001
1002 // Status from file: 'Valid' or 'KO'
1003 $statusline = (string) $line[16];
1004 }
1005
1006 if (empty($lineanalyzed) && $formatexport == 'VE2' && !empty($line[1])) { // Format Blockedlog V2+
1007 $lineanalyzed = 1;
1008 $linetech = $line[0];
1009
1010 $block_static->id = (int) $line[1];
1011 $block_static->entity = (int) $fileentity;
1012 $block_static->date_creation = (string) $line[2];
1013 $block_static->action = $lineactioncode = (string) $line[3];
1014 $block_static->module_source = (string) $line[4];
1015 $block_static->pos_source = (string) $line[5];
1016 $block_static->amounts_taxexcl = $lineamountht = ($line[6] === '' ? null : (float) $line[6]);
1017 $block_static->amounts = $lineamountttc = (float) $line[7];
1018 $block_static->ref_object = $lineref = (string) $line[8];
1019 $block_static->date_object = (int) $line[9];
1020 $block_static->user_fullname = (string) $line[10];
1021 $block_static->linktoref = (string) $line[11];
1022 $block_static->linktype = (string) $line[12];
1023 $block_static->object_data = json_decode((string) $line[13]);
1024 $block_static->object_version = (string) $line[14];
1025 $block_static->object_format = (string) $line[15];
1026 $block_static->signature = (string) $line[16];
1027
1028 // Status from file: 'Valid' or 'KO'
1029 $statusline = (string) $line[17];
1030 }
1031
1032 if ($block_static->id > 0) {
1033 // Status revalidated from calculation using the HMAC secret key (possible only when we are on the same instance than
1034 // the one hosting the initial database of the archive)
1035 $tmp = $block_static->checkSignature($previoushash, 2);
1036
1037 // To see the detail of line to to test signature calculation
1038 /*
1039 if ($block_static->id == 411) {
1040 $concatenateddata = $block_static->buildKeyForSignature($block_static->object_format);
1041 if (empty($previoushash)) {
1042 $tmparray = $block_static->getPreviousHash(0, $block_static->id);
1043 $previoushash = $tmparray['previoushash'];
1044 }
1045 var_dump($block_static->id, $previoushash, $concatenateddata, $tmp);
1046 exit;
1047 }*/
1048
1049 $signature = $tmp['calculatedsignature'];
1050 $previoushash = $block_static->signature;
1051
1052 //print 'Line '.$numline.': Recalculate from file: '.$signature.', in file '.$block_static->signature."<br>\n";
1053 if ($statusline == 'Valid') {
1054 // The signature calculated must match the recorded signature
1055 if ($signature != $block_static->signature) {
1056 $nbLinesModifiedInExportButKo++;
1057 //print 'Error: Line '.$numline.' reports that signature is ok but it seems to not match the one recalculated.';
1058 }
1059 }
1060 if ($statusline == 'KO') {
1061 $nbLinesModifiedBeforeExport++;
1062 //print 'The line '.$numline.' was modified into the Unalterable Log before being exported.';
1063 }
1064
1065 // Note: this field is not more used, it has been replacedwith a global signature on all the file
1066 /*
1067 try {
1068 $concatenateddata = $block_static->buildKeyForSignature();
1069 $signatureexport = dol_hash($previoushashexport.$concatenateddata, 'sha256');
1070 $previoushashexport = $signatureexport;
1071 } catch(Exception $e) {
1072 setEventMessages('Bad format for line '.$numline.'. '.$e->getMessage(), null, 'errors');
1073 break;
1074 }
1075 */
1076 }
1077
1078 if ($lineanalyzed == 1 && ($lineactioncode == 'BILL_VALIDATE' || $lineactioncode == 'PAYMENT_CUSTOMER_CREATE' || $lineactioncode == 'PAYMENT_CUSTOMER_DELETE')) {
1079 // For action = BILL_VALIDATE, we keep only first invoice found, but this should not happen because edition of invoice is never possible on
1080 // certified version (locked) and very difficult on other version.
1081 if ($lineactioncode != 'BILL_VALIDATE' || empty($refinvoicefound[$lineref])) {
1082 if ($lineactioncode == 'PAYMENT_CUSTOMER_CREATE' || $lineactioncode == 'PAYMENT_CUSTOMER_DELETE') {
1083 $lineactioncode = 'PAYMENT_CUSTOMER';
1084 }
1085
1086 $totalhtamountforaction[$lineactioncode] += $lineamountht;
1087 $totalvatamountforaction[$lineactioncode] += ($lineamountttc - $lineamountht);
1088 $totalamountforaction[$lineactioncode] += $lineamountttc;
1089 }
1090 if ($lineactioncode == 'BILL_VALIDATE') {
1091 $refinvoicefound[$lineref] = 1;
1092 }
1093 }
1094
1095 // Test if line is a summary line
1096 if (preg_match('/^SUMMARY /', (string) $line[0])) {
1097 // We are on a line for summary information
1098 $lineanalyzed = 2;
1099 }
1100
1101 if ($lineanalyzed == 2) {
1102 // We are in the footer section with comments
1103 $footer .= (string) str_replace(array(';', '*'), '', implode(' ', $line))."\n";
1104 }
1105
1106 if ($lineanalyzed == 3) {
1107 // We are in the footer section with comments
1108 $errorlines .= (string) $line[0]."\n";
1109 }
1110
1111 if (preg_match('/END - ([a-z0-9_]+)=([a-z0-9]+) - ([a-z0-9_]+)=([a-z0-9]+)$/', (string) $line[0], $reg)) {
1112 $lineanalyzed = 1;
1113 $algosign=$reg[1];
1114 $hashsign=$reg[2];
1115 $algoauth=$reg[3];
1116 $hashauth=$reg[4];
1117
1118 if ($algosign == 'sha256') {
1119 $algo = 'sha256';
1120 $recalculatedhashsign = hash_file($algo, $fullpathtmp);
1121 }
1122 if ($algoauth == 'hmac_sha256') {
1123 $algo = 'sha256';
1124 $recalculatedhashauth = hash_hmac_file($algo, $fullpathtmp, $secretkey);
1125 }
1126 }
1127
1128 if (!$lineanalyzed) {
1129 print 'Line '.$numline.' has format '.$formatexport.' that is not supported';
1130 }
1131 }
1132 fclose($handle);
1133 } else {
1134 setEventMessages('Failed to open file '.GETPOST('urlfile'), null, 'errors');
1135 }
1136
1137 print '<br><br>';
1138
1139 if ($recalculatedhashsign && hash_equals($recalculatedhashsign, $hashsign)) {
1140 print img_picto('', 'tick', 'class="valignmiddle pictofixedwidth"');
1141 print '<b>'.$langs->trans("FileIntegrity").'</b> ';
1142 print ' '.$form->textwithpicto('', $langs->trans("FileContentMatchSignature").'<br><br>'.$algosign.' = '.$recalculatedhashsign);
1143 } else {
1144 print img_picto('', 'cross', 'class="error valignmiddle pictofixedwidth"');
1145 print '<b>'.$langs->trans("FileIntegrity").'</b> ';
1146 print ' '.$form->textwithpicto('', $langs->trans("FileHasBeenCorrupted").'<br><br>Recalculated '.$recalculatedhashsign.' != Found in file '.$hashsign);
1147 }
1148 print '<br><br>';
1149
1150 if ($secretkey) {
1151 if ($recalculatedhashauth && hash_equals($recalculatedhashauth, $hashauth)) {
1152 print img_picto('', 'tick', 'class="valignmiddle pictofixedwidth"');
1153 print '<b>'.$langs->trans("FileAuthenticity").'</b> ';
1154 if (preg_match('/^'.$regnumber.'/', $secretkey) && !userIsTaxAuditor()) {
1155 print ' - <span class="opacitymedium">'.$langs->trans("FileWasGeneratedByThisInstance").'</span>';
1156 } else {
1157 print ' - <span class="opacitymedium">'.$langs->trans("FileWasGeneratedByTheInstanceWithRegistrationId").'</span>';
1158 }
1159 print ' '.$form->textwithpicto('', $langs->trans("FileContentMatchSignature").'<br><br>'.$algoauth.' = '.$recalculatedhashauth);
1160 } elseif ($recalculatedhashsign && hash_equals($recalculatedhashsign, $hashsign)) {
1161 print img_picto('', 'cross', 'class="error valignmiddle pictofixedwidth"');
1162 print '<b>'.$langs->trans("FileAuthenticity").'</b> ';
1163 print ' '.$form->textwithpicto('', $langs->trans("FileNotFromInstance").'<br><br>Recalculated '.$recalculatedhashauth.' != Found in file '.$hashauth);
1164 } else {
1165 print img_picto('', 'cross', 'class="error valignmiddle pictofixedwidth"');
1166 print '<b>'.$langs->trans("FileAuthenticity").'</b> ';
1167 print ' '.$form->textwithpicto('', $langs->trans("FileHasBeenCorruptedOrNotFromInstance").'<br><br>Recalculated '.$recalculatedhashauth.' != Found in file '.$hashauth);
1168 }
1169 } else {
1170 print img_picto('', 'cross', 'class="valignmiddle pictofixedwidth"');
1171 print '<b>'.$langs->trans("FileAuthenticity").'</b> ';
1172 print ' '.$form->textwithpicto('', $langs->trans("AuthenticityCantBeVerifiedIfFullRegistrationNumberNotProvided"));
1173 }
1174 print '<br><br>';
1175
1176 if ($nbLinesModifiedBeforeExport) {
1177 print img_picto('', 'warning', 'class="error valignmiddle pictofixedwidth"');
1178 print '<b>'.$langs->trans("nbLinesModifiedBeforeExport").'</b>';
1179 print '<br><br>';
1180 }
1181
1182 // Now print the error line section
1183 if ($errorlines) {
1184 print img_picto('', 'warning', 'class="error valignmiddle center pictofixedwidth"').'<b>'.$errorlines.'</b><br>';
1185
1186 print '<br>';
1187 }
1188
1189 // Now print the foot section
1190 print img_picto('', 'tick', 'class="valignmiddle center pictofixedwidth"');
1191 print '<b>'.$langs->trans("Summary").'</b>';
1192 print '<textarea class="centpercent" rows="14" spellcheck="false">';
1193 print dol_htmlcleanlastbr($footer);
1194 print '</textarea>';
1195
1196 print '<br>';
1197
1198 $text = $langs->trans("IfIntegrityAuthenticityIsOkYouCanGetdetailByOpeningTheFile");
1199 $text .= '<br>';
1200 $text .= $langs->trans("IfIntegrityAuthenticityIsOkYouCanGetdetailByOpeningTheFile2");
1201 //$langs->transnoentitiesnoconv("logBILL_VALIDATE"), $langs->transnoentitiesnoconv("logPAYMENT_CUSTOMER_CREATE"), $langs->transnoentitiesnoconv("logDOC_DOWNLOAD"), $langs->transnoentitiesnoconv("logCASHCONTROL_CLOSE")).'...');
1202 print info_admin($text);
1203 }
1204
1205
1206 print '<br><br>';
1207 print '<center><a href="'.$_SERVER["PHP_SELF"].'">'.$langs->trans("BackToList").'</a></center>';
1208}
1209
1210if ($action != 'check' && $action != 'checkconfirmed') {
1211 $htmltext = '';
1212
1213 if (!userIsTaxAuditor()) {
1214 $nbrecorddone = $block_static->countRecord();
1215 $mindisksize = 50; // Gb
1216 $maxtranspermonth = 10000;
1217 $nbrecordallowed = $mindisksize * 1024 * 1024 / 40 - $nbrecorddone;
1218 $nbmonthallowed = $nbrecordallowed / $maxtranspermonth;
1219
1220 $htmltext = '';
1221 $htmltext .= $langs->trans("UnalterableLogTool2", $langs->transnoentitiesnoconv("Archives"))."<br>";
1222 $htmltext .= '<span class="small">'.$langs->trans("UnalterableLogTool2MaxUsage", $nbrecorddone, $mindisksize, $nbrecordallowed)."</span><br>";
1223
1224 if ($mysoc->country_code == 'FR') {
1225 $htmltext .= '<br><span class="small">'.$langs->trans("UnalterableLogTool1FR", $langs->transnoentitiesnoconv("Archives")).'</span><br>';
1226 } else {
1227 $htmltext .= '<span class="small">'.$langs->trans("UnalterableLogTool2b", $langs->transnoentitiesnoconv("Archives"))."</span><br>";
1228 }
1229 //$htmltext .= $langs->trans("UnalterableLogTool1");
1230 //$htmltext .= $langs->trans("UnalterableLogTool3")."<br>";
1231
1232 print info_admin($htmltext, 0, 0, 'warning');
1233
1234 print '<br>';
1235 } else {
1236 print '<br>';
1237 }
1238
1239 $param = '';
1240 if ($contextpage != getDolDefaultContextPage(__FILE__)) {
1241 $param .= '&contextpage='.urlencode($contextpage);
1242 }
1243 if ($limit > 0 && $limit != $conf->liste_limit) {
1244 $param .= '&limit='.((int) $limit);
1245 }
1246 if ($search_id != '') {
1247 $param .= '&search_id='.urlencode($search_id);
1248 }
1249 if ($search_fk_user > 0) {
1250 $param .= '&search_fk_user='.urlencode($search_fk_user);
1251 }
1252 if ($search_startyear > 0) {
1253 $param .= '&search_startyear='.((int) $search_startyear);
1254 }
1255 if ($search_startmonth > 0) {
1256 $param .= '&search_startmonth='.((int) $search_startmonth);
1257 }
1258 if ($search_startday > 0) {
1259 $param .= '&search_startday='.((int) $search_startday);
1260 }
1261 if ($search_endyear > 0) {
1262 $param .= '&search_endyear='.((int) $search_endyear);
1263 }
1264 if ($search_endmonth > 0) {
1265 $param .= '&search_endmonth='.((int) $search_endmonth);
1266 }
1267 if ($search_endday > 0) {
1268 $param .= '&search_endday='.((int) $search_endday);
1269 }
1270 if ($search_amount) {
1271 $param .= '&search_amount='.urlencode($search_amount);
1272 }
1273 if ($search_signature) {
1274 $param .= '&search_signature='.urlencode($search_signature);
1275 }
1276 if ($search_showonlyerrors > 0) {
1277 $param .= '&search_showonlyerrors='.((int) $search_showonlyerrors);
1278 }
1279 if ($optioncss != '') {
1280 $param .= '&optioncss='.urlencode($optioncss);
1281 }
1282 if (GETPOST('withtab', 'alpha')) {
1283 $param .= '&withtab='.urlencode(GETPOST('withtab', 'alpha'));
1284 }
1285
1286 // Add $param from extra fields
1287 //include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
1288
1289 if ($action == 'deletefile') {
1290 $langs->load("companies"); // Need for string DeleteFile+ConfirmDeleteFiles
1291 print $form->formconfirm(
1292 $_SERVER["PHP_SELF"].'?urlfile='.urlencode(GETPOST("urlfile")).'&linkid='.GETPOSTINT('linkid').(empty($param) ? '' : $param),
1293 $langs->trans('DeleteFile'),
1294 $langs->trans('ConfirmDeleteFile'),
1295 'confirm_deletefile',
1296 '',
1297 '',
1298 1
1299 );
1300 }
1301
1302
1303 if (!userIsTaxAuditor()) {
1304 print '<form method="POST" id="exportArchives" action="'.$_SERVER["PHP_SELF"].'?output=file">';
1305 print '<input type="hidden" name="token" value="'.newToken().'">';
1306 print '<input type="hidden" name="action" value="export">';
1307
1308 print '<div class="right">';
1309
1310 print '<span class="hideonsmartphone">'.$langs->trans("RestrictYearToExport").': </span>';
1311 // Month
1312 print $formother->select_month((string) GETPOSTINT('monthtoexport'), 'monthtoexport', $langs->trans("Month"), 0, 'minwidth50 maxwidth75imp valignmiddle', true);
1313 print '<input type="text" name="yeartoexport" class="valignmiddle maxwidth75imp" value="'.GETPOST('yeartoexport').'" placeholder="'.$langs->trans("Year").'">';
1314
1315 print ' ';
1316
1317 // Disabled, we will use the getHashUniqueIdOfRegistration() as secret HMAC
1318 //print '<input type="text" name="hmacexportkey" class="valignmiddle minwidth150imp maxwidth300imp" required value="'.GETPOST('hmacexportkey').'" placeholder="'.$langs->trans("Password").'">';
1319
1320 print ' ';
1321
1322 print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
1323 print '<input type="submit" name="downloadcsv" class="button" value="'.$langs->trans('DownloadLogCSV').'">';
1324 /*if (getDolGlobalString('BLOCKEDLOG_USE_REMOTE_AUTHORITY')) {
1325 print ' | <a href="?action=downloadblockchain'.(GETPOST('withtab', 'alpha') ? '&withtab='.GETPOST('withtab', 'alpha') : '').'">'.$langs->trans('DownloadBlockChain').'</a>';
1326 }*/
1327 print ' </div><br>';
1328
1329 print '</form>';
1330 }
1331
1332 /*
1333 print '<form method="POST" id="searchFormList" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
1334
1335 if ($optioncss != '') {
1336 print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
1337 }
1338 print '<input type="hidden" name="token" value="'.newToken().'">';
1339 print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
1340 print '<input type="hidden" name="action" value="list">';
1341 print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
1342 print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
1343 print '<input type="hidden" name="page" value="'.$page.'">';
1344 print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
1345 print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
1346
1347 print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
1348 */
1349
1350 $filearray = dol_dir_list($upload_dir, 'files', 0, '', null, 'name', SORT_ASC, 1);
1351
1352 $modulepart = 'blockedlog';
1353 $relativepathwithnofile = 'archives/';
1354 $disablemove = 1;
1355 /*
1356 $param = '&id='.$object->id.'&entity='.(empty($object->entity) ? getDolEntity() : $object->entity);
1357 include DOL_DOCUMENT_ROOT.'/core/tpl/document_actions_post_headers.tpl.php';
1358 */
1359
1360 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
1361 $formfile = new FormFile($db);
1362
1363 $savingdocmask = '';
1364
1365 $object = $block_static;
1366
1367 // Get the form to add files (upload and links)
1368 $tmparray = $formfile->form_attach_new_file(
1369 $_SERVER["PHP_SELF"],
1370 '',
1371 0,
1372 0,
1373 $permission,
1374 $conf->browser->layout == 'phone' ? 40 : 60,
1375 $object,
1376 '',
1377 1,
1378 $savingdocmask,
1379 1,
1380 'formuserfile',
1381 '',
1382 '',
1383 0,
1384 0,
1385 0,
1386 2
1387 );
1388
1389 $formToUploadAFile = '';
1390
1391 if (is_array($tmparray) && !empty($tmparray)) {
1392 $formToUploadAFile = $tmparray['formToUploadAFile'];
1393 }
1394
1395 // List of document
1396 $formfile->list_of_documents(
1397 $filearray,
1398 null,
1399 $modulepart,
1400 $param,
1401 0,
1402 $relativepathwithnofile, // relative path with no file. For example "0/1"
1403 $permission,
1404 0,
1405 '',
1406 0,
1407 $langs->transnoentitiesnoconv('Archives'),
1408 '',
1409 0,
1410 $permtoedit,
1411 $upload_dir,
1412 $sortfield,
1413 $sortorder,
1414 $disablemove,
1415 0,
1416 -1,
1417 '',
1418 array('afteruploadtitle' => $formToUploadAFile, 'showhideaddbutton' => 1, 'hideshared' => 1, 'buttons' => array(0 => array('picto' => img_picto($langs->trans("ControlFile"), 'question'), 'url' => $_SERVER["PHP_SELF"].'?action=check&token=notrequired'.$param)))
1419 );
1420}
1421
1422
1423print dol_get_fiche_end();
1424
1425print '<br><br>';
1426
1427// End of page
1428llxFooter();
1429$db->close();
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
sumAmountsForUnalterableEvent($block, &$refinvoicefound, &$totalhtamount, &$totalvatamount, &$totalamount, &$total_ht, &$total_vat, &$total_ttc)
sumAmountsForUnalterableEvent
userIsTaxAuditor()
Call remote API service to push the last counter and signature.
blockedlogadmin_prepare_head($withtabsetup)
Define head array for tabs of blockedlog tools setup pages.
isRegistrationDataSavedAndPushed()
Return if the KYC mandatory parameters are set AND pushed/registered centralized server.
getHashUniqueIdOfRegistration($algo='sha256')
Return a hash unique identifier of the registration (used to identify the registration of instance wi...
isALNERunningVersion($blockedlogtestalreadydone=0, $blockedlogmodulealreadydone=0)
Return if the application is executed with the LNE requirements on.
Class to manage Blocked Log.
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
Class to help generate other html components Only common components are here.
getFullName($langs, $option=0, $nameorder=-1, $maxlen=0)
Return full name (civility+' '+name+' '+lastname)
global $mysoc
dol_get_first_day($year, $month=1, $gm=false)
Return GMT time for first day of a month or year.
Definition date.lib.php:604
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:126
dol_get_last_day($year, $month=12, $gm=false)
Return GMT time for last day of a month or year.
Definition date.lib.php:623
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_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
removeLastLine($fullpath)
Remove the last line of a text file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
csvClean($newvalue, $charset='', $separator='')
Clean a cell to respect rules of CSV file cells.
dol_now($mode='gmt')
Return date for now.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
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)
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='')
Show information in HTML for admin users or standard users.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dolChmod($filepath, $newmask='')
Change mod of a file.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getMultidirOutput($object, $module='', $forobject=0, $mode='output')
Return the full path of the directory where a module (or an object of a module) stores its files.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
getDolDefaultContextPage($s)
Return the default context page string.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
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_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.
dolDecrypt($chain, $key='', $patterntotest='')
Decode a string with a symmetric encryption.
isHTTPS()
Return if we are using a HTTPS connection Check HTTPS (no way to be modified by user but may be empty...