dolibarr 25.0.0-alpha
customreports.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2020-2024 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
4 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
48'
49@phan-var-force ?int[] $toselect
50';
51
52// Initialise values
53$tabfamily = null;
54$objecttype = null;
55
56if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
57 require '../main.inc.php';
58
59 // Get parameters
60 $action = GETPOST('action', 'aZ09') ? GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ...
61 $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists)
62
63 $mode = GETPOST('mode', 'alpha');
64 $objecttype = (string) GETPOST('objecttype', 'aZ09arobase');
65 $tabfamily = GETPOST('tabfamily', 'aZ09');
66
67 $search_measures = GETPOST('search_measures', 'array:alphanohtml');
68
69 if (GETPOST('search_xaxis', 'alpha') && GETPOST('search_xaxis', 'alpha') != '-1') {
70 $search_xaxis = array(GETPOST('search_xaxis', 'alpha'));
71 } else {
72 $search_xaxis = array();
73 }
74 if (GETPOST('search_groupby', 'alpha') && GETPOST('search_groupby', 'alpha') != '-1') {
75 $search_groupby = array(GETPOST('search_groupby', 'alpha'));
76 } else {
77 $search_groupby = array();
78 }
79 '@phan-var-force string[] $search_groupby';
80
81 $search_yaxis = GETPOST('search_yaxis', 'array:alphanohtml');
82 $search_graph = (string) GETPOST('search_graph', 'restricthtml');
83
90 function sanititzekey($value)
91 {
92 return preg_replace('/[^a-z0-9\._\-]+/', '', $value);
93 }
94
95 $sanitized_search_measures = array_map('sanititzekey', $search_measures);
96 $sanitized_search_xaxis = array_map('sanititzekey', $search_xaxis);
97 $sanitized_search_yaxis = array_map('sanititzekey', $search_yaxis);
98 $sanitized_search_groupby = array_map('sanititzekey', $search_groupby);
99
100 // Load variable for pagination
101 $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
102 $sortfield = GETPOST('sortfield', 'aZ09comma');
103 $sortorder = GETPOST('sortorder', 'aZ09comma');
104 $page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
105 if (empty($page) || $page == -1 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha') || (empty($toselect) && $massaction === '0')) {
106 $page = 0;
107 } // If $page is not defined, or '' or -1 or if we click on clear filters or if we select empty mass action
108 $offset = $limit * $page;
109 $pageprev = $page - 1;
110 $pagenext = $page + 1;
111
112 $object = null;
113} else {
114 // When included into a main page
124 '
125 @phan-var-force int<0,1> $SHOWLEGEND
126 @phan-var-force string $customreportkey
127 @phan-var-force ?string $customsql
128 @phan-var-force ?string[] $sanitized_search_groupby Array with the third dimension
129 @phan-var-force int $page
130 @phan-var-force string $sortfield
131 @phan-var-force string $sortorder
132 ';
133
134 // $sanitized_search_measures, $sanitized_search_xaxis or $sanitized_search_yaxis may have been defined by the parent.
135
136 if (empty($user) || empty($user->id)) {
137 print 'Page is called as an include but $user and its permission loaded with loadRights() are not defined. We stop here.';
138 exit(1);
139 }
140 if (empty($object)) {
141 print 'Page is called as an include but $object is not defined. We stop here.';
142 exit(1);
143 }
144}
145
146// In customreport context, we force the protection to avoid forging of criteria including bind SQL injection
147global $dolibarr_allow_unsecured_select_in_extrafields_filter;
148$dolibarr_allow_unsecured_select_in_extrafields_filter = 0;
149
150if (empty($mode)) {
151 $mode = 'graph';
152}
153if (!isset($sanitized_search_measures)) {
154 $sanitized_search_measures = array(0 => 't.count');
155}
156if (!isset($sanitized_search_xaxis)) {
157 // Ensure value is set and not null.
158 $sanitized_search_xaxis = array();
159}
160if (!isset($sanitized_search_yaxis)) {
161 // Ensure value is set and not null.
162 $sanitized_search_yaxis = array();
163}
164if (!isset($sanitized_search_groupby)) {
165 // Ensure value is set and not null.
166 $sanitized_search_groupby = array();
167}
168if (!isset($search_graph)) {
169 // Ensure value is set and not null
170 $search_graph = '';
171}
172if (!empty($object)) {
173 $objecttype = $object->element.($object->module ? '@'.$object->module : '');
174}
175if ((!is_string($objecttype) || empty($objecttype)) && isModEnabled('societe')) {
176 $objecttype = 'thirdparty';
177}
178'@phan-var-force string $objecttype'; // Help phan that suggests $objecttype can be null
179
180require_once DOL_DOCUMENT_ROOT."/core/class/extrafields.class.php";
181require_once DOL_DOCUMENT_ROOT."/core/class/html.form.class.php";
182require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
183require_once DOL_DOCUMENT_ROOT."/core/lib/company.lib.php";
184require_once DOL_DOCUMENT_ROOT."/core/lib/date.lib.php";
185require_once DOL_DOCUMENT_ROOT."/core/lib/customreports.lib.php";
186require_once DOL_DOCUMENT_ROOT."/core/class/dolgraph.class.php";
187require_once DOL_DOCUMENT_ROOT."/core/class/doleditor.class.php";
188require_once DOL_DOCUMENT_ROOT."/core/class/html.formother.class.php";
189
190// Load traductions files requiredby by page (stocks is for labels of linked warehouses in tables of fields)
191$langs->loadLangs(array("companies", "other", "exports", "sendings", "stocks"));
192
193$extrafields = new ExtraFields($db);
194
195$hookmanager->initHooks(array('customreport')); // Note that conf->hooks_modules contains array
196
197$title = '';
198$picto = '';
199$errormessage = null;
200$keyforlabeloffield = null;
201$head = array();
202$ObjectClassName = '';
203// Objects available by default
204$arrayoftype = array(
205 'thirdparty' => array('label' => 'ThirdParties', 'picto' => 'company', 'ObjectClassName' => 'Societe', 'enabled' => isModEnabled('societe'), 'ClassPath' => "/societe/class/societe.class.php", 'langs' => 'companies'),
206 'contact' => array('label' => 'Contacts', 'picto' => 'contact', 'ObjectClassName' => 'Contact', 'enabled' => isModEnabled('societe'), 'ClassPath' => "/contact/class/contact.class.php"),
207 'proposal' => array('label' => 'Proposals', 'picto' => 'proposal', 'ObjectClassName' => 'Propal', 'enabled' => isModEnabled('propal'), 'ClassPath' => "/comm/propal/class/propal.class.php", 'langs' => 'propal'),
208 'proposaldet' => array('label' => 'ProposalLines', 'picto' => 'proposal', 'ObjectClassName' => 'PropaleLigne', 'enabled' => isModEnabled('propal'), 'ClassPath' => "/comm/propal/class/propaleligne.class.php", 'langs' => 'propal'),
209 'order' => array('label' => 'Orders', 'picto' => 'order', 'ObjectClassName' => 'Commande', 'enabled' => isModEnabled('order'), 'ClassPath' => "/commande/class/commande.class.php", 'langs' => 'orders'),
210 'orderdet' => array('label' => 'SaleOrderLines', 'picto' => 'order', 'ObjectClassName' => 'OrderLine', 'enabled' => isModEnabled('order'), 'ClassPath' => "/commande/class/orderline.class.php", 'langs' => 'orders'),
211 'invoice' => array('label' => 'Invoices', 'picto' => 'bill', 'ObjectClassName' => 'Facture', 'enabled' => isModEnabled('invoice'), 'ClassPath' => "/compta/facture/class/facture.class.php", 'langs' => 'bills'),
212 'invoice_template' => array('label' => 'PredefinedInvoices', 'picto' => 'bill', 'ObjectClassName' => 'FactureRec', 'enabled' => isModEnabled('invoice'), 'ClassPath' => "/compta/facture/class/facture-rec.class.php", 'langs' => 'bills'),
213 'contract' => array('label' => 'Contracts', 'picto' => 'contract', 'ObjectClassName' => 'Contrat', 'enabled' => isModEnabled('contract'), 'ClassPath' => "/contrat/class/contrat.class.php", 'langs' => 'contracts'),
214 'contractdet' => array('label' => 'ContractLines', 'picto' => 'contract', 'ObjectClassName' => 'ContratLigne', 'enabled' => isModEnabled('contract'), 'ClassPath' => "/contrat/class/contrat.class.php", 'langs' => 'contracts'),
215 'bom' => array('label' => 'BOM', 'picto' => 'bom', 'ObjectClassName' => 'Bom', 'enabled' => isModEnabled('bom')),
216 'mrp' => array('label' => 'MO', 'picto' => 'mrp', 'ObjectClassName' => 'Mo', 'enabled' => isModEnabled('mrp'), 'ClassPath' => "/mrp/class/mo.class.php"),
217 'ticket' => array('label' => 'Ticket', 'picto' => 'ticket', 'ObjectClassName' => 'Ticket', 'enabled' => isModEnabled('ticket')),
218 'member' => array('langs' => 'members', 'label' => 'Adherent', 'picto' => 'member', 'ObjectClassName' => 'Adherent', 'enabled' => isModEnabled('member'), 'ClassPath' => "/adherents/class/adherent.class.php"),
219 'cotisation' => array('langs' => 'members', 'label' => 'Subscriptions', 'picto' => 'member', 'ObjectClassName' => 'Subscription', 'enabled' => isModEnabled('member'), 'ClassPath' => "/adherents/class/subscription.class.php"),
220);
221
222
223// Complete $arrayoftype by external modules
224$parameters = array('objecttype' => $objecttype, 'tabfamily' => $tabfamily);
225// @phan-suppress-next-line PhanTypeMismatchArgumentNullable
226$reshook = $hookmanager->executeHooks('loadDataForCustomReports', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
227if ($reshook < 0) {
228 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
229} elseif (is_array($hookmanager->resArray)) {
230 if (!empty($hookmanager->resArray['title'])) { // Add entries for tabs
231 $title = $hookmanager->resArray['title'];
232 }
233 if (!empty($hookmanager->resArray['picto'])) { // Add entries for tabs
234 $picto = $hookmanager->resArray['picto'];
235 }
236 if (!empty($hookmanager->resArray['head'])) { // Add entries for tabs
237 $head = array_merge($head, $hookmanager->resArray['head']);
238 }
239 if (!empty($hookmanager->resArray['arrayoftype'])) { // Add entries from hook
240 foreach ($hookmanager->resArray['arrayoftype'] as $key => $val) {
241 $arrayoftype[$key] = $val;
242 }
243 }
244}
245
246// Load the main $object for statistics
247if ($objecttype) {
248 try {
249 if (!empty($arrayoftype[$objecttype]['ClassPath'])) {
250 $fileforclass = $arrayoftype[$objecttype]['ClassPath'];
251 } else {
252 $fileforclass = "/".$objecttype."/class/".$objecttype.".class.php";
253 }
254 $ObjectClassName = null;
255
256 if ($fileforclass !== null) {
257 dol_include_once($fileforclass);
258
259 $ObjectClassName = $arrayoftype[$objecttype]['ObjectClassName'];
260 }
261 if (!empty($ObjectClassName)) {
262 if (class_exists($ObjectClassName)) {
263 $object = new $ObjectClassName($db);
264 } else {
265 print 'Failed to load class for type '.$objecttype.'. Class file found but Class object named '.$ObjectClassName.' not found.';
266 }
267 } else {
268 print 'Failed to load class for type '.$objecttype.'. Class file name is unknown.';
269 }
270 } catch (Exception $e) {
271 print 'Failed to load class for type '.$objecttype.'. Class path not found.';
272 }
273}
274
275'@phan-var-force CommonObject $object';
276
277// Security check
278//$socid = 0;
279if ($user->socid > 0) { // Protection if external user
280 //$socid = $user->socid;
281 accessforbidden('Access forbidden to external users');
282}
283
284// Fetch optionals attributes and labels
285$extrafields->fetch_name_optionals_label('all'); // We load all extrafields definitions for all objects
286//$extrafields->fetch_name_optionals_label($object->table_element_line);
287
288if (!empty($object->table_element)) {
289 $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
290} else {
291 $search_array_options = array();
292}
293
294$search_component_params = array('');
295$search_component_params_hidden = trim(GETPOST('search_component_params_hidden', 'alphanohtml'));
296$search_component_params_input = trim(GETPOST('search_component_params_input', 'alphanohtml'));
297//var_dump($search_component_params_hidden);
298//var_dump($search_component_params_input);
299
300// If string is not an universal filter string, we try to convert it into universal filter syntax string
301$errorstr = '';
302forgeSQLFromUniversalSearchCriteria($search_component_params_input, $errorstr); // Try conversion UFS->SQL
303//var_dump($errorstr);
304if ($errorstr) {
305 $value = $search_component_params_input;
306
307 $value = preg_replace('/([a-z\.]+)\s*([!<>=]+|in|notin|like|notlike)\s*/', '\1:\2:', $value); // Clean string 'x < 10' into 'x:<:10' so we can then explode on space to get all AND tests to do
308 $value = preg_replace('/\s*\|\s*/', '|', $value);
309 //var_dump($value);
310
311 $crits = explode(' ', trim($value)); // the string after the name of the field. Explode on each AND
312 $res = '';
313
314 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
315 foreach ($crits as $crit) { // Loop on each AND criteria
316 $crit = trim($crit);
317
318 $i2 = 0; // count the nb of valid criteria added for this first criteria
319 $newres = '';
320 $tmpcrits = explode('|', $crit);
321 $i3 = 0; // count the nb of valid criteria added for this current field
322 foreach ($tmpcrits as $tmpcrit) {
323 if ($tmpcrit !== '0' && empty($tmpcrit)) {
324 continue;
325 }
326 $tmpcrit = trim($tmpcrit);
327 //var_dump($tmpcrit);
328
329 $errorstr = '';
330 $parenthesislevel = 0;
331 $rescheckfilter = dolCheckFilters($tmpcrit, $errorstr, $parenthesislevel);
332 if ($rescheckfilter) {
333 while ($parenthesislevel > 0) {
334 $tmpcrit = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $tmpcrit));
335 $parenthesislevel--;
336 }
337 }
338
339 $field = preg_replace('/(:[!<>=\s]+:|:in:|:notin:|:like:|:notlike:).*$/', '', $tmpcrit); // the name of the field
340 $tmpcrit = preg_replace('/^.*(:[!<>=\s]+:|:in:|:notin:|:like:|:notlike:)/', '\1', $tmpcrit); // the condition after the name of the field
341 //var_dump($field); var_dump($tmpcrit); var_dump($i3);
342
343 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
344
345 $operator = '=';
346 $newcrit = preg_replace('/(:[!<>=\s]+:|:in:|:notin:|:like:|:notlike:)/', '', $tmpcrit);
347 //var_dump($newcrit);
348
349 $reg = array();
350 preg_match('/:([!<>=\s]+|in|notin|like|notlike):/', $tmpcrit, $reg);
351 if (!empty($reg[1])) {
352 $operator = $reg[1];
353 }
354 if ($newcrit != '') {
355 if (!preg_match('/^\'[^\']*\'$/', $newcrit)) {
356 $numnewcrit = price2num($newcrit);
357 $newres .= '('.$field.':'.$operator.':'.((float) $numnewcrit).')';
358 } else {
359 $newres .= '('.$field.':'.$operator.":".((string) $newcrit).')';
360 }
361 $i3++; // a criteria was added to string
362 }
363 }
364 $i2++; // a criteria for 1 more field was added to string
365
366 if ($newres) {
367 $res = $res.($res ? ' AND ' : '').($i3 > 1 ? '(' : '').$newres.($i3 > 1 ? ')' : '');
368 }
369 $i1++;
370 }
371 $res = "(".$res.")";
372
373 //var_dump($res);exit;
374 $search_component_params_input = $res;
375}
376
377$arrayofandtagshidden = dolForgeExplodeAnd($search_component_params_hidden);
378$arrayofandtagsinput = dolForgeExplodeAnd($search_component_params_input);
379
380$search_component_params_hidden = implode(' AND ', array_merge($arrayofandtagshidden, $arrayofandtagsinput));
381//var_dump($search_component_params_hidden);
382
383$MAXUNIQUEVALFORGROUP = 20;
384$MAXMEASURESINBARGRAPH = 20;
385$SHOWLEGEND = (isset($SHOWLEGEND) ? $SHOWLEGEND : 1);
386
387$YYYY = substr($langs->trans("Year"), 0, 1).substr($langs->trans("Year"), 0, 1).substr($langs->trans("Year"), 0, 1).substr($langs->trans("Year"), 0, 1);
388$MM = substr($langs->trans("Month"), 0, 1).substr($langs->trans("Month"), 0, 1);
389$DD = substr($langs->trans("Day"), 0, 1).substr($langs->trans("Day"), 0, 1);
390$HH = substr($langs->trans("Hour"), 0, 1).substr($langs->trans("Hour"), 0, 1);
391$MI = substr($langs->trans("Minute"), 0, 1).substr($langs->trans("Minute"), 0, 1);
392$SS = substr($langs->trans("Second"), 0, 1).substr($langs->trans("Second"), 0, 1);
393
394$arrayoffilterfields = array();
395$arrayofmesures = array();
396$arrayofxaxis = array();
397$arrayofgroupby = array();
398$arrayofyaxis = array();
399$arrayofvaluesforgroupby = array();
400
401$features = '';
402if (!empty($object->element)) {
403 $features = $object->element;
404} else {
405 $features = '';
406}
407if (!empty($object->element_for_permission)) {
408 $features = $object->element_for_permission;
409} else {
410 $features .= (empty($object->module) ? '' : '@'.$object->module);
411}
412
413// $arrayoftype contains several features
414// Test on permission can be done on a given selected feature only
415
416// Security check (do not stop here, get only result to show message later)
417$resultcheck = restrictedArea($user, $features, 0, '', '', 'fk_soc', 'rowid', 0, 1);
418
419
420/*
421 * Actions
422 */
423
424// None
425
426
427
428/*
429 * View
430 */
431
432$form = new Form($db);
433$formother = new FormOther($db);
434
435if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
436 llxHeader('', $langs->transnoentitiesnoconv('CustomReports'), '');
437
438 if (empty($head)) {
439 print dol_get_fiche_head($head, 'customreports', $title, -2, $picto);
440 } else {
441 print dol_get_fiche_head($head, 'customreports', $title, -1, $picto);
442 }
443}
444
445// Define $newarrayoftype that is array of object available for report
446$newarrayoftype = array();
447foreach ($arrayoftype as $key => $val) {
448 if (dol_eval((string) $val['enabled'], 1, 1, '1')) {
449 $newarrayoftype[$key] = $arrayoftype[$key];
450 }
451 if (!empty($val['langs'])) {
452 $langs->load($val['langs']);
453 }
454}
455
456$count = 0;
457$label = '';
458if (array_key_exists($objecttype, $newarrayoftype)) {
459 $label = $langs->trans($newarrayoftype[$objecttype]['label']);
460}
461$arrayoffilterfields = fillArrayOfFilterFields($object, 't', $label, $arrayoffilterfields, 0, $count);
462$arrayoffilterfields = dol_sort_array($arrayoffilterfields, 'position', 'asc', 0, 0, 1);
463
464$count = 0;
465$arrayofmesures = fillArrayOfMeasures($object, 't', $label, $arrayofmesures, 0, $count);
466$arrayofmesures = dol_sort_array($arrayofmesures, 'position', 'asc', 0, 0, 1);
467
468$count = 0;
469$arrayofxaxis = fillArrayOfXAxis($object, 't', $label, $arrayofxaxis, 0, $count);
470$arrayofxaxis = dol_sort_array($arrayofxaxis, 'position', 'asc', 0, 0, 1);
471
472$count = 0;
473$arrayofgroupby = fillArrayOfGroupBy($object, 't', $label, $arrayofgroupby, 0, $count);
474$arrayofgroupby = dol_sort_array($arrayofgroupby, 'position', 'asc', 0, 0, 1);
475
476
477// Check parameters
478if ($action == 'viewgraph') {
479 if (!count($sanitized_search_measures)) {
480 setEventMessages($langs->trans("AtLeastOneMeasureIsRequired"), null, 'warnings');
481 } elseif ($mode == 'graph' && is_array($sanitized_search_xaxis) && count($sanitized_search_xaxis) > 1) {
482 setEventMessages($langs->trans("OnlyOneFieldForXAxisIsPossible"), null, 'warnings');
483 $sanitized_search_xaxis = array(0 => $sanitized_search_xaxis[0]);
484 }
485 if (count($sanitized_search_groupby) >= 2) {
486 setEventMessages($langs->trans("ErrorOnlyOneFieldForGroupByIsPossible"), null, 'warnings');
487 $sanitized_search_groupby = array(0 => $sanitized_search_groupby[0]);
488 }
489 if (!count($sanitized_search_xaxis)) {
490 setEventMessages($langs->trans("AtLeastOneXAxisIsRequired"), null, 'warnings');
491 } elseif ($mode == 'graph' && $search_graph == 'bars' && count($sanitized_search_measures) > $MAXMEASURESINBARGRAPH) {
492 $langs->load("errors");
493 setEventMessages($langs->trans("GraphInBarsAreLimitedToNMeasures", $MAXMEASURESINBARGRAPH), null, 'warnings');
494 $search_graph = 'lines';
495 }
496}
497
498// Get all possible values of fields when a 'group by' is set, and save this into $arrayofvaluesforgroupby
499// $arrayofvaluesforgroupby will be used to forge lael of each grouped series
500if (count($sanitized_search_groupby)) {
501 $fieldtocount = '';
502 foreach ($sanitized_search_groupby as $gkey => $sanitized_gval) {
503 $gvalwithoutprefix = preg_replace('/^[a-z]+\./i', '', $sanitized_gval);
504 $gvalsanitized = preg_replace('/[^a-z0-9\._\-]+/i', '', $sanitized_gval);
505
506 $sanitizedfieldtocount = '';
507
508 if (preg_match('/\-year$/', $gvalsanitized)) {
509 $tmpval = preg_replace('/\-year$/', '', $gvalsanitized);
510 $sanitizedfieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y')";
511 } elseif (preg_match('/\-month$/', $gvalsanitized)) {
512 $tmpval = preg_replace('/\-month$/', '', $gvalsanitized);
513 $sanitizedfieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y-%m')";
514 } elseif (preg_match('/\-day$/', $gvalsanitized)) {
515 $tmpval = preg_replace('/\-day$/', '', $gvalsanitized);
516 $sanitizedfieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y-%m-%d')";
517 } else {
518 $sanitizedfieldtocount = $gvalsanitized;
519 }
520
521 $fieldtocount = $sanitizedfieldtocount;
522 $sql = "SELECT DISTINCT ".$sanitizedfieldtocount." as val"; // $fieldtocount has been sanitized by previous lines as we can't use db->sanitize()
523
524 if (strpos($fieldtocount, 'te') === 0) {
525 $tabletouse = $object->table_element;
526 $tablealiastouse = 'te';
527 if (!empty($arrayofgroupby[$sanitized_gval])) {
528 $tmpval = explode('.', $sanitized_gval);
529 $tabletouse = $arrayofgroupby[$sanitized_gval]['table'];
530 $tablealiastouse = $tmpval[0];
531 }
532 //var_dump($tablealiastouse);exit;
533
534 //$sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element."_extrafields as te";
535 $sql .= " FROM ".MAIN_DB_PREFIX.$tabletouse."_extrafields as ".$tablealiastouse;
536 } else {
537 $tabletouse = $object->table_element;
538 $tablealiastouse = 't';
539 if (!empty($arrayofgroupby[$sanitized_gval])) {
540 $tmpval = explode('.', $sanitized_gval);
541 $tabletouse = $arrayofgroupby[$sanitized_gval]['table'];
542 $tablealiastouse = $tmpval[0];
543 }
544 $sql .= " FROM ".MAIN_DB_PREFIX.$tabletouse." as ".$tablealiastouse;
545 }
546
547 // Add a where here keeping only the criteria on $tabletouse
548 /* TODO
549 if ($search_component_params_hidden) {
550 $errormessage = '';
551 $sql .= forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage);
552 }
553 */
554
555 $sql .= " LIMIT ".((int) ($MAXUNIQUEVALFORGROUP + 1));
556
557 //print $sql;
558 $resql = $db->query($sql);
559 if (!$resql) {
560 dol_print_error($db);
561 }
562
563 while ($obj = $db->fetch_object($resql)) {
564 if (is_null($obj->val)) {
565 $keytouse = '__NULL__';
566 $valuetranslated = $langs->transnoentitiesnoconv("NotDefined");
567 } elseif ($obj->val === '') {
568 $keytouse = '';
569 $valuetranslated = $langs->transnoentitiesnoconv("Empty");
570 } else {
571 $keytouse = (string) $obj->val;
572 $valuetranslated = $obj->val;
573 }
574
575 $regs = array();
576 if (isset($object->fields[$gvalwithoutprefix])) {
577 if (!empty($object->fields[$gvalwithoutprefix]['arrayofkeyval'])) {
578 $valuetranslated = $object->fields[$gvalwithoutprefix]['arrayofkeyval'][$obj->val];
579 if (is_null($valuetranslated)) {
580 $valuetranslated = $langs->transnoentitiesnoconv("UndefinedKey");
581 }
582 $valuetranslated = $langs->trans($valuetranslated);
583 } elseif (preg_match('/integer:([^:]+):([^:]+)$/', $object->fields[$gvalwithoutprefix]['type'], $regs)) {
584 $classname = $regs[1];
585 $classpath = $regs[2];
586 dol_include_once($classpath);
587 if (class_exists($classname)) {
588 $tmpobject = new $classname($db);
589 '@phan-var-force CommonObject $tmpobject';
590 $tmpobject->fetch($obj->val);
591 foreach ($tmpobject->fields as $fieldkey => $field) {
592 if ($field['showoncombobox']) {
593 $valuetranslated = $tmpobject->$fieldkey;
594 //if ($valuetranslated == '-') $valuetranslated = $langs->transnoentitiesnoconv("Unknown")
595 break;
596 }
597 }
598 //$valuetranslated = $tmpobject->ref.'eee';
599 }
600 }
601 }
602
603 $arrayofvaluesforgroupby['g_'.$gkey][$keytouse] = $valuetranslated;
604 }
605 // Add also the possible NULL value if field is a parent field that is not a strict join
606 $tmpfield = explode('.', $sanitized_gval);
607 if ($tmpfield[0] != 't' || (isset($object->fields[$tmpfield[1]]) && is_array($object->fields[$tmpfield[1]]) && empty($object->fields[$tmpfield[1]]['notnull']))) {
608 dol_syslog("The group by field ".$sanitized_gval." may be null (because field is null or it is a left join), so we add __NULL__ entry in list of possible values");
609 //var_dump($sanitized_gval); var_dump($object->fields);
610 $arrayofvaluesforgroupby['g_'.$gkey]['__NULL__'] = $langs->transnoentitiesnoconv("NotDefined");
611 }
612
613 if (is_array($arrayofvaluesforgroupby['g_'.$gkey])) {
614 asort($arrayofvaluesforgroupby['g_'.$gkey]);
615 }
616
617 // Add a protection/error to refuse the request if number of differentr values for the group by is higher than $MAXUNIQUEVALFORGROUP
618 if (is_array($arrayofvaluesforgroupby['g_'.$gkey]) && count($arrayofvaluesforgroupby['g_'.$gkey]) > $MAXUNIQUEVALFORGROUP) {
619 $langs->load("errors");
620
621 if (strpos($fieldtocount, 'te') === 0) { // This is a field of an extrafield
622 //if (!empty($extrafields->attributes[$object->table_element]['langfile'][$gvalwithoutprefix])) {
623 // $langs->load($extrafields->attributes[$object->table_element]['langfile'][$gvalwithoutprefix]);
624 //}
625 $keyforlabeloffield = $extrafields->attributes[$object->table_element]['label'][$gvalwithoutprefix];
626 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
627 } elseif (strpos($fieldtocount, 't__') === 0) { // This is a field of a foreign key
628 $reg = array();
629 if (preg_match('/^(.*)\.(.*)/', $gvalwithoutprefix, $reg)) {
630 /*
631 $gvalwithoutprefix = preg_replace('/\..*$/', '', $gvalwithoutprefix);
632 $gvalwithoutprefix = preg_replace('/^t__/', '', $gvalwithoutprefix);
633 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
634 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield).'-'.$reg[2];
635 */
636 $labeloffield = $arrayofgroupby[$fieldtocount]['labelnohtml'];
637 } else {
638 $labeloffield = 'FK_ISSUE'; // $langs->transnoentitiesnoconv($keyforlabeloffield);
639 }
640 } else { // This is a common field
641 $reg = array();
642 if (preg_match('/^(.*)\-(year|month|day)/', $gvalwithoutprefix, $reg)) {
643 $gvalwithoutprefix = preg_replace('/\-(year|month|day)/', '', $gvalwithoutprefix);
644 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
645 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield).'-'.$reg[2];
646 } else {
647 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
648 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
649 }
650 }
651 //var_dump($labeloffield);
652 setEventMessages($langs->transnoentitiesnoconv("ErrorTooManyDifferentValueForSelectedGroupBy", (string) $MAXUNIQUEVALFORGROUP, (string) $labeloffield), null, 'warnings');
653 $sanitized_search_groupby = array();
654 }
655
656 $db->free($resql);
657 }
658}
659//var_dump($arrayofvaluesforgroupby);exit;
660
661
662if (!$resultcheck) {
663 print '<div class="error">';
664 print $langs->trans("NotEnoughPermissions");
665 print '</div>';
666}
667
668
669
670//$tmparray = dol_getdate(dol_now());
671//$endyear = $tmparray['year'];
672//$endmonth = $tmparray['mon'];
673//$datelastday = dol_get_last_day($endyear, $endmonth, 1);
674//$startyear = $endyear - 2;
675
676$param = '';
677
678
679if (!defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) {
680 print '<form method="post" action="'.$_SERVER['PHP_SELF'].'" autocomplete="off">';
681 print '<input type="hidden" name="token" value="'.newToken().'">';
682 print '<input type="hidden" name="action" value="viewgraph">';
683 print '<input type="hidden" name="tabfamily" value="'.(string) $tabfamily.'">';
684
685 $viewmode = '';
686
687 $viewmode .= '<div class="divadvancedsearchfield">';
688 $arrayofgraphs = array('bars' => 'Bars', 'lines' => 'Lines'); // also 'pies'
689 $viewmode .= '<div class="inline-block opacitymedium"><span class="fas fa-chart-area paddingright" title="'.$langs->trans("Graph").'"></span>'.$langs->trans("Graph").'</div> ';
690 // The class onrightofpage allows to align the popup list on the right of the select component (so it can't go out of screen on the right).
691 // For this the select must also be inside a parent with class parentonrightofpage (and a fixed width to avoid the parent to be enlarged when the dropdown list is open).
692 $viewmode .= '<div class="inline-block width100 parentonrightofpage">';
693 $viewmode .= $form->selectarray('search_graph', $arrayofgraphs, $search_graph, 0, 0, 0, '', 1, 0, 0, '', 'graphtype width100 onrightofpage');
694 $viewmode .= '</div>';
695 $viewmode .= '</div>';
696
697 $num = 0;
698 $massactionbutton = '';
699 $nav = '';
700 $newcardbutton = '';
701 $limit = 0;
702
703 print_barre_liste('', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, -1, 'object_action', 0, $nav.'<span class="marginleftonly"></span>'.$newcardbutton, '', $limit, 1, 0, 1, $viewmode);
704
705
706 foreach ($newarrayoftype as $tmpkey => $tmpval) {
707 $newarrayoftype[$tmpkey]['data-html'] = img_picto('', $tmpval['picto'], 'class="pictofixedwidth"').$langs->trans($tmpval['label']);
708 $newarrayoftype[$tmpkey]['label'] = $langs->trans($tmpval['label']);
709 }
710
711 print '<div class="liste_titre liste_titre_bydiv liste_titre_bydiv_inlineblock liste_titre_bydiv_nothingafter centpercent">';
712
713 // Select object
714 print '<div class="divadvancedsearchfield center floatnone">';
715 print '<div class="inline-block"><span class="opacitymedium">'.$langs->trans("StatisticsOn").'</span></div> ';
716
717 print $form->selectarray('objecttype', $newarrayoftype, $objecttype, 0, 0, 0, '', 1, 0, 0, '', 'minwidth250', 1, '', 0, 1);
718 if (empty($conf->use_javascript_ajax)) {
719 print '<input type="submit" class="button buttongen button-save nomargintop" name="changeobjecttype" value="'.$langs->trans("Refresh").'">';
720 } else {
721 print '<!-- js code to reload page with good object type -->
722 <script nonce="'.getNonce().'" type="text/javascript">
723 jQuery(document).ready(function() {
724 jQuery("#objecttype").change(function() {
725 console.log("Reload for "+jQuery("#objecttype").val());
726 location.href = "'.$_SERVER["PHP_SELF"].'?objecttype="+jQuery("#objecttype").val()+"'.($tabfamily ? '&tabfamily='.urlencode($tabfamily) : '').(GETPOSTINT('show_search_component_params_hidden') ? '&show_search_component_params_hidden='.((int) GETPOSTINT('show_search_component_params_hidden')) : '').'";
727 });
728 });
729 </script>';
730 }
731 print '</div><div class="clearboth"></div>';
732
733 if (!empty($newarrayoftype)) {
734 // Filter (you can use param &show_search_component_params_hidden=1 for debug)
735 if (!empty($object)) {
736 print '<div class="divadvancedsearchfield divadvancedsearchfieldusf">';
737 print $form->searchComponent(array($object->element => $object->fields), $search_component_params, array(), $search_component_params_hidden, $arrayoffilterfields);
738 print '</div>';
739 }
740
741 // YAxis (add measures into array)
742 $count = 0;
743 //var_dump($arrayofmesures);
744 print '<div class="divadvancedsearchfield clearboth">';
745 print '<div class="inline-block"><span class="fas fa-ruler-combined paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("Measures")).'"></span><span class="fas fa-caret-left caretleftaxis" title="'.dol_escape_htmltag($langs->trans("Measures")).'"></span></div>';
746 $simplearrayofmesures = array();
747 foreach ($arrayofmesures as $key => $val) {
748 $simplearrayofmesures[$key] = $arrayofmesures[$key]['label'];
749 }
750 print $form->multiselectarray('search_measures', $simplearrayofmesures, $sanitized_search_measures, 0, 0, 'minwidth200 maxwidth300 combolargelist', 1, 0, '', '', $langs->transnoentitiesnoconv("Measures")); // Fill the array $arrayofmeasures with possible fields
751 print '</div>';
752
753 // XAxis
754 $count = 0;
755 print '<div class="divadvancedsearchfield">';
756 print '<div class="inline-block"><span class="fas fa-ruler-combined paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("XAxis")).'"></span><span class="fas fa-caret-down caretdownaxis" title="'.dol_escape_htmltag($langs->trans("XAxis")).'"></span></div>';
757 //var_dump($arrayofxaxis);
758 print $formother->selectXAxisField($object, $sanitized_search_xaxis, $arrayofxaxis, $langs->trans("XAxis"), 'minwidth200 maxwidth300 combolargelist'); // Fill the array $arrayofxaxis with possible fields
759 print '</div>';
760
761 // Group by
762 $count = 0;
763 print '<div class="divadvancedsearchfield">';
764 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-horizontal paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("GroupBy")).'"></span></div>';
765 print $formother->selectGroupByField($object, $sanitized_search_groupby, $arrayofgroupby, 'minwidth200 maxwidth300 combolargelist', $langs->trans("GroupBy")); // Fill the array $arrayofgroupby with possible fields
766 print '</div>';
767 }
768
769 if ($mode == 'grid') {
770 // YAxis
771 print '<div class="divadvancedsearchfield">';
772 foreach ($object->fields as $key => $val) {
773 if (empty($val['measure']) && (!isset($val['enabled']) || dol_eval((string) $val['enabled'], 1, 1, '1'))) {
774 if (in_array($key, array('id', 'rowid', 'entity', 'last_main_doc', 'extraparams'))) {
775 continue;
776 }
777 if (preg_match('/^fk_/', $key)) {
778 continue;
779 }
780 if (in_array($val['type'], array('html', 'text'))) {
781 continue;
782 }
783 if (in_array($val['type'], array('timestamp', 'date', 'datetime'))) {
784 $arrayofyaxis['t.'.$key.'-year'] = array(
785 'label' => $langs->trans($val['label']).' ('.$YYYY.')',
786 'position' => $val['position'],
787 'table' => $object->table_element
788 );
789 $arrayofyaxis['t.'.$key.'-month'] = array(
790 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.')',
791 'position' => $val['position'],
792 'table' => $object->table_element
793 );
794 $arrayofyaxis['t.'.$key.'-day'] = array(
795 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.'-'.$DD.')',
796 'position' => $val['position'],
797 'table' => $object->table_element
798 );
799 } else {
800 $arrayofyaxis['t.'.$key] = array(
801 'label' => $val['label'],
802 'position' => (int) $val['position'],
803 'table' => $object->table_element
804 );
805 }
806 }
807 }
808 // Add measure from extrafields
809 if ($object->isextrafieldmanaged) {
810 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
811 if (!empty($extrafields->attributes[$object->table_element]['totalizable'][$key]) && (!isset($extrafields->attributes[$object->table_element]['enabled'][$key]) || dol_eval((string) $extrafields->attributes[$object->table_element]['enabled'][$key], 1, 1, '1'))) {
812 $arrayofyaxis['te.'.$key] = array(
813 'label' => $extrafields->attributes[$object->table_element]['label'][$key],
814 'position' => (int) $extrafields->attributes[$object->table_element]['pos'][$key],
815 'table' => $object->table_element
816 );
817 }
818 }
819 }
820 $arrayofyaxis = dol_sort_array($arrayofyaxis, 'position');
821 $arrayofyaxislabel = array();
822 foreach ($arrayofyaxis as $key => $val) {
823 $arrayofyaxislabel[$key] = $val['label'];
824 }
825 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-vertical paddingright" title="'.$langs->trans("YAxis").'"></span>'.$langs->trans("YAxis").'</div> ';
826 print $form->multiselectarray('search_yaxis', $arrayofyaxislabel, $sanitized_search_yaxis != null ? $sanitized_search_yaxis : array(), 0, 0, 'minwidth100', 1);
827 print '</div>';
828 }
829
830 if (!empty($newarrayoftype)) {
831 print '<div class="divadvancedsearchfield">';
832 print '<input type="submit" class="button buttongen button-save nomargintop" value="'.$langs->trans("Refresh").'">';
833 print '</div>';
834 }
835
836 print '</div>';
837 print '</form>';
838}
839
840// Generate the SQL request
841$sql = '';
842if (!empty($sanitized_search_measures) && !empty($sanitized_search_xaxis)) {
843 $errormessage = '';
844
845 $fieldid = 'rowid';
846
847 $sql = "SELECT ";
848 foreach ($sanitized_search_xaxis as $sql_key => $sql_val) {
849 if (preg_match('/\-year$/', $sql_val)) {
850 $sql_tmpval = preg_replace('/\-year$/', '', $sql_val);
851 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y') as x_".$sql_key.', ';
852 } elseif (preg_match('/\-month$/', $sql_val)) {
853 $sql_tmpval = preg_replace('/\-month$/', '', $sql_val);
854 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m') as x_".$sql_key.', ';
855 } elseif (preg_match('/\-day$/', $sql_val)) {
856 $sql_tmpval = preg_replace('/\-day$/', '', $sql_val);
857 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m-%d') as x_".$sql_key.', ';
858 } else {
859 $sql .= $sql_val." as x_".$sql_key.", ";
860 }
861 }
862 if (!empty($sanitized_search_groupby)) {
863 foreach ($sanitized_search_groupby as $sql_key => $sql_val) {
864 if (preg_match('/\-year$/', $sql_val)) {
865 $sql_tmpval = preg_replace('/\-year$/', '', $sql_val);
866 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y') as g_".$sql_key.', ';
867 } elseif (preg_match('/\-month$/', $sql_val)) {
868 $sql_tmpval = preg_replace('/\-month$/', '', $sql_val);
869 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m') as g_".$sql_key.', ';
870 } elseif (preg_match('/\-day$/', $sql_val)) {
871 $sql_tmpval = preg_replace('/\-day$/', '', $sql_val);
872 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m-%d') as g_".$sql_key.', ';
873 } else {
874 $sql .= $sql_val." as g_".$sql_key.", ";
875 }
876 }
877 }
878 foreach ($sanitized_search_measures as $sql_key => $sql_val) {
879 if ($sql_val == 't.count') {
880 $sql .= "COUNT(t.".$fieldid.") as y_".$sql_key.', ';
881 } elseif (preg_match('/\-sum$/', $sql_val)) {
882 $sql_tmpval = preg_replace('/\-sum$/', '', $sql_val);
883 $sql .= "SUM(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
884 } elseif (preg_match('/\-average$/', $sql_val)) {
885 $sql_tmpval = preg_replace('/\-average$/', '', $sql_val);
886 $sql .= "AVG(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
887 } elseif (preg_match('/\-min$/', $sql_val)) {
888 $sql_tmpval = preg_replace('/\-min$/', '', $sql_val);
889 $sql .= "MIN(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
890 } elseif (preg_match('/\-max$/', $sql_val)) {
891 $sql_tmpval = preg_replace('/\-max$/', '', $sql_val);
892 $sql .= "MAX(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
893 } elseif (preg_match('/\-stddevpop$/', $sql_val)) {
894 $sql_tmpval = preg_replace('/\-stddevpop$/', '', $sql_val);
895 $sql .= "STDDEV_POP(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
896 }
897 }
898 $sql = preg_replace('/,\s*$/', '', $sql);
899 $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
900 // Add measure from extrafields
901 if ($object->isextrafieldmanaged) {
902 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as te ON te.fk_object = t.".$fieldid;
903 }
904 // Add table for link on multientity
905 if ($object->ismultientitymanaged) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
906 if ($object->ismultientitymanaged == 1) {
907 // No table to add here
908 } else {
909 $tmparray = explode('@', $object->ismultientitymanaged);
910 $sql .= " INNER JOIN ".MAIN_DB_PREFIX.$db->sanitize($tmparray[1])." as parenttableforentity ON t.".$db->sanitize($tmparray[0])." = parenttableforentity.rowid";
911 $sql .= " AND parenttableforentity.entity IN (".getEntity($tmparray[1]).")";
912 }
913 }
914
915 // Init the list of tables added. We include by default always the main table.
916 $listoftablesalreadyadded = array($object->table_element => $object->table_element);
917
918 // Add LEFT JOIN for all parent tables mentioned into the Xaxis
919 //var_dump($arrayofxaxis); var_dump($sanitized_search_xaxis);
920 foreach ($sanitized_search_xaxis as $key => $val) {
921 if (!empty($arrayofxaxis[$val])) {
922 $tmpval = explode('.', $val);
923 //var_dump($arrayofgroupby);
924 $tmpforloop = dolExplodeIntoArray($arrayofxaxis[$val]['tablefromt'], ',');
925 foreach ($tmpforloop as $tmptable => $tmptablealias) {
926 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
927 $tmpforexplode = explode('__', $tmptablealias);
928 $endpart = end($tmpforexplode);
929 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
930
931 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
932 $listoftablesalreadyadded[$tmptable] = $tmptable;
933
934 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
935 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
936 $listoftablesalreadyadded[$tmptable] = $tmptable;
937 }
938 }
939 }
940 } else {
941 $errormessage = 'Found a key into search_xaxis not found into arrayofxaxis';
942 }
943 }
944
945 // Add LEFT JOIN for all parent tables mentioned into the Group by
946 //var_dump($arrayofgroupby); var_dump($sanitized_search_groupby);
947 foreach ($sanitized_search_groupby as $key => $val) {
948 if (!empty($arrayofgroupby[$val])) {
949 $tmpval = explode('.', $val);
950 //var_dump($arrayofgroupby[$val]); var_dump($tmpval);
951 $tmpforloop = dolExplodeIntoArray($arrayofgroupby[$val]['tablefromt'], ',');
952 foreach ($tmpforloop as $tmptable => $tmptablealias) {
953 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
954 $tmpforexplode = explode('__', $tmptablealias);
955 $endpart = end($tmpforexplode);
956 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
957
958 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
959 $listoftablesalreadyadded[$tmptable] = $tmptable;
960
961 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
962 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
963 $listoftablesalreadyadded[$tmptable] = $tmptable;
964 }
965 }
966 }
967 } else {
968 $errormessage = 'Found a key into search_groupby not found into arrayofgroupby';
969 }
970 }
971
972 // Add LEFT JOIN for all parent tables mentioned into the Yaxis
973 //var_dump($arrayofgroupby); var_dump($sanitized_search_groupby);
974 foreach ($sanitized_search_measures as $key => $val) {
975 if (!empty($arrayofmesures[$val])) {
976 $tmpval = explode('.', $val);
977 //var_dump($arrayofgroupby);
978 $tmpforloop = dolExplodeIntoArray($arrayofmesures[$val]['tablefromt'], ',');
979 foreach ($tmpforloop as $tmptable => $tmptablealias) {
980 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
981 $tmpforexplode = explode('__', $tmptablealias);
982 $endpart = end($tmpforexplode);
983 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
984
985 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
986 $listoftablesalreadyadded[$tmptable] = $tmptable;
987
988 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
989 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
990 $listoftablesalreadyadded[$tmptable] = $tmptable;
991 }
992 }
993 }
994 } else {
995 $errormessage = 'Found a key into search_measures not found into arrayofmesures';
996 }
997 }
998
999 // Add LEFT JOIN for all tables mentioned into filter
1000 if (!empty($search_component_params_hidden)) {
1001 // Get all fields used into the filter
1002 $matches = array();
1003 preg_match_all('/\b(t[\w]*_[\w]*)\.(\w+(-\w+)?)/', $search_component_params_hidden, $matches);
1004 $fieldsUsedInFilter = array_unique($matches[0]);
1005
1006 // Remove fields used before to avoid double join
1007 $fieldsToRemove = array_merge($sanitized_search_measures, $sanitized_search_groupby, $sanitized_search_xaxis);
1008 $fieldsUsedInFilter = array_diff($fieldsUsedInFilter, $fieldsToRemove);
1009
1010 foreach ($fieldsUsedInFilter as $key => $val) {
1011 if (!empty($arrayoffilterfields[$val])) {
1012 $tmpval = explode('.', $val);
1013 $tmpforloop = dolExplodeIntoArray($arrayoffilterfields[$val]['tablefromt'], ',');
1014 foreach ($tmpforloop as $tmptable => $tmptablealias) {
1015 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
1016 $tmpforexplode = explode('__', $tmptablealias);
1017 $endpart = end($tmpforexplode);
1018 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
1019
1020 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
1021 $listoftablesalreadyadded[$tmptable] = $tmptable;
1022
1023 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
1024 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
1025 $listoftablesalreadyadded[$tmptable] = $tmptable;
1026 }
1027 }
1028 }
1029 } else {
1030 $errormessage = 'Found a key into search_filterfields not found into arrayoffilterfields';
1031 }
1032 }
1033 }
1034
1035 $sql .= " WHERE 1 = 1";
1036 if ($object->ismultientitymanaged == 1) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
1037 $sql .= " AND t.entity IN (".getEntity($object->element).")";
1038 }
1039 // Add the where here
1040 if ($search_component_params_hidden) {
1041 $sql .= forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage, 0, 0, 1);
1042
1043 // Replace date values by $db->idate(dol_mktime(...)) @phan-suppress-next-line SqlInjection
1044 $sql = preg_replace_callback(
1045 "/(\w+)\.(\w+)\s*(=|!=|<>|<|>|<=|>=)\s*'(\d{4})-(\d{2})-(\d{2})'/",
1050 static function (array $matches): string {
1051 global $db;
1052 '@phan-var-force DoliDB $db';
1053 $column = $matches[1] . '.' . $matches[2];
1054 $operator = $matches[3];
1055 $year = (int) $matches[4];
1056 $month = (int) $matches[5];
1057 $day = (int) $matches[6];
1058
1059 $startOfDay = $db->idate(dol_mktime(0, 0, 0, $month, $day, $year));
1060 $endOfDay = $db->idate(dol_mktime(23, 59, 59, $month, $day, $year));
1061
1062 switch ($operator) {
1063 case "=":
1064 return "($column >= '$startOfDay' AND $column <= '$endOfDay')";
1065 case "!=":
1066 case "<>":
1067 return "NOT ($column >= '$startOfDay' AND $column <= '$endOfDay')";
1068 case "<":
1069 return "$column < '$startOfDay'";
1070 case ">":
1071 return "$column > '$endOfDay'";
1072 case "<=":
1073 return "$column <= '$endOfDay'";
1074 case ">=":
1075 return "$column >= '$startOfDay'";
1076 default:
1077 return "";
1078 }
1079 },
1080 $sql
1081 );
1082 }
1083 $sql .= " GROUP BY ";
1084 foreach ($sanitized_search_xaxis as $key => $sanitized_val) {
1085 if (preg_match('/\-year$/', $sanitized_val)) {
1086 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1087 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1088 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1089 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1090 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1091 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1092 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1093 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1094 } else {
1095 $sql .= $sanitized_val.", ";
1096 }
1097 }
1098 if (!empty($sanitized_search_groupby)) {
1099 foreach ($sanitized_search_groupby as $key => $sanitized_val) {
1100 if (preg_match('/\-year$/', $sanitized_val)) {
1101 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1102 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1103 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1104 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1105 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1106 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1107 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1108 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1109 } else {
1110 $sql .= $sanitized_val.', ';
1111 }
1112 }
1113 }
1114 $sql = preg_replace('/,\s*$/', '', $sql);
1115 $sql .= ' ORDER BY ';
1116 foreach ($sanitized_search_xaxis as $key => $sanitized_val) {
1117 if (preg_match('/\-year$/', $sanitized_val)) {
1118 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1119 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1120 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1121 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1122 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1123 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1124 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1125 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1126 } else {
1127 $sql .= $sanitized_val.', ';
1128 }
1129 }
1130 if (!empty($sanitized_search_groupby)) {
1131 foreach ($sanitized_search_groupby as $key => $sanitized_val) {
1132 if (preg_match('/\-year$/', $sanitized_val)) {
1133 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1134 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1135 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1136 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1137 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1138 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1139 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1140 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1141 } else {
1142 $sql .= $sanitized_val.', ';
1143 }
1144 }
1145 }
1146 $sql = preg_replace('/,\s*$/', '', $sql);
1147
1148 // Can overwrite the SQL with a custom SQL string (when used as an include)
1149 if (!empty($customsql)) {
1150 $sql = $customsql;
1151 }
1152}
1153//print $sql;
1154
1155if ($errormessage) {
1156 print '<div class="warning">';
1157 print dol_escape_htmltag($errormessage);
1158 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
1159 print '</div>';
1160 $sql = '';
1161}
1162
1163$legend = array();
1164foreach ($sanitized_search_measures as $key => $val) {
1165 $legend[] = $langs->trans($arrayofmesures[$val]['label']);
1166}
1167
1168$useagroupby = count($sanitized_search_groupby);
1169//var_dump($useagroupby);
1170//var_dump($arrayofvaluesforgroupby);
1171
1172// Execute the SQL request
1173$totalnbofrecord = 0;
1174$data = array();
1175if ($sql) {
1176 $resql = $db->query($sql);
1177 if (!$resql) {
1178 print '<div class="warning">';
1179 print dol_escape_htmltag($db->lasterror());
1180 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
1181 print '</div>';
1182 } else {
1183 $ifetch = 0;
1184 $xi = 0;
1185 $oldlabeltouse = '';
1186 while ($obj = $db->fetch_object($resql)) {
1187 $ifetch++;
1188 if ($useagroupby) {
1189 $xval = $sanitized_search_xaxis[0];
1190 $fieldforxkey = 'x_0';
1191 $xlabel = $obj->$fieldforxkey;
1192 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
1193
1194 // Define $xlabel
1195 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1196 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1197 }
1198 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1199
1200 if ($oldlabeltouse !== '' && ($labeltouse != $oldlabeltouse)) {
1201 $xi++; // Increase $xi
1202 }
1203 //var_dump($labeltouse.' '.$oldlabeltouse.' '.$xi);
1204 $oldlabeltouse = $labeltouse;
1205
1206 /* Example of value for $arrayofvaluesforgroupby
1207 * array (size=1)
1208 * 'g_0' =>
1209 * array (size=6)
1210 * 0 => string '0' (length=1)
1211 * '' => string 'Empty' (length=5)
1212 * '__NULL__' => string 'Not defined' (length=11)
1213 * 'done' => string 'done' (length=4)
1214 * 'processing' => string 'processing' (length=10)
1215 * 'undeployed' => string 'undeployed' (length=10)
1216 */
1217 foreach ($sanitized_search_measures as $key => $val) {
1218 $gi = 0;
1219 foreach ($sanitized_search_groupby as $gkey => $gval) {
1220 //var_dump('*** Fetch #'.$ifetch.' for labeltouse='.$labeltouse.' measure number '.$key.' and group g_'.$gi);
1221 //var_dump($arrayofvaluesforgroupby);
1222 foreach ($arrayofvaluesforgroupby['g_'.$gi] as $gvaluepossiblekey => $gvaluepossiblelabel) {
1223 $ykeysuffix = $gvaluepossiblelabel;
1224 $gvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $gval);
1225
1226 $fieldfory = 'y_'.$key;
1227 $fieldforg = 'g_'.$gi;
1228 $fieldforybis = 'y_'.$key.'_'.$ykeysuffix;
1229 //var_dump('gvaluepossiblekey='.$gvaluepossiblekey.' gvaluepossiblelabel='.$gvaluepossiblelabel.' ykeysuffix='.$ykeysuffix.' gval='.$gval.' gvalwithoutsuffix='.$gvalwithoutprefix);
1230 //var_dump('fieldforg='.$fieldforg.' obj->$fieldforg='.$obj->$fieldforg.' fieldfory='.$fieldfory.' obj->$fieldfory='.$obj->$fieldfory.' fieldforybis='.$fieldforybis);
1231
1232 if (!array_key_exists($xi, $data)) {
1233 $data[$xi] = array();
1234 }
1235
1236 if (!array_key_exists('label', $data[$xi])) {
1237 $data[$xi] = array();
1238 $data[$xi]['label'] = $labeltouse;
1239 }
1240
1241 $objfieldforg = $obj->$fieldforg;
1242 if (is_null($objfieldforg)) {
1243 $objfieldforg = '__NULL__';
1244 }
1245
1246 if ($gvaluepossiblekey == '0') { // $gvaluepossiblekey can have type int or string. So we create a special if, used when value is '0'
1247 //var_dump($objfieldforg.' == \'0\' -> '.($objfieldforg == '0'));
1248 if ($objfieldforg == '0') {
1249 // The record we fetch is for this group
1250 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1251 } elseif (!isset($data[$xi][$fieldforybis])) {
1252 // The record we fetch is not for this group
1253 $data[$xi][$fieldforybis] = '0';
1254 }
1255 } else {
1256 //var_dump((string) $objfieldforg.' === '.(string) $gvaluepossiblekey.' -> '.((string) $objfieldforg === (string) $gvaluepossiblekey));
1257 if ((string) $objfieldforg === (string) $gvaluepossiblekey) {
1258 // The record we fetch is for this group
1259 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1260 } elseif (!isset($data[$xi][$fieldforybis])) {
1261 // The record we fetch is not for this group
1262 $data[$xi][$fieldforybis] = '0';
1263 }
1264 }
1265 }
1266 //var_dump($data[$xi]);
1267 $gi++;
1268 }
1269 }
1270 } else { // No group by
1271 $xval = $sanitized_search_xaxis[0];
1272 $fieldforxkey = 'x_0';
1273 $xlabel = $obj->$fieldforxkey;
1274 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
1275
1276 // Define $xlabel
1277 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1278 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1279 }
1280
1281 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1282 $xarrayforallseries = array('label' => $labeltouse);
1283 foreach ($sanitized_search_measures as $key => $val) {
1284 $fieldfory = 'y_'.$key;
1285 $xarrayforallseries[$fieldfory] = $obj->$fieldfory;
1286 }
1287 $data[$xi] = $xarrayforallseries;
1288 $xi++;
1289 }
1290 }
1291
1292 $totalnbofrecord = count($data);
1293 }
1294}
1295//var_dump($data);
1296
1297print '<!-- Section to show the result -->'."\n";
1298print '<div class="customreportsoutput'.($totalnbofrecord ? '' : ' customreportsoutputnotdata').'">';
1299
1300if (empty($newarrayoftype)) {
1301 $langs->load("admin");
1302 print info_admin($langs->trans("NoSupportedModulesHaveBeenActivated").' '.$langs->trans("YouCanEnableModulesFrom"), 0, 0, 'info');
1303}
1304
1305if ($mode == 'grid') {
1306 // TODO
1307}
1308
1309if ($mode == 'graph') {
1310 $WIDTH = '80%';
1311 $HEIGHT = (empty($_SESSION['dol_screenheight']) ? 400 : $_SESSION['dol_screenheight'] - 500);
1312
1313 // Show graph
1314 $px1 = new DolGraph();
1315 $mesg = $px1->isGraphKo();
1316 if (!$mesg) {
1317 //var_dump($legend);
1318 //var_dump($data);
1319 $px1->SetData($data);
1320 unset($data);
1321
1322 $arrayoftypes = array();
1323 foreach ($sanitized_search_measures as $key => $val) {
1324 $arrayoftypes[] = $search_graph;
1325 }
1326
1327 $px1->SetLegend($legend);
1328 $px1->setShowLegend($SHOWLEGEND);
1329 $px1->SetMinValue((int) $px1->GetFloorMinValue());
1330 $px1->SetMaxValue($px1->GetCeilMaxValue());
1331 $px1->SetWidth($WIDTH);
1332 $px1->SetHeight($HEIGHT);
1333 $px1->SetYLabel($langs->trans("Y"));
1334 $px1->SetShading(3);
1335 $px1->SetHorizTickIncrement(1);
1336 $px1->SetCssPrefix("cssboxes");
1337 $px1->SetType($arrayoftypes);
1338 $px1->mode = 'depth';
1339 $px1->SetTitle('');
1340
1341 $dir = $conf->user->dir_temp;
1342 dol_mkdir($dir);
1343 // $customreportkey may be defined when using customreports.php as an include
1344 if (!empty($object->element)) {
1345 $filenamekey = $dir.'/customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1346 $fileurlkey = DOL_URL_ROOT.'/viewimage.php?modulepart=user&file=customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1347 }
1348
1349 if (isset($filenamekey) && isset($fileurlkey)) {
1350 $px1->draw($filenamekey, $fileurlkey);
1351 }
1352
1353 $texttoshow = $langs->trans("NoRecordFound");
1354 if (!GETPOSTISSET('search_measures') || !GETPOSTISSET('search_xaxis')) {
1355 $texttoshow = $langs->trans("SelectYourGraphOptionsFirst");
1356 }
1357
1358 print $px1->show($totalnbofrecord ? 0 : $texttoshow);
1359 }
1360}
1361
1362print '</div>';
1363
1364if ($sql && !defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) {
1365 // Show admin info
1366 print '<br>'.info_admin($langs->trans("SQLUsedForExport").':<br> '.$sql, 0, 0, '1', '', 'TechnicalInformation');
1367}
1368
1369
1370if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
1371 print dol_get_fiche_end();
1372
1373 llxFooter();
1374 // End of page
1375
1376 $db->close();
1377}
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
Class to build graphs.
Class to manage standard extra fields.
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.
fillArrayOfGroupBy($object, $tablealias, $labelofobject, &$arrayofgroupby, $level=0, &$count=0, &$tablepath='')
Fill arrayofgroupby for an object.
fillArrayOfFilterFields($object, $tablealias, $labelofobject, &$arrayoffields, $level=0, &$count=0, &$tablepath='')
Fill array of possible filter fields for an object.
fillArrayOfMeasures($object, $tablealias, $labelofobject, &$arrayofmesures, $level=0, &$count=0, &$tablepath='')
Fill arrayofmesures for an object.
fillArrayOfXAxis($object, $tablealias, $labelofobject, &$arrayofxaxis, $level=0, &$count=0, &$tablepath='')
Fill arrayofmesures for an object.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
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...
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
dolExplodeIntoArray($string, $delimiter=';', $kv='=')
Split a string with 2 keys into key array.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_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.
Definition html.lib.php:540
dol_get_fiche_end($notab=0)
Return tab footer of a card.
Definition html.lib.php:738
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Definition html.lib.php:181
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $nodie=0, $mode='')
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.