dolibarr 23.0.3
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-2025 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', 'other'));
50
51// Get Parameters
52$action = GETPOST('action', 'aZ09');
53//$confirm = GETPOST('confirm', 'aZ09');
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//$hmacexportkey = GETPOST('hmacexportkey', 'password');
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')) {
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
136$permission = $user->hasRight('blockedlog', 'read');
137$permissiontoadd = $user->hasRight('blockedlog', 'read'); // Permission is to upload new files to scan them
138$permtoedit = $permissiontoadd;
139
140$upload_dir = getMultidirOutput($block_static, 'blockedlog').'/archives';
141
142dol_mkdir($upload_dir);
143
144$fh = null;
145
146
147/*
148 * Actions
149 */
150
151// Purge search criteria
152if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
153 $search_id = '';
154 $search_fk_user = '';
155 $search_start = -1;
156 $search_end = -1;
157 $search_code = array();
158 $search_ref = '';
159 $search_amount = '';
160 $search_signature = '';
161 $search_showonlyerrors = 0;
162 $search_startyear = '';
163 $search_startmonth = '';
164 $search_startday = '';
165 $search_endyear = '';
166 $search_endmonth = '';
167 $search_endday = '';
168 $toselect = array();
169 $search_array_options = array();
170}
171
172include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php';
173
174if (GETPOST('action') == 'export' && $user->hasRight('blockedlog', 'read')) { // read is read/export for blockedlog
175 $error = 0;
176
177 $previoushash = '';
178 $firstid = '';
179 $periodnotcomplete = 0;
180
181 if (! (GETPOSTINT('yeartoexport') > 0)) {
182 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Year")), null, "errors");
183 $error++;
184 }
185 /*
186 if (empty($hmacexportkey)) {
187 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Password")), null, "errors");
188 $error++;
189 }
190 */
191
192 $dates = dol_get_first_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 1);
193 $datee = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 12);
194
195 if ($datee >= dol_now()) {
196 $periodnotcomplete = 1;
197 }
198
199 $suffixperiod = ($periodnotcomplete ? 'INCOMPLETE' : 'DONOTMODIFY');
200
201 if (!$error) {
202 // Get the ID of the first line qualified
203 $sql = "SELECT rowid";
204 $sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
205 $sql .= " WHERE entity = ".((int) $conf->entity);
206 // For unalterable log, we are using the date of creation of the log. Note that a bookkeeper may decide to dispatch an invoice
207 // on different periods for example to manage depreciation.
208 $sql .= " AND date_creation BETWEEN '".$db->idate($dates)."' AND '".$db->idate($datee)."'";
209 $sql .= " ORDER BY date_creation ASC, rowid ASC"; // Required so we get the first one
210 $sql .= $db->plimit(1);
211
212 $res = $db->query($sql);
213 if ($res) {
214 // Make the first fetch to get first line and then get the previous hash.
215 $obj = $db->fetch_object($res);
216 if ($obj) {
217 $firstid = $obj->rowid;
218 $previoushash = $block_static->getPreviousHash(0, $firstid);
219 } else { // If not data found for filter, we do not need previoushash neither firstid
220 $firstid = '';
221 $previoushash = 'nodata';
222 }
223 } else {
224 $error++;
225 setEventMessages($db->lasterror, null, 'errors');
226 }
227 }
228
229 // Define file name
230 $registrationnumber = getHashUniqueIdOfRegistration();
231 $secretkey = $registrationnumber;
232
233 $yearmonthtoexport = GETPOSTINT('yeartoexport').'-'.(GETPOSTINT('monthtoexport') > 0 ? sprintf("%02d", GETPOSTINT('monthtoexport')) : '');
234 $yearmonthdateofexport = dol_print_date(dol_now(), 'dayhourrfc', 'gmt');
235 $yearmonthdateofexportstandard = dol_print_date(dol_now(), 'dayhourlog', 'gmt');
236
237 $nameofdownoadedfile = "unalterable-log-archive-".$dolibarr_main_db_name."-".str_replace('-', '', $yearmonthtoexport).'-'.$yearmonthdateofexportstandard.'UTC-'.$suffixperiod.'.csv';
238
239 //$tmpfile = $conf->admin->dir_temp.'/unalterable-log-archive-tmp-'.$user->id.'.csv';
240 $tmpfile = getMultidirOutput($block_static, 'blockedlog').'/archives/'.$nameofdownoadedfile;
241
242 $formatexport = 'VE1';
243
244
245 // Init var for totals
246 /*
247 $totalhtamountalllines = array('BILL_VALIDATE' => 0, 'PAYMENT_CUSTOMER_CREATE' => 0);
248 $totalvatamountalllines = array('BILL_VALIDATE' => 0, 'PAYMENT_CUSTOMER_CREATE' => 0);
249 $totalamountalllines = array('BILL_VALIDATE' => 0, 'PAYMENT_CUSTOMER_CREATE' => 0);
250 $totalhtamountlifetime = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER_CREATE' => array());
251 $totalvatamountlifetime = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER_CREATE' => array());
252 $totalamountlifetime = array('BILL_VALIDATE' => array(), 'PAYMENT_CUSTOMER_CREATE' => array());
253 */
254
255 if (!$error) {
256 $fh = fopen($tmpfile, 'w');
257 }
258
259 if (!$error && $fh) {
260 // Now restart request with all data, so without the limit(1) in sql request
261 $sql = "SELECT rowid, date_creation, tms, user_fullname, action, amounts_taxexcl, amounts, element, fk_object, date_object, ref_object,";
262 $sql .= " signature, fk_user, object_data, object_version, object_format, debuginfo";
263 $sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
264 $sql .= " WHERE entity = ".((int) $conf->entity);
265 // For unalterable log, we are using the date of creation of the log. Note that a bookkeeper may decide to dispatch an invoice
266 // or payment on different periods for example to manage depreciation, but we want here is not accountancy but payment data.
267 $sql .= " AND date_creation BETWEEN '".$db->idate($dates)."' AND '".$db->idate($datee)."'";
268 $sql .= " ORDER BY date_creation ASC, rowid ASC"; // Required so later we can use the parameter $previoushash of checkSignature()
269
270 $resql = $db->query($sql);
271 if ($resql) {
272 // Print line with title
273 fwrite($fh, "BEGIN - date=".$yearmonthdateofexport." - period=".$yearmonthtoexport.($periodnotcomplete ? '-'.$suffixperiod : '')." - formatexport=".$formatexport." - user=".$user->getFullName($langs)
274 .';'.$langs->transnoentities('Id')
275 .';'.$langs->transnoentities('DateCreation')
276 .';'.$langs->transnoentities('Action')
277 .';'.$langs->transnoentities('Origin')
278 .';'.$langs->transnoentities('AmountHT')
279 .';'.$langs->transnoentities('AmountTTC')
280 .';'.$langs->transnoentities('Ref')
281 .';'.$langs->transnoentities('Date')
282 .';'.$langs->transnoentities('User')
283 .';'.$langs->transnoentities('LinkTo')
284 .';'.$langs->transnoentities('LinkType')
285 .';'.$langs->transnoentities('FullData')
286 .';'.$langs->transnoentities('Version') // Version Dolibarr, example 22.0.0
287 .';'.$langs->transnoentities('VersionSignature') // Rule used for fingerprint calculation
288 .';'.$langs->transnoentities('FingerprintDatabase') // Signature
289 .';'.$langs->transnoentities('Status')
290 .';'.$langs->transnoentities('FingerprintExport')
291 ."\n");
292
293 $loweridinerror = 0;
294 $i = 0;
295
296 $refinvoicefound = array();
297 $totalhtamount = array();
298 $totalvatamount = array();
299 $totalamount = array();
300
301 while ($obj = $db->fetch_object($resql)) {
302 // We set here all data used into signature calculation (see checkSignature method) and more
303 // IMPORTANT: We must have here, the same rule for transformation of data than into the fetch method (db->jdate for date, ...)
304 $block_static->id = $obj->rowid;
305 $block_static->entity = $obj->entity;
306
307 $block_static->date_creation = $db->jdate($obj->date_creation); // jdate(date_creation) is UTC
308
309 $block_static->module_source = $obj->module_source;
310
311 $block_static->amounts_taxexcl = (float) $obj->amounts_taxexcl; // Database store value with 8 digits, we cut ending 0 them with (flow)
312 $block_static->amounts = (float) $obj->amounts; // Database store value with 8 digits, we cut ending 0 them with (flow)
313
314 $block_static->action = $obj->action;
315 $block_static->date_object = $db->jdate($obj->date_object); // jdate(date_object) is UTC
316 $block_static->ref_object = $obj->ref_object;
317
318 $block_static->user_fullname = $obj->user_fullname;
319
320 $block_static->object_data = $block_static->dolDecodeBlockedData($obj->object_data);
321
322 // Old hash + Previous fields concatenated = signature
323 $block_static->signature = $obj->signature;
324
325 $block_static->element = $obj->element; // Not in signature
326 $block_static->fk_object = $obj->fk_object; // Not in signature
327
328 $block_static->fk_user = $obj->fk_user; // Not in signature
329
330 $block_static->date_modification = $db->jdate($obj->tms); // Not in signature
331 $block_static->object_version = $obj->object_version; // Not in signature
332 $block_static->object_format = $obj->object_format; // Not in signature
333
334 $block_static->certified = ($obj->certified == 1);
335
336 $block_static->linktoref = $obj->linktoref;
337 $block_static->linktype = $obj->linktype;
338
339 $block_static->debuginfo = $obj->debuginfo;
340
341 //var_dump($block->id.' '.$block->signature, $block->object_data);
342 $checksignature = $block_static->checkSignature($previoushash); // If $previoushash is not defined, checkSignature will search it
343
344 if ($checksignature) {
345 $statusofrecord = 'Valid';
346 if ($loweridinerror > 0) {
347 $statusofrecordnote = 'ValidButFoundAPreviousKO';
348 } else {
349 $statusofrecordnote = '';
350 }
351 } else {
352 $statusofrecord = 'KO';
353 $statusofrecordnote = 'LineCorruptedOrNotMatchingPreviousOne';
354 $loweridinerror = $obj->rowid;
355 }
356
357 if ($i == 0) {
358 $statusofrecordnote = $langs->trans("PreviousFingerprint").': '.$previoushash.($statusofrecordnote ? ' - '.$statusofrecordnote : '');
359 }
360
361 $concatenateddata = $block_static->buildKeyForSignature();
362
363 // Version archive V1=sha256
364 $signatureexport = dol_hash($previoushash.$concatenateddata, 'sha256'); // SHA256
365 //$signatureexporthmac = 'TODO';
366
367
368 // Define $totalhtamount, $totalvatamount, $totalamount for $block->action event code
369 $total_ht = $total_vat = $total_ttc = 0;
370 sumAmountsForUnalterableEvent($block_static, $refinvoicefound, $totalhtamount, $totalvatamount, $totalamount, $total_ht, $total_vat, $total_ttc);
371
372
373 fwrite($fh, ";"
374 .csvClean($block_static->id).';'
375 .csvClean($block_static->date_creation).';'
376 .csvClean($block_static->action).';'
377 .csvClean($block_static->module_source).';'
378 .csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
379 .csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
380 .csvClean($block_static->ref_object).';'
381 .csvClean($block_static->date_object).';'
382 .csvClean($block_static->user_fullname).';'
383 .csvClean($block_static->linktoref).';'
384 .csvClean($block_static->linktype).';'
385 .csvClean($obj->object_data).';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
386 .csvClean($block_static->object_version).';'
387 .csvClean($block_static->object_format).';'
388 .csvClean($block_static->signature).';'
389 .csvClean($statusofrecord).';'
390 .csvClean($signatureexport).';'."\n");
391
392 // Set new previous hash for next fetch
393 $previoushash = $obj->signature;
394
395 $i++;
396 }
397 } else {
398 $error++;
399 setEventMessages($db->lasterror, null, 'errors');
400 }
401
402 // Now calculate cumulative total of all invoices validated
403 /*
404 if (array_key_exists('BILL_VALIDATE', $totalhtamount)) {
405 foreach ($totalhtamount['BILL_VALIDATE'] as $val) { // Loop on each module
406 $totalhtamountalllines['BILL_VALIDATE'] += $val;
407 }
408 foreach ($totalvatamount['BILL_VALIDATE'] as $val) {
409 $totalvatamountalllines['BILL_VALIDATE'] += $val;
410 }
411 foreach ($totalamount['BILL_VALIDATE'] as $val) {
412 $totalamountalllines['BILL_VALIDATE'] += $val;
413 }
414 }
415 if (array_key_exists('PAYMENT_CUSTOMER_CREATE', $totalhtamount)) {
416 foreach ($totalhtamount['PAYMENT_CUSTOMER_CREATE'] as $val) {
417 $totalhtamountalllines['PAYMENT_CUSTOMER_CREATE'] += $val;
418 }
419 foreach ($totalvatamount['PAYMENT_CUSTOMER_CREATE'] as $val) {
420 $totalvatamountalllines['PAYMENT_CUSTOMER_CREATE'] += $val;
421 }
422 foreach ($totalamount['PAYMENT_CUSTOMER_CREATE'] as $val) {
423 $totalamountalllines['PAYMENT_CUSTOMER_CREATE'] += $val;
424 }
425 }
426
427
428 // Add a final line with cumulative total of invoices validated (BILL_VALIDATE)
429 $block_static->id = '';
430 $block_static->date_creation = '';
431 $block_static->action = 'BILL_VALIDATE';
432 $block_static->module_source = '*';
433 $block_static->amounts_taxexcl = $totalhtamountalllines['BILL_VALIDATE'];
434 $block_static->amounts = $totalamountalllines['BILL_VALIDATE'];
435 $block_static->ref_object = $langs->transnoentitiesnoconv("VAT").': '.$totalvatamountalllines['BILL_VALIDATE'];
436 $block_static->date_object = '';
437 $block_static->user_fullname = '';
438 $block_static->linktoref = '';
439 $block_static->linktype = '';
440 $block_static->object_version = '';
441 $block_static->object_format = '';
442 $block_static->signature = '';
443
444 $statusofrecord = '';
445 $signatureexport = '';
446
447
448 fwrite($fh, 'Cumulative total - Invoice validations (all invoices);'
449 .csvClean($block_static->id).';'
450 .csvClean($block_static->date_creation).';'
451 .csvClean($block_static->action).';'
452 .csvClean($block_static->module_source).';'
453 .csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
454 .csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
455 .csvClean($block_static->ref_object).';'
456 .csvClean($block_static->date_object).';'
457 .csvClean($block_static->user_fullname).';'
458 .csvClean($block_static->linktoref).';'
459 .csvClean($block_static->linktype).';'
460 .csvClean($obj->object_data).';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
461 .csvClean($block_static->object_version).';'
462 .csvClean($block_static->object_format).';'
463 .csvClean($block_static->signature).';'
464 .csvClean($statusofrecord).';'
465 .csvClean($signatureexport).';'."\n");
466
467
468 // Add a final line with cumulative total of invoices validated (PAYMENT_CUSTOMER_CREATE)
469 $block_static->id = '';
470 $block_static->date_creation = '';
471 $block_static->action = 'PAYMENT_CUSTOMER_CREATE';
472 $block_static->module_source = '*';
473 $block_static->amounts_taxexcl = '';
474 $block_static->amounts = $totalamountalllines['PAYMENT_CUSTOMER_CREATE'];
475 $block_static->ref_object = '';
476 $block_static->date_object = '';
477 $block_static->user_fullname = '';
478 $block_static->linktoref = '';
479 $block_static->linktype = '';
480 $block_static->object_version = '';
481 $block_static->object_format = '';
482 $block_static->signature = '';
483 $statusofrecord = '';
484 $signatureexport = '';
485
486 fwrite($fh, 'Cumulative total - Invoice payments (all payments);'
487 .csvClean($block_static->id).';'
488 .csvClean($block_static->date_creation).';'
489 .csvClean($block_static->action).';'
490 .csvClean($block_static->module_source).';'
491 .csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
492 .csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
493 .csvClean($block_static->ref_object).';'
494 .csvClean($block_static->date_object).';'
495 .csvClean($block_static->user_fullname).';'
496 .csvClean($block_static->linktoref).';'
497 .csvClean($block_static->linktype).';'
498 .csvClean($obj->object_data).';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
499 .csvClean($block_static->object_version).';'
500 .csvClean($block_static->object_format).';'
501 .csvClean($block_static->signature).';'
502 .csvClean($statusofrecord).';'
503 .csvClean($signatureexport).';'."\n");
504
505
506 // Calculate lifetime totals (with date of first record)
507 $sql = "SELECT action, module_source, object_format, MIN(date_creation) as datemin, SUM(amounts_taxexcl) as sumamounts_taxexcl, SUM(amounts) as sumamounts";
508 $sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
509 $sql .= " WHERE entity = ".((int) $conf->entity);
510 //$sql .= " AND action IN ('BILL_VALIDATE', 'BILL_SENTBYMAIL', 'PAYMENT_CUSTOMER_CREATE', 'CASHCONTROL_CLOSE', 'PAYMENT_CUSTOMER_DELETE', 'DOC_DOWNLOAD', 'DOC_PREVIEW')";
511 $sql .= " AND action IN ('BILL_VALIDATE', 'PAYMENT_CUSTOMER_CREATE', 'PAYMENT_CUSTOMER_DELETE')"; // Only event into lifetime total
512 //$sql .= " AND action IN ('PAYMENT_CUSTOMER_CREATE')";
513 $sql .= " GROUP BY action, module_source, object_format";
514
515 $foundoldformat = 0;
516 $firstrecorddatearray = array();
517 $firstrecorddate = 0;
518 $resql = $db->query($sql);
519 if ($resql) {
520 while ($obj = $db->fetch_object($resql)) {
521 // First record date per action code and module
522 if (!empty($firstrecorddatearray[$obj->action][$obj->module_source])) {
523 $firstrecorddatearray[$obj->action][$obj->module_source] = min($firstrecorddatearray[$obj->action][$obj->module_source], $db->jdate($obj->datemin));
524 } else {
525 $firstrecorddatearray[$obj->action] = array();
526 $firstrecorddatearray[$obj->action][$obj->module_source] = $db->jdate($obj->datemin);
527 }
528 // First record for all actions code
529 if (!empty($firstrecorddate)) {
530 $firstrecorddate = min($firstrecorddate, $db->jdate($obj->datemin));
531 } else {
532 $firstrecorddate = $obj->datemin;
533 }
534
535 if (!isset($totalamountlifetime[$obj->action][$obj->module_source])) {
536 $totalamountlifetime[$obj->action][$obj->module_source] = 0;
537 }
538
539 //var_dump($obj->action, $obj->module_source, $obj->sumamounts);
540
541 // Total per action code and module
542 $totalamountlifetime[$obj->action][$obj->module_source] += $obj->sumamounts;
543
544 // If format of line is old, the sumamounts_taxexcl was not recorded. So we flag this case.
545 if (empty($obj->object_format) || $obj->object_format == 'V1') {
546 $foundoldformat = 1;
547 } else {
548 $totalhtamountlifetime[$obj->action][$obj->module_source] += $obj->sumamounts_taxexcl;
549 }
550 }
551 } else {
552 $error++;
553 setEventMessages($db->lasterror, null, 'errors');
554 }
555
556 // Add a final line with perpetual total for invoice validations
557 $block_static->id = '';
558 $block_static->date_creation = '';
559 $block_static->action = 'BILL_VALIDATE';
560 $block_static->module_source = '*';
561 // if an old format was found, we do not have reliable amount excluding tax for lifetime value, we do not show it
562
563 $block_static->amounts_taxexcl = ($foundoldformat ? '' : array_sum($totalhtamountlifetime['BILL_VALIDATE']));
564 $block_static->amounts = array_sum($totalamountlifetime['BILL_VALIDATE']);
565 // if an old format was found, we do not have reliable VAT amount for lifetime value, we do not show it
566 $block_static->ref_object = ($foundoldformat ? '' : $langs->transnoentitiesnoconv("VAT").': '.($block_static->amounts - $block_static->amounts_taxexcl));
567 $block_static->date_object = '';
568 $block_static->user_fullname = '';
569 $block_static->linktoref = '';
570 $block_static->linktype = '';
571 $block_static->object_version = '';
572 $block_static->object_format = '';
573 $block_static->signature = '';
574
575 $statusofrecord = '';
576 $signatureexport = '';
577
578 fwrite($fh, 'Lifetime total (>= '.dol_print_date($firstrecorddate, 'standard').') - Invoice validations (all invoices);'
579 .csvClean($block_static->id).';'
580 .csvClean($block_static->date_creation).';'
581 .csvClean($block_static->action).';'
582 .csvClean($block_static->module_source).';'
583 .csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
584 .csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
585 .csvClean($block_static->ref_object).';'
586 .csvClean($block_static->date_object).';'
587 .csvClean($block_static->user_fullname).';'
588 .csvClean($block_static->linktoref).';'
589 .csvClean($block_static->linktype).';'
590 .csvClean($obj->object_data).';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
591 .csvClean($block_static->object_version).';'
592 .csvClean($block_static->object_format).';'
593 .csvClean($block_static->signature).';'
594 .csvClean($statusofrecord).';'
595 .csvClean($signatureexport).';'."\n");
596
597
598 // Add a final line with perpetual total for customer payments
599 $block_static->id = '';
600 $block_static->date_creation = '';
601 $block_static->action = 'PAYMENT_CUSTOMER_CREATE';
602 $block_static->module_source = '*';
603 $block_static->amounts_taxtecl = '';
604 $block_static->amounts = array_sum($totalamountlifetime['PAYMENT_CUSTOMER_CREATE']);
605 $block_static->ref_object = '';
606 $block_static->date_object = '';
607 $block_static->user_fullname = '';
608 $block_static->linktoref = '';
609 $block_static->linktype = '';
610 $block_static->object_version = '';
611 $block_static->object_format = '';
612 $block_static->signature = '';
613
614 $statusofrecord = '';
615 $signatureexport = '';
616
617 fwrite($fh, 'Lifetime total (>= '.dol_print_date($firstrecorddate, 'standard').') - Invoice payments (all payments);'
618 .csvClean($block_static->id).';'
619 .csvClean($block_static->date_creation).';'
620 .csvClean($block_static->action).';'
621 .csvClean($block_static->module_source).';'
622 .csvClean($block_static->amounts_taxexcl).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
623 .csvClean($block_static->amounts).';' // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
624 .csvClean($block_static->ref_object).';'
625 .csvClean($block_static->date_object).';'
626 .csvClean($block_static->user_fullname).';'
627 .csvClean($block_static->linktoref).';'
628 .csvClean($block_static->linktype).';'
629 .csvClean($obj->object_data).';' // We must use the string (so $obj->object_data) and not the array decoded with dolDecodeBlockedData
630 .csvClean($block_static->object_version).';'
631 .csvClean($block_static->object_format).';'
632 .csvClean($block_static->signature).';'
633 .csvClean($statusofrecord).';'
634 .csvClean($signatureexport).';'."\n");
635 */
636
637 fclose($fh);
638
639 // Calculate the signature of the file (the last line has a return line)
640 $algo = 'sha256';
641 $sha256 = hash_file($algo, $tmpfile);
642 $hmacsha256 = hash_hmac_file($algo, $tmpfile, $secretkey);
643
644 // Now add a signature to check integrity at end of file
645 file_put_contents($tmpfile, 'END - sha256='.$sha256.' - hmac_sha256='.$hmacsha256, FILE_APPEND);
646 dolChmod($tmpfile);
647
648 if (!$error) {
649 setEventMessages($langs->trans("FileGenerated"), null);
650 }
651 }
652
653 if (!$error) {
654 if ($periodnotcomplete) {
655 setEventMessages($langs->trans("ErrorPeriodMustBePastToAllowExport"), null, "warnings");
656 } else {
657 // We record the export as a new line into the unalterable logs
658 require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
659 $b = new BlockedLog($db);
660
661 $object = new stdClass();
662 $object->id = 0;
663 $object->element = 'module';
664 $object->ref = 'systemevent';
665 $object->entity = $conf->entity;
666 $object->date = dol_now();
667 $object->fullname = $user->getFullName($langs);
668
669 $object->label = 'Export unalterable logs';
670 $object->period = 'year='.GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') ? ' month='.GETPOSTINT('monthtoexport') : '');
671
672 $action = 'BLOCKEDLOG_EXPORT';
673 $result = $b->setObjectData($object, $action, 0, $user, null);
674 //var_dump($b); exit;
675
676 if ($result < 0) {
677 setEventMessages('Failed to insert the export int the unalterable log', null, 'errors');
678 $error++;
679 }
680
681 $res = $b->create($user);
682
683 if ($res < 0) {
684 setEventMessages('Failed to insert the export int the unalterable log', null, 'errors');
685 $error++;
686 }
687 }
688 }
689}
690
691
692/*
693 * View
694 */
695
696$form = new Form($db);
697$formother = new FormOther($db);
698
699if (GETPOST('withtab', 'alpha')) {
700 $title = $langs->trans("ModuleSetup").' '.$langs->trans('BlockedLog');
701} else {
702 $title = $langs->trans("BrowseBlockedLog");
703}
704$help_url = "EN:Module_Unalterable_Archives_-_Logs|FR:Module_Archives_-_Logs_Inaltérable";
705
706llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'bodyforlist mod-blockedlog page-admin_blockedlog_list');
707
708$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);
709if (!is_array($blocks)) {
710 if ($blocks == -2) {
711 setEventMessages($langs->trans("TooManyRecordToScanRestrictFilters", $MAXLINES), null, 'errors');
712 } else {
713 dol_print_error($block_static->db, $block_static->error, $block_static->errors);
714 exit;
715 }
716}
717
718$linkback = '';
719if (GETPOST('withtab', 'alpha')) {
720 $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>';
721}
722
723$morehtmlcenter = '';
724
725$registrationnumber = getHashUniqueIdOfRegistration();
726$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
727
728print load_fiche_titre($title.'<br>'.$texttop, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
729
730$head = blockedlogadmin_prepare_head(GETPOST('withtab', 'alpha'));
731
732print dol_get_fiche_head($head, 'archives', '', -1);
733
734//print $texttop;
735//print '<br><br>';
736
737print '<div class="opacitymedium hideonsmartphone justify">';
738
739print $langs->trans("ArchivesDesc")."<br>";
740
741print "</div>\n";
742
743$htmltext = '';
744
745$htmltext .= $langs->trans("UnalterableLogTool2", $langs->transnoentities("Archives"))."<br>";
746if ($mysoc->country_code == 'FR') {
747 $htmltext .= '<br>'.$langs->trans("UnalterableLogTool1FR").'<br>';
748}
749//$htmltext .= $langs->trans("UnalterableLogTool1");
750//$htmltext .= $langs->trans("UnalterableLogTool3")."<br>";
751
752print info_admin($htmltext, 0, 0, 'warning');
753
754
755print '<br>';
756
757$param = '';
758if ($contextpage != getDolDefaultContextPage(__FILE__)) {
759 $param .= '&contextpage='.urlencode($contextpage);
760}
761if ($limit > 0 && $limit != $conf->liste_limit) {
762 $param .= '&limit='.((int) $limit);
763}
764if ($search_id != '') {
765 $param .= '&search_id='.urlencode($search_id);
766}
767if ($search_fk_user > 0) {
768 $param .= '&search_fk_user='.urlencode($search_fk_user);
769}
770if ($search_startyear > 0) {
771 $param .= '&search_startyear='.((int) $search_startyear);
772}
773if ($search_startmonth > 0) {
774 $param .= '&search_startmonth='.((int) $search_startmonth);
775}
776if ($search_startday > 0) {
777 $param .= '&search_startday='.((int) $search_startday);
778}
779if ($search_endyear > 0) {
780 $param .= '&search_endyear='.((int) $search_endyear);
781}
782if ($search_endmonth > 0) {
783 $param .= '&search_endmonth='.((int) $search_endmonth);
784}
785if ($search_endday > 0) {
786 $param .= '&search_endday='.((int) $search_endday);
787}
788if ($search_amount) {
789 $param .= '&search_amount='.urlencode($search_amount);
790}
791if ($search_signature) {
792 $param .= '&search_signature='.urlencode($search_signature);
793}
794if ($search_showonlyerrors > 0) {
795 $param .= '&search_showonlyerrors='.((int) $search_showonlyerrors);
796}
797if ($optioncss != '') {
798 $param .= '&optioncss='.urlencode($optioncss);
799}
800if (GETPOST('withtab', 'alpha')) {
801 $param .= '&withtab='.urlencode(GETPOST('withtab', 'alpha'));
802}
803
804// Add $param from extra fields
805//include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
806
807if ($action == 'deletefile') {
808 $langs->load("companies"); // Need for string DeleteFile+ConfirmDeleteFiles
809 print $form->formconfirm(
810 $_SERVER["PHP_SELF"].'?urlfile='.urlencode(GETPOST("urlfile")).'&linkid='.GETPOSTINT('linkid').(empty($param) ? '' : $param),
811 $langs->trans('DeleteFile'),
812 $langs->trans('ConfirmDeleteFile'),
813 'confirm_deletefile',
814 '',
815 '',
816 1
817 );
818}
819
820
821print '<form method="POST" id="exportArchives" action="'.$_SERVER["PHP_SELF"].'?output=file">';
822print '<input type="hidden" name="token" value="'.newToken().'">';
823print '<input type="hidden" name="action" value="export">';
824
825print '<div class="right">';
826
827print '<span class="hideonsmartphone">'.$langs->trans("RestrictYearToExport").': </span>';
828// Month
829print $formother->select_month((string) GETPOSTINT('monthtoexport'), 'monthtoexport', $langs->trans("Month"), 0, 'minwidth50 maxwidth75imp valignmiddle', true);
830print '<input type="text" name="yeartoexport" class="valignmiddle maxwidth75imp" value="'.GETPOST('yeartoexport').'" placeholder="'.$langs->trans("Year").'">';
831
832print ' ';
833
834// Disabled, we will use the getHashUniqueIdOfRegistration() as secret HMAC
835//print '<input type="text" name="hmacexportkey" class="valignmiddle minwidth150imp maxwidth300imp" required value="'.GETPOST('hmacexportkey').'" placeholder="'.$langs->trans("Password").'">';
836
837print ' ';
838
839print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
840print '<input type="submit" name="downloadcsv" class="button" value="'.$langs->trans('DownloadLogCSV').'">';
841/*if (getDolGlobalString('BLOCKEDLOG_USE_REMOTE_AUTHORITY')) {
842 print ' | <a href="?action=downloadblockchain'.(GETPOST('withtab', 'alpha') ? '&withtab='.GETPOST('withtab', 'alpha') : '').'">'.$langs->trans('DownloadBlockChain').'</a>';
843}*/
844print ' </div><br>';
845
846print '</form>';
847
848
849/*
850print '<form method="POST" id="searchFormList" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
851
852if ($optioncss != '') {
853 print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
854}
855print '<input type="hidden" name="token" value="'.newToken().'">';
856print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
857print '<input type="hidden" name="action" value="list">';
858print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
859print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
860print '<input type="hidden" name="page" value="'.$page.'">';
861print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
862print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
863
864print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
865*/
866
867$filearray = dol_dir_list($upload_dir, 'files', 0, '', null, 'name', SORT_ASC, 1);
868
869$modulepart = 'blockedlog';
870$relativepathwithnofile = 'archives/';
871$disablemove = 1;
872/*
873$param = '&id='.$object->id.'&entity='.(empty($object->entity) ? getDolEntity() : $object->entity);
874include DOL_DOCUMENT_ROOT.'/core/tpl/document_actions_post_headers.tpl.php';
875*/
876
877include_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
878$formfile = new FormFile($db);
879
880$savingdocmask = '';
881
882$object = $block_static;
883
884// Get the form to add files (upload and links)
885$tmparray = $formfile->form_attach_new_file(
886 $_SERVER["PHP_SELF"],
887 '',
888 0,
889 0,
890 $permission,
891 $conf->browser->layout == 'phone' ? 40 : 60,
892 $object,
893 '',
894 1,
895 $savingdocmask,
896 1,
897 'formuserfile',
898 '',
899 '',
900 0,
901 0,
902 0,
903 2
904);
905
906$formToUploadAFile = '';
907
908if (is_array($tmparray) && !empty($tmparray)) {
909 $formToUploadAFile = $tmparray['formToUploadAFile'];
910}
911
912// List of document
913// TODO Replace with specific code to list files with mass action, ...
914$formfile->list_of_documents(
915 $filearray,
916 null,
917 $modulepart,
918 $param,
919 0,
920 $relativepathwithnofile, // relative path with no file. For example "0/1"
921 $permission,
922 0,
923 '',
924 0,
925 $langs->transnoentitiesnoconv('Archives'),
926 '',
927 0,
928 $permtoedit,
929 $upload_dir,
930 $sortfield,
931 $sortorder,
932 $disablemove,
933 0,
934 -1,
935 '',
936 array('afteruploadtitle' => $formToUploadAFile, 'showhideaddbutton' => 1)
937);
938
939/*
940print '</div>';
941
942print '</form>';
943*/
944
945// Javascript to manage the showinfo popup
946print '<script type="text/javascript">
947
948jQuery(document).ready(function () {
949 jQuery("#dialogforpopup").dialog({
950 closeOnEscape: true,
951 classes: { "ui-dialog": "highlight" },
952 maxHeight: window.innerHeight-60,
953 height: window.innerHeight-60,
954 width: '.($conf->browser->layout == 'phone' ? 400 : 700).',
955 modal: true,
956 autoOpen: false
957 }).css("z-index: 5000");
958
959 $("a[rel=show-info]").click(function() {
960 console.log("We click on tooltip a[rel=show-info], we open popup and get content using an ajax call");
961
962 var fk_block = $(this).attr("data-blockid");
963
964 $.ajax({
965 method: "GET",
966 data: { token: \''.currentToken().'\' },
967 url: "'.DOL_URL_ROOT.'/blockedlog/ajax/block-info.php?id="+fk_block,
968 dataType: "html"
969 }).done(function(data) {
970 jQuery("#dialogforpopup").html(data);
971 });
972
973 var mydialog = jQuery("#dialogforpopup");
974 mydialog.dialog({autoOpen: false, modal: true, height: (window.innerHeight - 150), width: \'80%\', title: \''.dol_escape_js($langs->trans("UnlaterableDataOfEvent")).'\',});
975 mydialog.dialog("open");
976 return false;
977 });
978})
979</script>'."\n";
980
981
982if (GETPOST('withtab', 'alpha')) {
983 print dol_get_fiche_end();
984}
985
986print '<br><br>';
987
988// End of page
989llxFooter();
990$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
getHashUniqueIdOfRegistration()
Return a hash unique identifier of the registration.
blockedlogadmin_prepare_head($withtabsetup)
Define head array for tabs of blockedlog tools setup pages.
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 permettant la generation de composants html autre 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:603
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition date.lib.php:125
dol_get_last_day($year, $month=12, $gm=false)
Return GMT time for last day of a month or year.
Definition date.lib.php:622
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)
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.
currentToken()
Return the value of token currently saved into session with name 'token'.
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.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
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.
if(!function_exists( 'utf8_encode')) if(!function_exists('utf8_decode')) if(!function_exists( 'str_starts_with')) if(!function_exists('str_ends_with')) if(!function_exists( 'str_contains')) 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_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.
isModEnabled($module)
Is Dolibarr module enabled.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
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)
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
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.