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