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 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
191$langs->loadLangs(array("companies", "other", "exports", "sendings"));
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 ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 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) {
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 $viewmode .= $form->selectarray('search_graph', $arrayofgraphs, $search_graph, 0, 0, 0, '', 1, 0, 0, '', 'graphtype width100');
691 $viewmode .= '</div>';
692
693 $num = 0;
694 $massactionbutton = '';
695 $nav = '';
696 $newcardbutton = '';
697 $limit = 0;
698
699 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);
700
701
702 foreach ($newarrayoftype as $tmpkey => $tmpval) {
703 $newarrayoftype[$tmpkey]['data-html'] = img_picto('', $tmpval['picto'], 'class="pictofixedwidth"').$langs->trans($tmpval['label']);
704 $newarrayoftype[$tmpkey]['label'] = $langs->trans($tmpval['label']);
705 }
706
707 print '<div class="liste_titre liste_titre_bydiv liste_titre_bydiv_inlineblock liste_titre_bydiv_nothingafter centpercent">';
708
709 // Select object
710 print '<div class="divadvancedsearchfield center floatnone">';
711 print '<div class="inline-block"><span class="opacitymedium">'.$langs->trans("StatisticsOn").'</span></div> ';
712
713 print $form->selectarray('objecttype', $newarrayoftype, $objecttype, 0, 0, 0, '', 1, 0, 0, '', 'minwidth250', 1, '', 0, 1);
714 if (empty($conf->use_javascript_ajax)) {
715 print '<input type="submit" class="button buttongen button-save nomargintop" name="changeobjecttype" value="'.$langs->trans("Refresh").'">';
716 } else {
717 print '<!-- js code to reload page with good object type -->
718 <script nonce="'.getNonce().'" type="text/javascript">
719 jQuery(document).ready(function() {
720 jQuery("#objecttype").change(function() {
721 console.log("Reload for "+jQuery("#objecttype").val());
722 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')) : '').'";
723 });
724 });
725 </script>';
726 }
727 print '</div><div class="clearboth"></div>';
728
729 if (!empty($newarrayoftype)) {
730 // Filter (you can use param &show_search_component_params_hidden=1 for debug)
731 if (!empty($object)) {
732 print '<div class="divadvancedsearchfield">';
733 print $form->searchComponent(array($object->element => $object->fields), $search_component_params, array(), $search_component_params_hidden, $arrayoffilterfields);
734 print '</div>';
735 }
736
737 // YAxis (add measures into array)
738 $count = 0;
739 //var_dump($arrayofmesures);
740 print '<div class="divadvancedsearchfield clearboth">';
741 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>';
742 $simplearrayofmesures = array();
743 foreach ($arrayofmesures as $key => $val) {
744 $simplearrayofmesures[$key] = $arrayofmesures[$key]['label'];
745 }
746 print $form->multiselectarray('search_measures', $simplearrayofmesures, $sanitized_search_measures, 0, 0, 'minwidth300 widthcentpercentminusx', 1, 0, '', '', $langs->transnoentitiesnoconv("Measures")); // Fill the array $arrayofmeasures with possible fields
747 print '</div>';
748
749 // XAxis
750 $count = 0;
751 print '<div class="divadvancedsearchfield">';
752 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>';
753 //var_dump($arrayofxaxis);
754 print $formother->selectXAxisField($object, $sanitized_search_xaxis, $arrayofxaxis, $langs->trans("XAxis"), 'minwidth300 maxwidth400 widthcentpercentminusx'); // Fill the array $arrayofxaxis with possible fields
755 print '</div>';
756
757 // Group by
758 $count = 0;
759 print '<div class="divadvancedsearchfield">';
760 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-horizontal paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("GroupBy")).'"></span></div>';
761 print $formother->selectGroupByField($object, $sanitized_search_groupby, $arrayofgroupby, 'minwidth250 maxwidth300 widthcentpercentminusx', $langs->trans("GroupBy")); // Fill the array $arrayofgroupby with possible fields
762 print '</div>';
763 }
764
765 if ($mode == 'grid') {
766 // YAxis
767 print '<div class="divadvancedsearchfield">';
768 foreach ($object->fields as $key => $val) {
769 if (empty($val['measure']) && (!isset($val['enabled']) || dol_eval((string) $val['enabled'], 1, 1, '1'))) {
770 if (in_array($key, array('id', 'rowid', 'entity', 'last_main_doc', 'extraparams'))) {
771 continue;
772 }
773 if (preg_match('/^fk_/', $key)) {
774 continue;
775 }
776 if (in_array($val['type'], array('html', 'text'))) {
777 continue;
778 }
779 if (in_array($val['type'], array('timestamp', 'date', 'datetime'))) {
780 $arrayofyaxis['t.'.$key.'-year'] = array(
781 'label' => $langs->trans($val['label']).' ('.$YYYY.')',
782 'position' => $val['position'],
783 'table' => $object->table_element
784 );
785 $arrayofyaxis['t.'.$key.'-month'] = array(
786 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.')',
787 'position' => $val['position'],
788 'table' => $object->table_element
789 );
790 $arrayofyaxis['t.'.$key.'-day'] = array(
791 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.'-'.$DD.')',
792 'position' => $val['position'],
793 'table' => $object->table_element
794 );
795 } else {
796 $arrayofyaxis['t.'.$key] = array(
797 'label' => $val['label'],
798 'position' => (int) $val['position'],
799 'table' => $object->table_element
800 );
801 }
802 }
803 }
804 // Add measure from extrafields
805 if ($object->isextrafieldmanaged) {
806 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
807 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'))) {
808 $arrayofyaxis['te.'.$key] = array(
809 'label' => $extrafields->attributes[$object->table_element]['label'][$key],
810 'position' => (int) $extrafields->attributes[$object->table_element]['pos'][$key],
811 'table' => $object->table_element
812 );
813 }
814 }
815 }
816 $arrayofyaxis = dol_sort_array($arrayofyaxis, 'position');
817 $arrayofyaxislabel = array();
818 foreach ($arrayofyaxis as $key => $val) {
819 $arrayofyaxislabel[$key] = $val['label'];
820 }
821 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-vertical paddingright" title="'.$langs->trans("YAxis").'"></span>'.$langs->trans("YAxis").'</div> ';
822 print $form->multiselectarray('search_yaxis', $arrayofyaxislabel, $sanitized_search_yaxis != null ? $sanitized_search_yaxis : array(), 0, 0, 'minwidth100', 1);
823 print '</div>';
824 }
825
826 if (!empty($newarrayoftype)) {
827 print '<div class="divadvancedsearchfield">';
828 print '<input type="submit" class="button buttongen button-save nomargintop" value="'.$langs->trans("Refresh").'">';
829 print '</div>';
830 }
831
832 print '</div>';
833 print '</form>';
834}
835
836// Generate the SQL request
837$sql = '';
838if (!empty($sanitized_search_measures) && !empty($sanitized_search_xaxis)) {
839 $errormessage = '';
840
841 $fieldid = 'rowid';
842
843 $sql = "SELECT ";
844 foreach ($sanitized_search_xaxis as $sql_key => $sql_val) {
845 if (preg_match('/\-year$/', $sql_val)) {
846 $sql_tmpval = preg_replace('/\-year$/', '', $sql_val);
847 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y') as x_".$sql_key.', ';
848 } elseif (preg_match('/\-month$/', $sql_val)) {
849 $sql_tmpval = preg_replace('/\-month$/', '', $sql_val);
850 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m') as x_".$sql_key.', ';
851 } elseif (preg_match('/\-day$/', $sql_val)) {
852 $sql_tmpval = preg_replace('/\-day$/', '', $sql_val);
853 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m-%d') as x_".$sql_key.', ';
854 } else {
855 $sql .= $sql_val." as x_".$sql_key.", ";
856 }
857 }
858 if (!empty($sanitized_search_groupby)) {
859 foreach ($sanitized_search_groupby as $sql_key => $sql_val) {
860 if (preg_match('/\-year$/', $sql_val)) {
861 $sql_tmpval = preg_replace('/\-year$/', '', $sql_val);
862 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y') as g_".$sql_key.', ';
863 } elseif (preg_match('/\-month$/', $sql_val)) {
864 $sql_tmpval = preg_replace('/\-month$/', '', $sql_val);
865 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m') as g_".$sql_key.', ';
866 } elseif (preg_match('/\-day$/', $sql_val)) {
867 $sql_tmpval = preg_replace('/\-day$/', '', $sql_val);
868 $sql .= "DATE_FORMAT(".$sql_tmpval.", '%Y-%m-%d') as g_".$sql_key.', ';
869 } else {
870 $sql .= $sql_val." as g_".$sql_key.", ";
871 }
872 }
873 }
874 foreach ($sanitized_search_measures as $sql_key => $sql_val) {
875 if ($sql_val == 't.count') {
876 $sql .= "COUNT(t.".$fieldid.") as y_".$sql_key.', ';
877 } elseif (preg_match('/\-sum$/', $sql_val)) {
878 $sql_tmpval = preg_replace('/\-sum$/', '', $sql_val);
879 $sql .= "SUM(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
880 } elseif (preg_match('/\-average$/', $sql_val)) {
881 $sql_tmpval = preg_replace('/\-average$/', '', $sql_val);
882 $sql .= "AVG(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
883 } elseif (preg_match('/\-min$/', $sql_val)) {
884 $sql_tmpval = preg_replace('/\-min$/', '', $sql_val);
885 $sql .= "MIN(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
886 } elseif (preg_match('/\-max$/', $sql_val)) {
887 $sql_tmpval = preg_replace('/\-max$/', '', $sql_val);
888 $sql .= "MAX(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
889 } elseif (preg_match('/\-stddevpop$/', $sql_val)) {
890 $sql_tmpval = preg_replace('/\-stddevpop$/', '', $sql_val);
891 $sql .= "STDDEV_POP(".$db->ifsql($sql_tmpval.' IS NULL', '0', $sql_tmpval).") as y_".$sql_key.", ";
892 }
893 }
894 $sql = preg_replace('/,\s*$/', '', $sql);
895 $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
896 // Add measure from extrafields
897 if ($object->isextrafieldmanaged) {
898 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as te ON te.fk_object = t.".$fieldid;
899 }
900 // Add table for link on multientity
901 if ($object->ismultientitymanaged) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
902 if ($object->ismultientitymanaged == 1) {
903 // No table to add here
904 } else {
905 $tmparray = explode('@', $object->ismultientitymanaged);
906 $sql .= " INNER JOIN ".MAIN_DB_PREFIX.$db->sanitize($tmparray[1])." as parenttableforentity ON t.".$db->sanitize($tmparray[0])." = parenttableforentity.rowid";
907 $sql .= " AND parenttableforentity.entity IN (".getEntity($tmparray[1]).")";
908 }
909 }
910
911 // Init the list of tables added. We include by default always the main table.
912 $listoftablesalreadyadded = array($object->table_element => $object->table_element);
913
914 // Add LEFT JOIN for all parent tables mentioned into the Xaxis
915 //var_dump($arrayofxaxis); var_dump($sanitized_search_xaxis);
916 foreach ($sanitized_search_xaxis as $key => $val) {
917 if (!empty($arrayofxaxis[$val])) {
918 $tmpval = explode('.', $val);
919 //var_dump($arrayofgroupby);
920 $tmpforloop = dolExplodeIntoArray($arrayofxaxis[$val]['tablefromt'], ',');
921 foreach ($tmpforloop as $tmptable => $tmptablealias) {
922 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
923 $tmpforexplode = explode('__', $tmptablealias);
924 $endpart = end($tmpforexplode);
925 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
926
927 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
928 $listoftablesalreadyadded[$tmptable] = $tmptable;
929
930 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
931 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
932 $listoftablesalreadyadded[$tmptable] = $tmptable;
933 }
934 }
935 }
936 } else {
937 $errormessage = 'Found a key into search_xaxis not found into arrayofxaxis';
938 }
939 }
940
941 // Add LEFT JOIN for all parent tables mentioned into the Group by
942 //var_dump($arrayofgroupby); var_dump($sanitized_search_groupby);
943 foreach ($sanitized_search_groupby as $key => $val) {
944 if (!empty($arrayofgroupby[$val])) {
945 $tmpval = explode('.', $val);
946 //var_dump($arrayofgroupby[$val]); var_dump($tmpval);
947 $tmpforloop = dolExplodeIntoArray($arrayofgroupby[$val]['tablefromt'], ',');
948 foreach ($tmpforloop as $tmptable => $tmptablealias) {
949 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
950 $tmpforexplode = explode('__', $tmptablealias);
951 $endpart = end($tmpforexplode);
952 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
953
954 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
955 $listoftablesalreadyadded[$tmptable] = $tmptable;
956
957 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
958 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
959 $listoftablesalreadyadded[$tmptable] = $tmptable;
960 }
961 }
962 }
963 } else {
964 $errormessage = 'Found a key into search_groupby not found into arrayofgroupby';
965 }
966 }
967
968 // Add LEFT JOIN for all parent tables mentioned into the Yaxis
969 //var_dump($arrayofgroupby); var_dump($sanitized_search_groupby);
970 foreach ($sanitized_search_measures as $key => $val) {
971 if (!empty($arrayofmesures[$val])) {
972 $tmpval = explode('.', $val);
973 //var_dump($arrayofgroupby);
974 $tmpforloop = dolExplodeIntoArray($arrayofmesures[$val]['tablefromt'], ',');
975 foreach ($tmpforloop as $tmptable => $tmptablealias) {
976 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
977 $tmpforexplode = explode('__', $tmptablealias);
978 $endpart = end($tmpforexplode);
979 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
980
981 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
982 $listoftablesalreadyadded[$tmptable] = $tmptable;
983
984 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
985 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
986 $listoftablesalreadyadded[$tmptable] = $tmptable;
987 }
988 }
989 }
990 } else {
991 $errormessage = 'Found a key into search_measures not found into arrayofmesures';
992 }
993 }
994
995 // Add LEFT JOIN for all tables mentioned into filter
996 if (!empty($search_component_params_hidden)) {
997 // Get all fields used into the filter
998 $matches = array();
999 preg_match_all('/\b(t[\w]*_[\w]*)\.(\w+(-\w+)?)/', $search_component_params_hidden, $matches);
1000 $fieldsUsedInFilter = array_unique($matches[0]);
1001
1002 // Remove fields used before to avoid double join
1003 $fieldsToRemove = array_merge($sanitized_search_measures, $sanitized_search_groupby, $sanitized_search_xaxis);
1004 $fieldsUsedInFilter = array_diff($fieldsUsedInFilter, $fieldsToRemove);
1005
1006 foreach ($fieldsUsedInFilter as $key => $val) {
1007 if (!empty($arrayoffilterfields[$val])) {
1008 $tmpval = explode('.', $val);
1009 $tmpforloop = dolExplodeIntoArray($arrayoffilterfields[$val]['tablefromt'], ',');
1010 foreach ($tmpforloop as $tmptable => $tmptablealias) {
1011 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
1012 $tmpforexplode = explode('__', $tmptablealias);
1013 $endpart = end($tmpforexplode);
1014 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
1015
1016 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
1017 $listoftablesalreadyadded[$tmptable] = $tmptable;
1018
1019 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
1020 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
1021 $listoftablesalreadyadded[$tmptable] = $tmptable;
1022 }
1023 }
1024 }
1025 } else {
1026 $errormessage = 'Found a key into search_filterfields not found into arrayoffilterfields';
1027 }
1028 }
1029 }
1030
1031 $sql .= " WHERE 1 = 1";
1032 if ($object->ismultientitymanaged == 1) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
1033 $sql .= " AND t.entity IN (".getEntity($object->element).")";
1034 }
1035 // Add the where here
1036 if ($search_component_params_hidden) {
1037 $sql .= forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage, 0, 0, 1);
1038
1039 // Replace date values by $db->idate(dol_mktime(...)) @phan-suppress-next-line SqlInjection
1040 $sql = preg_replace_callback(
1041 "/(\w+)\.(\w+)\s*(=|!=|<>|<|>|<=|>=)\s*'(\d{4})-(\d{2})-(\d{2})'/",
1046 static function (array $matches): string {
1047 global $db;
1048 '@phan-var-force DoliDB $db';
1049 $column = $matches[1] . '.' . $matches[2];
1050 $operator = $matches[3];
1051 $year = (int) $matches[4];
1052 $month = (int) $matches[5];
1053 $day = (int) $matches[6];
1054
1055 $startOfDay = $db->idate(dol_mktime(0, 0, 0, $month, $day, $year));
1056 $endOfDay = $db->idate(dol_mktime(23, 59, 59, $month, $day, $year));
1057
1058 switch ($operator) {
1059 case "=":
1060 return "($column >= '$startOfDay' AND $column <= '$endOfDay')";
1061 case "!=":
1062 case "<>":
1063 return "NOT ($column >= '$startOfDay' AND $column <= '$endOfDay')";
1064 case "<":
1065 return "$column < '$startOfDay'";
1066 case ">":
1067 return "$column > '$endOfDay'";
1068 case "<=":
1069 return "$column <= '$endOfDay'";
1070 case ">=":
1071 return "$column >= '$startOfDay'";
1072 default:
1073 return "";
1074 }
1075 },
1076 $sql
1077 );
1078 }
1079 $sql .= " GROUP BY ";
1080 foreach ($sanitized_search_xaxis as $key => $sanitized_val) {
1081 if (preg_match('/\-year$/', $sanitized_val)) {
1082 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1083 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1084 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1085 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1086 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1087 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1088 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1089 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1090 } else {
1091 $sql .= $sanitized_val.", ";
1092 }
1093 }
1094 if (!empty($sanitized_search_groupby)) {
1095 foreach ($sanitized_search_groupby as $key => $sanitized_val) {
1096 if (preg_match('/\-year$/', $sanitized_val)) {
1097 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1098 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1099 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1100 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1101 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1102 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1103 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1104 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1105 } else {
1106 $sql .= $sanitized_val.', ';
1107 }
1108 }
1109 }
1110 $sql = preg_replace('/,\s*$/', '', $sql);
1111 $sql .= ' ORDER BY ';
1112 foreach ($sanitized_search_xaxis as $key => $sanitized_val) {
1113 if (preg_match('/\-year$/', $sanitized_val)) {
1114 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1115 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1116 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1117 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1118 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1119 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1120 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1121 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1122 } else {
1123 $sql .= $sanitized_val.', ';
1124 }
1125 }
1126 if (!empty($sanitized_search_groupby)) {
1127 foreach ($sanitized_search_groupby as $key => $sanitized_val) {
1128 if (preg_match('/\-year$/', $sanitized_val)) {
1129 $sanitized_tmpval = preg_replace('/\-year$/', '', $sanitized_val);
1130 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y'), ";
1131 } elseif (preg_match('/\-month$/', $sanitized_val)) {
1132 $sanitized_tmpval = preg_replace('/\-month$/', '', $sanitized_val);
1133 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m'), ";
1134 } elseif (preg_match('/\-day$/', $sanitized_val)) {
1135 $sanitized_tmpval = preg_replace('/\-day$/', '', $sanitized_val);
1136 $sql .= "DATE_FORMAT(".$sanitized_tmpval.", '%Y-%m-%d'), ";
1137 } else {
1138 $sql .= $sanitized_val.', ';
1139 }
1140 }
1141 }
1142 $sql = preg_replace('/,\s*$/', '', $sql);
1143
1144 // Can overwrite the SQL with a custom SQL string (when used as an include)
1145 if (!empty($customsql)) {
1146 $sql = $customsql;
1147 }
1148}
1149//print $sql;
1150
1151if ($errormessage) {
1152 print '<div class="warning">';
1153 print dol_escape_htmltag($errormessage);
1154 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
1155 print '</div>';
1156 $sql = '';
1157}
1158
1159$legend = array();
1160foreach ($sanitized_search_measures as $key => $val) {
1161 $legend[] = $langs->trans($arrayofmesures[$val]['label']);
1162}
1163
1164$useagroupby = count($sanitized_search_groupby);
1165//var_dump($useagroupby);
1166//var_dump($arrayofvaluesforgroupby);
1167
1168// Execute the SQL request
1169$totalnbofrecord = 0;
1170$data = array();
1171if ($sql) {
1172 $resql = $db->query($sql);
1173 if (!$resql) {
1174 print '<div class="warning">';
1175 print dol_escape_htmltag($db->lasterror());
1176 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
1177 print '</div>';
1178 } else {
1179 $ifetch = 0;
1180 $xi = 0;
1181 $oldlabeltouse = '';
1182 while ($obj = $db->fetch_object($resql)) {
1183 $ifetch++;
1184 if ($useagroupby) {
1185 $xval = $sanitized_search_xaxis[0];
1186 $fieldforxkey = 'x_0';
1187 $xlabel = $obj->$fieldforxkey;
1188 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
1189
1190 // Define $xlabel
1191 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1192 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1193 }
1194 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1195
1196 if ($oldlabeltouse !== '' && ($labeltouse != $oldlabeltouse)) {
1197 $xi++; // Increase $xi
1198 }
1199 //var_dump($labeltouse.' '.$oldlabeltouse.' '.$xi);
1200 $oldlabeltouse = $labeltouse;
1201
1202 /* Example of value for $arrayofvaluesforgroupby
1203 * array (size=1)
1204 * 'g_0' =>
1205 * array (size=6)
1206 * 0 => string '0' (length=1)
1207 * '' => string 'Empty' (length=5)
1208 * '__NULL__' => string 'Not defined' (length=11)
1209 * 'done' => string 'done' (length=4)
1210 * 'processing' => string 'processing' (length=10)
1211 * 'undeployed' => string 'undeployed' (length=10)
1212 */
1213 foreach ($sanitized_search_measures as $key => $val) {
1214 $gi = 0;
1215 foreach ($sanitized_search_groupby as $gkey => $gval) {
1216 //var_dump('*** Fetch #'.$ifetch.' for labeltouse='.$labeltouse.' measure number '.$key.' and group g_'.$gi);
1217 //var_dump($arrayofvaluesforgroupby);
1218 foreach ($arrayofvaluesforgroupby['g_'.$gi] as $gvaluepossiblekey => $gvaluepossiblelabel) {
1219 $ykeysuffix = $gvaluepossiblelabel;
1220 $gvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $gval);
1221
1222 $fieldfory = 'y_'.$key;
1223 $fieldforg = 'g_'.$gi;
1224 $fieldforybis = 'y_'.$key.'_'.$ykeysuffix;
1225 //var_dump('gvaluepossiblekey='.$gvaluepossiblekey.' gvaluepossiblelabel='.$gvaluepossiblelabel.' ykeysuffix='.$ykeysuffix.' gval='.$gval.' gvalwithoutsuffix='.$gvalwithoutprefix);
1226 //var_dump('fieldforg='.$fieldforg.' obj->$fieldforg='.$obj->$fieldforg.' fieldfory='.$fieldfory.' obj->$fieldfory='.$obj->$fieldfory.' fieldforybis='.$fieldforybis);
1227
1228 if (!array_key_exists($xi, $data)) {
1229 $data[$xi] = array();
1230 }
1231
1232 if (!array_key_exists('label', $data[$xi])) {
1233 $data[$xi] = array();
1234 $data[$xi]['label'] = $labeltouse;
1235 }
1236
1237 $objfieldforg = $obj->$fieldforg;
1238 if (is_null($objfieldforg)) {
1239 $objfieldforg = '__NULL__';
1240 }
1241
1242 if ($gvaluepossiblekey == '0') { // $gvaluepossiblekey can have type int or string. So we create a special if, used when value is '0'
1243 //var_dump($objfieldforg.' == \'0\' -> '.($objfieldforg == '0'));
1244 if ($objfieldforg == '0') {
1245 // The record we fetch is for this group
1246 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1247 } elseif (!isset($data[$xi][$fieldforybis])) {
1248 // The record we fetch is not for this group
1249 $data[$xi][$fieldforybis] = '0';
1250 }
1251 } else {
1252 //var_dump((string) $objfieldforg.' === '.(string) $gvaluepossiblekey.' -> '.((string) $objfieldforg === (string) $gvaluepossiblekey));
1253 if ((string) $objfieldforg === (string) $gvaluepossiblekey) {
1254 // The record we fetch is for this group
1255 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1256 } elseif (!isset($data[$xi][$fieldforybis])) {
1257 // The record we fetch is not for this group
1258 $data[$xi][$fieldforybis] = '0';
1259 }
1260 }
1261 }
1262 //var_dump($data[$xi]);
1263 $gi++;
1264 }
1265 }
1266 } else { // No group by
1267 $xval = $sanitized_search_xaxis[0];
1268 $fieldforxkey = 'x_0';
1269 $xlabel = $obj->$fieldforxkey;
1270 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
1271
1272 // Define $xlabel
1273 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1274 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1275 }
1276
1277 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1278 $xarrayforallseries = array('label' => $labeltouse);
1279 foreach ($sanitized_search_measures as $key => $val) {
1280 $fieldfory = 'y_'.$key;
1281 $xarrayforallseries[$fieldfory] = $obj->$fieldfory;
1282 }
1283 $data[$xi] = $xarrayforallseries;
1284 $xi++;
1285 }
1286 }
1287
1288 $totalnbofrecord = count($data);
1289 }
1290}
1291//var_dump($data);
1292
1293print '<!-- Section to show the result -->'."\n";
1294print '<div class="customreportsoutput'.($totalnbofrecord ? '' : ' customreportsoutputnotdata').'">';
1295
1296if (empty($newarrayoftype)) {
1297 $langs->load("admin");
1298 print info_admin($langs->trans("NoSupportedModulesHaveBeenActivated").' '.$langs->trans("YouCanEnableModulesFrom"), 0, 0, 'info');
1299}
1300
1301if ($mode == 'grid') {
1302 // TODO
1303}
1304
1305if ($mode == 'graph') {
1306 $WIDTH = '80%';
1307 $HEIGHT = (empty($_SESSION['dol_screenheight']) ? 400 : $_SESSION['dol_screenheight'] - 500);
1308
1309 // Show graph
1310 $px1 = new DolGraph();
1311 $mesg = $px1->isGraphKo();
1312 if (!$mesg) {
1313 //var_dump($legend);
1314 //var_dump($data);
1315 $px1->SetData($data);
1316 unset($data);
1317
1318 $arrayoftypes = array();
1319 foreach ($sanitized_search_measures as $key => $val) {
1320 $arrayoftypes[] = $search_graph;
1321 }
1322
1323 $px1->SetLegend($legend);
1324 $px1->setShowLegend($SHOWLEGEND);
1325 $px1->SetMinValue((int) $px1->GetFloorMinValue());
1326 $px1->SetMaxValue($px1->GetCeilMaxValue());
1327 $px1->SetWidth($WIDTH);
1328 $px1->SetHeight($HEIGHT);
1329 $px1->SetYLabel($langs->trans("Y"));
1330 $px1->SetShading(3);
1331 $px1->SetHorizTickIncrement(1);
1332 $px1->SetCssPrefix("cssboxes");
1333 $px1->SetType($arrayoftypes);
1334 $px1->mode = 'depth';
1335 $px1->SetTitle('');
1336
1337 $dir = $conf->user->dir_temp;
1338 dol_mkdir($dir);
1339 // $customreportkey may be defined when using customreports.php as an include
1340 if (!empty($object->element)) {
1341 $filenamekey = $dir.'/customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1342 $fileurlkey = DOL_URL_ROOT.'/viewimage.php?modulepart=user&file=customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1343 }
1344
1345 if (isset($filenamekey) && isset($fileurlkey)) {
1346 $px1->draw($filenamekey, $fileurlkey);
1347 }
1348
1349 $texttoshow = $langs->trans("NoRecordFound");
1350 if (!GETPOSTISSET('search_measures') || !GETPOSTISSET('search_xaxis')) {
1351 $texttoshow = $langs->trans("SelectYourGraphOptionsFirst");
1352 }
1353
1354 print $px1->show($totalnbofrecord ? 0 : $texttoshow);
1355 }
1356}
1357
1358print '</div>';
1359
1360if ($sql && !defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) {
1361 // Show admin info
1362 print '<br>'.info_admin($langs->trans("SQLUsedForExport").':<br> '.$sql, 0, 0, '1', '', 'TechnicalInformation');
1363}
1364
1365
1366if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
1367 print dol_get_fiche_end();
1368
1369 llxFooter();
1370 // End of page
1371
1372 $db->close();
1373}
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:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
dolExplodeIntoArray($string, $delimiter=';', $kv='=')
Split a string with 2 keys into key array.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
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)
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
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.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
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.
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_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.