Skip to content

Commit 4a1199a

Browse files
authored
Merge pull request Dolibarr#31419 from frederic34/extrafields_object_filter
can filter on object properties
2 parents 6cb03ff + d11a888 commit 4a1199a

File tree

4 files changed

+570
-178
lines changed

4 files changed

+570
-178
lines changed
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
<?php
2+
/* Copyright (C) 2007-2024 Laurent Destailleur <[email protected]>
3+
* Copyright (C) 2024 Frédéric France <[email protected]>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation; either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
/**
20+
* \file htdocs/core/ajax/ajaxextrafield.php
21+
* \ingroup extrafield
22+
* \brief This script returns content of extrafield
23+
*/
24+
25+
if (!defined('NOTOKENRENEWAL')) {
26+
// Disables token renewal
27+
define('NOTOKENRENEWAL', 1);
28+
}
29+
if (!defined('NOREQUIREMENU')) {
30+
define('NOREQUIREMENU', '1');
31+
}
32+
if (!defined('NOREQUIREHTML')) {
33+
define('NOREQUIREHTML', '1');
34+
}
35+
if (!defined('NOREQUIREAJAX')) {
36+
define('NOREQUIREAJAX', '1');
37+
}
38+
if (!defined('NOHEADERNOFOOTER')) {
39+
define('NOHEADERNOFOOTER', '1');
40+
}
41+
42+
include '../../main.inc.php';
43+
include_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
44+
/**
45+
* @var Conf $conf
46+
* @var DoliDB $db
47+
* @var Translate $langs
48+
* @var User $user
49+
*/
50+
51+
// object id
52+
$objectid = GETPOST('objectid', 'aZ09');
53+
// 'module' or 'myobject@mymodule', 'mymodule_myobject'
54+
$objecttype = GETPOST('objecttype', 'aZ09arobase');
55+
$objectkey = GETPOST('objectkey', 'restricthtml');
56+
$search = GETPOST('search', 'restricthtml');
57+
$page = GETPOSTINT('page');
58+
$mode = GETPOSTINT('mode');
59+
$value = GETPOST('value', 'alphanohtml');
60+
$limit = 10;
61+
$offset = (($page - 1) * $limit);
62+
$element_ref = '';
63+
if (is_numeric($objectid)) {
64+
$objectid = (int) $objectid;
65+
} else {
66+
$element_ref = $objectid;
67+
$objectid = 0;
68+
}
69+
// Load object according to $element
70+
$object = fetchObjectByElement($objectid, $objecttype, $element_ref);
71+
if (empty($object->element)) {
72+
httponly_accessforbidden('Failed to get object with fetchObjectByElement(id=' . $objectid . ', objecttype=' . $objecttype . ')');
73+
}
74+
75+
$module = $object->module;
76+
$element = $object->element;
77+
78+
$usesublevelpermission = ($module != $element ? $element : '');
79+
if ($usesublevelpermission && !$user->hasRight($module, $element)) { // There is no permission on object defined, we will check permission on module directly
80+
$usesublevelpermission = '';
81+
}
82+
83+
// print $object->id.' - '.$object->module.' - '.$object->element.' - '.$object->table_element.' - '.$usesublevelpermission."\n";
84+
85+
// Security check
86+
restrictedArea($user, $object->module, $object, $object->table_element, $usesublevelpermission);
87+
88+
89+
/*
90+
* View
91+
*/
92+
93+
top_httphead();
94+
95+
$data = [
96+
'results' => [],
97+
'pagination' => [
98+
'more' => true,
99+
]
100+
];
101+
if ($page == 1) {
102+
$data['results'][] = [
103+
'id' => -1,
104+
'text' => '&nbsp;',
105+
];
106+
}
107+
$i = 0;
108+
if ($object instanceof CommonObject) {
109+
$extrafields = new ExtraFields($db);
110+
$extrafields->fetch_name_optionals_label($element);
111+
$options = $extrafields->attributes[$element]['param'][$objectkey]['options'];
112+
if (is_array($options)) {
113+
$tmpparamoptions = array_keys($options);
114+
$paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
115+
116+
$InfoFieldList = explode(":", $paramoptions[0], 5);
117+
// 0 : tableName
118+
// 1 : label field name
119+
// 2 : key fields name (if different of rowid)
120+
// optional parameters...
121+
// 3 : key field parent (for dependent lists). How this is used ?
122+
// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on the second line.
123+
// 5 : string category type. This replace the filter.
124+
// 6 : ids categories list separated by comma for category root. This replace the filter.
125+
// 7 : sort field (not used here but used into format for commobject)
126+
127+
// If there is a filter, we extract it by taking all content inside parenthesis.
128+
if (! empty($InfoFieldList[4])) {
129+
$pos = 0; // $pos will be position of ending filter
130+
$parenthesisopen = 0;
131+
while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
132+
if (substr($InfoFieldList[4], $pos, 1) == '(') {
133+
$parenthesisopen++;
134+
}
135+
if (substr($InfoFieldList[4], $pos, 1) == ')') {
136+
$parenthesisopen--;
137+
}
138+
$pos++;
139+
}
140+
$tmpbefore = substr($InfoFieldList[4], 0, $pos);
141+
$tmpafter = substr($InfoFieldList[4], $pos + 1);
142+
//var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
143+
$InfoFieldList[4] = $tmpbefore;
144+
if ($tmpafter !== '') {
145+
$InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
146+
}
147+
148+
// Fix better compatibility with some old extrafield syntax filter "(field=123)"
149+
$reg = array();
150+
if (preg_match('/^\(?([a-z0-9]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
151+
$InfoFieldList[4] = '(' . $reg[1] . ':' . $reg[2] . ':' . $reg[3] . ')';
152+
}
153+
154+
//var_dump($InfoFieldList);
155+
}
156+
157+
$parentName = '';
158+
$parentField = '';
159+
$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
160+
161+
if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
162+
list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
163+
$keyList .= ', ' . $parentField;
164+
}
165+
if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
166+
if (strpos($InfoFieldList[4], 'extra.') !== false) {
167+
$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
168+
} else {
169+
$keyList = $InfoFieldList[2] . ' as rowid';
170+
}
171+
}
172+
173+
$filter_categorie = false;
174+
if (count($InfoFieldList) > 5) {
175+
if ($InfoFieldList[0] == 'categorie') {
176+
$filter_categorie = true;
177+
}
178+
}
179+
180+
if (!$filter_categorie) {
181+
$fields_label = explode('|', $InfoFieldList[1]);
182+
if (count($fields_label) > 0) {
183+
$keyList .= ', ';
184+
$keyList .= implode(', ', $fields_label);
185+
}
186+
187+
$sqlwhere = '';
188+
$sql = "SELECT " . $keyList;
189+
$sql .= ' FROM ' . $db->prefix() . $InfoFieldList[0];
190+
191+
// Add filter from 4th field
192+
if (!empty($InfoFieldList[4])) {
193+
$tags = [];
194+
preg_match_all('/\$(.*?)\$/', $InfoFieldList[4], $tags);
195+
foreach ($tags[0] as $keytag => $valuetag) {
196+
$property = strtolower($tags[1][$keytag]);
197+
if (strpos($InfoFieldList[4], $valuetag) !== false && property_exists($object, $property) && !empty($object->$property)) {
198+
$InfoFieldList[4] = str_replace($valuetag, (string) $object->$property, $InfoFieldList[4]);
199+
} else {
200+
$InfoFieldList[4] = str_replace($valuetag, '0', $InfoFieldList[4]);
201+
}
202+
}
203+
// can use current entity filter
204+
if (strpos($InfoFieldList[4], '$ENTITY$') !== false) {
205+
$InfoFieldList[4] = str_replace('$ENTITY$', (string) $conf->entity, $InfoFieldList[4]);
206+
}
207+
// can use SELECT request
208+
if (strpos($InfoFieldList[4], '$SEL$') !== false) {
209+
$InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
210+
}
211+
// can use MODE request (list or view)
212+
if (strpos($InfoFieldList[4], '$MODE$') !== false) {
213+
$InfoFieldList[4] = str_replace('$MODE$', (string) $mode, $InfoFieldList[4]);
214+
}
215+
216+
// current object id can be use into filter
217+
if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
218+
$InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
219+
} else {
220+
$InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
221+
}
222+
223+
// We have to join on extrafield table
224+
$errstr = '';
225+
if (strpos($InfoFieldList[4], 'extra.') !== false) {
226+
$sql .= ' as main, ' . $db->sanitize($db->prefix() . $InfoFieldList[0]) . '_extrafields as extra';
227+
$sqlwhere .= " WHERE extra.fk_object = main." . $db->sanitize($InfoFieldList[2]);
228+
$sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
229+
} else {
230+
$sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
231+
}
232+
} else {
233+
$sqlwhere .= ' WHERE 1=1';
234+
}
235+
236+
// Some tables may have field, some other not. For the moment we disable it.
237+
if (in_array($InfoFieldList[0], array('tablewithentity'))) {
238+
$sqlwhere .= ' AND entity = ' . ((int) $conf->entity);
239+
}
240+
if ($search) {
241+
if ($fields_label) {
242+
$sqlwhere .= " " . natural_search($fields_label, $search, 0);
243+
}
244+
}
245+
$sql .= $sqlwhere;
246+
$orderfields = explode('|', $InfoFieldList[1]);
247+
$keyList = $InfoFieldList[1];
248+
if (count($orderfields)) {
249+
$keyList = implode(', ', $orderfields);
250+
}
251+
$sql .= $db->order($keyList);
252+
$sql .= $db->plimit($limit, $offset);
253+
254+
$data['sql'] = $sql;
255+
256+
$resql = $db->query($sql);
257+
if ($resql) {
258+
// $out .= '<option value="0">&nbsp;</option>';
259+
$num = $db->num_rows($resql);
260+
$i = 0;
261+
while ($i < $num) {
262+
$labeltoshow = '';
263+
$obj = $db->fetch_object($resql);
264+
265+
// Several field into label (eq table:code|label:rowid)
266+
$notrans = false;
267+
$fields_label = explode('|', $InfoFieldList[1]);
268+
if (count($fields_label) > 1) {
269+
$notrans = true;
270+
foreach ($fields_label as $field_toshow) {
271+
$labeltoshow .= $obj->$field_toshow . ' ';
272+
}
273+
} else {
274+
$labeltoshow = $obj->{$InfoFieldList[1]};
275+
}
276+
277+
if ($value == $obj->rowid) {
278+
if (!$notrans) {
279+
foreach ($fields_label as $field_toshow) {
280+
$translabel = $langs->trans($obj->$field_toshow);
281+
$labeltoshow = $translabel . ' ';
282+
}
283+
}
284+
// $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
285+
$data['results'][] = [
286+
'id' => $obj->rowid,
287+
'text' => $labeltoshow,
288+
];
289+
} else {
290+
if (!$notrans) {
291+
$translabel = $langs->trans($obj->{$InfoFieldList[1]});
292+
$labeltoshow = $translabel;
293+
}
294+
if (empty($labeltoshow)) {
295+
$labeltoshow = '(not defined)';
296+
}
297+
298+
if (!empty($InfoFieldList[3]) && $parentField) {
299+
$parent = $parentName . ':' . $obj->{$parentField};
300+
}
301+
302+
// $out .= '<option value="'.$obj->rowid.'"';
303+
// $out .= ($value == $obj->rowid ? ' selected' : '');
304+
// $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
305+
// $out .= '>'.$labeltoshow.'</option>';
306+
$data['results'][] = [
307+
'id' => $obj->rowid,
308+
'text' => $labeltoshow,
309+
];
310+
}
311+
312+
$i++;
313+
}
314+
$db->free($resql);
315+
} else {
316+
dol_syslog('Error in request ' . $db->lasterror() . '. Check setup of extra parameters.', LOG_ERR);
317+
}
318+
} else {
319+
require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
320+
require_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
321+
$form = new Form($db);
322+
$categories = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
323+
// $out .= '<option value="0">&nbsp;</option>';
324+
// if (is_array($categories)) {
325+
// foreach ($categories as $category_key => $category_value) {
326+
// $out .= '<option value="'.$category_key.'"';
327+
// $out .= ($value == $category_key ? ' selected' : '');
328+
// $out .= '>'.$category_value.'</option>';
329+
// }
330+
// }
331+
}
332+
}
333+
}
334+
335+
if ($page > 1 && $i < 9) {
336+
$data['pagination'] = [
337+
'more' => false,
338+
];
339+
}
340+
print json_encode($data);
341+
342+
$db->close();

htdocs/core/class/commonobject.class.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7954,6 +7954,8 @@ function handlemultiinputdisabling(htmlname){
79547954
} else {
79557955
$sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
79567956
}
7957+
$sql .= ' LIMIT ' . getDolGlobalInt('MAIN_EXTRAFIELDS_LIMIT_SELLIST_SQL', 1000);
7958+
// print $sql;
79577959

79587960
dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
79597961
$resql = $this->db->query($sql);

0 commit comments

Comments
 (0)