Modifications pour le document Macros de résultats Livetable

Modifié par Florent Charton le 2026/03/13 11:03

Depuis la version 1.1
modifié par superadmin
sur 2022/06/20 08:48
Commentaire de modification : Install extension [org.xwiki.platform:xwiki-platform-livetable-ui/13.10.6]
À la version 5.1
modifié par Florent Charton
sur 2026/01/13 15:00
Commentaire de modification : Install extension [org.xwiki.platform:xwiki-platform-livetable-ui/17.10.2]

Résumé

Détails

Propriétés de la Page
Auteur du document
... ... @@ -1,1 +1,1 @@
1 -XWiki.superadmin
1 +xwiki:XWiki.fcharton
Contenu
... ... @@ -35,7 +35,18 @@
35 35   ##
36 36   #set($tablelist = [])
37 37   #foreach($colname in $collist)
38 - #livetable_addColumnToQuery($colname)
38 + ## If a classname is defined and the class field corresponding to the column name,
39 + ## we check the type of the field and skip it if it's Password.
40 + #if ($className != '' && $class.get($colname))
41 + #set ($isPasswordType = $class.get($colname).classType == 'Password')
42 + #set ($isEmailType = $class.get($colname).classType == 'Email')
43 + #set ($emailObfuscated = $services.mail.general.shouldObfuscate())
44 + #if (!($isPasswordType || ($isEmailType && $emailObfuscated)))
45 + #livetable_addColumnToQuery($colname)
46 + #end
47 + #else
48 + #livetable_addColumnToQuery($colname)
49 + #end
39 39   #end
40 40   ##
41 41   ## Tag filtering
... ... @@ -89,6 +89,8 @@
89 89   #set($order = "$!request.sort")
90 90   #if ($order == 'doc.location')
91 91   #set ($order = 'doc.fullName')
103 + #elseif ($order == 'email' && $services.mail.general.shouldObfuscate())
104 + #set ($order = '')
92 92   #end
93 93   #set ($orderSql = '')
94 94   #if($order != '')
... ... @@ -263,7 +263,13 @@
263 263   ##
264 264   ## TagCloud matching all the documents used by the live table
265 265   ##
266 - #set($allMatchingTags = $xwiki.tag.getTagCountForQuery($allMatchingTagsFrom, $allMatchingTagsWhere, $allMatchingParams))
279 + ## If all the query parameters are the same as for $tagsMatchingFilters, no need to run the query again.
280 + ## This optimization allows to divide the time to compute the tagcloud by 2 when the table has no filters applied.
281 + #if ($allMatchingTagsFrom.trim() != $tagsMatchingFiltersFrom.trim() || $allMatchingTagsWhere.trim() != $tagsMatchingFiltersWhere.trim() || $tagsMatchingParams != $allMatchingParams)
282 + #set($allMatchingTags = $xwiki.tag.getTagCountForQuery($allMatchingTagsFrom, $allMatchingTagsWhere, $allMatchingParams))
283 + #else
284 + #set($allMatchingTags = $tagsMatchingFilters)
285 + #end
267 267   ## FIXME: We use a list of maps just because the client expects an array, but we should simply return $allMatchingTags..
268 268   #set($tags = [])
269 269   #foreach($tag in $allMatchingTags.keySet())
... ... @@ -286,10 +286,7 @@
286 286   #if(!$offset || $offset < 0)
287 287   #set($offset = 0)
288 288   #end
289 - #set($limit = $numbertool.toNumber($request.get('limit')).intValue())
290 - #if(!$limit)
291 - #set ($limit = 15)
292 - #end
308 + #getAndValidateQueryLimitFromRequest('limit', 15, $limit)
293 293   #set($query = $services.query.hql($sql))
294 294   ## Apply query filters if defined. Otherwise use default.
295 295   #foreach ($queryFilter in $stringtool.split($!request.queryFilters, ', '))
... ... @@ -298,7 +298,12 @@
298 298   #set ($query = $query.setLimit($limit).setOffset($offset).bindValues($sqlParams))
299 299   #set($items = $query.execute())
300 300   #set($discard = $map.put('totalrows', $query.count()))
301 - #set($discard = $map.put('returnedrows', $mathtool.min($items.size(), $limit)))
317 + #if ($limit > 0)
318 + #set($discard = $map.put('returnedrows', $mathtool.min($items.size(), $limit)))
319 + #else
320 + ## When the limit is 0, it's equivalent to no limit at all and the actual number of returned results can be used.
321 + #set($discard = $map.put('returnedrows', $items.size()))
322 + #end
302 302   #set($discard = $map.put('offset', $mathtool.add($offset, 1)))
303 303   #set($rows = [])
304 304   #foreach($item in $items)
... ... @@ -377,7 +377,6 @@
377 377   #set($discard = $itemDoc.use($className))
378 378   #set($discard = $row.put('doc_objectCount', $itemDoc.getObjectNumbers($className)))
379 379   #set($discard = $row.put('doc_edit_url', $itemDoc.getURL($itemDoc.defaultEditMode)))
380 - #set($discard = $row.put('doc_author_url', $xwiki.getURL($translatedDoc.author)))
381 381   #set($discard = $row.put('doc_date', $xwiki.formatDate($translatedDoc.date)))
382 382   #set($discard = $row.put('doc_title', $translatedDoc.plainTitle))
383 383   #set($rawTitle = $translatedDoc.title)
... ... @@ -384,7 +384,15 @@
384 384   #if($rawTitle != $row['doc_title'])
385 385   #set($discard = $row.put('doc_title_raw', $rawTitle))
386 386   #end
387 - #set($discard = $row.put('doc_author', $xwiki.getPlainUserName($translatedDoc.authorReference)))
407 + #set ($metadataAuthor = $translatedDoc.authors.originalMetadataAuthor)
408 + #if ($metadataAuthor == $services.user.getGuestUserReference())
409 + ## Special handling for guest so that it displays unknown user.
410 + #set($discard = $row.put('doc_author', $xwiki.getPlainUserName($NULL)))
411 + #else
412 + #set($discard = $row.put('doc_author', $xwiki.getPlainUserName($metadataAuthor)))
413 + #end
414 +
415 + #set($discard = $row.put('doc_author_url', $xwiki.getURL($metadataAuthor)))
388 388   #set($discard = $row.put('doc_creationDate', $xwiki.formatDate($translatedDoc.creationDate)))
389 389   #set($discard = $row.put('doc_creator', $xwiki.getPlainUserName($translatedDoc.creatorReference)))
390 390   #set($discard = $row.put('doc_hidden', $translatedDoc.isHidden()))
... ... @@ -437,10 +437,12 @@
437 437   #set($fieldProperty = $fieldObject.getProperty($colname))
438 438   #if ($fieldProperty.getPropertyClass().classType == 'Password')
439 439   #set($fieldValue = '********')
468 + #elseif ($fieldProperty.getPropertyClass().classType == 'Email' && $services.mail.general.shouldObfuscate())
469 + #set ($fieldValue = $services.mail.general.obfuscate("$!fieldProperty.getValue()"))
440 440   #else
441 441   #set($fieldValue = "$!fieldProperty.getValue()")
442 442   #end
443 - #set($fieldDisplayValue = "$!itemDoc.display($colname, 'view')")
473 + #set($fieldDisplayValue = "#unwrapXPropertyDisplay($itemDoc.display($colname, 'view'))")
444 444   #if($fieldDisplayValue == '')
445 445   #set($fieldDisplayValue = $services.localization.render("${request.transprefix}emptyvalue"))
446 446   #end
... ... @@ -452,7 +452,7 @@
452 452   #set($fieldUrl = '')
453 453   #end
454 454   #end
455 - #set($discard = $row.put($colname, $fieldDisplayValue.replaceFirst($regextool.quote('{{html clean="false" wiki="false"}}'), '').replaceAll("$regextool.quote('{{/html}}')$", '')))
485 + #set($discard = $row.put($colname, $fieldDisplayValue))
456 456   #set($discard = $row.put("${colname}_value", $fieldValue))
457 457   #set($discard = $row.put("${colname}_url", $fieldUrl))
458 458   ## Reset to the default class
... ... @@ -473,7 +473,9 @@
473 473   #set($discard = $map.put('params', $sqlParams))
474 474   #end
475 475   #set($discard = $map.put('reqNo', $numbertool.toNumber($request.reqNo).intValue()))
476 - #gridresult_buildTagCloudJSON($map)
506 + #if("$!request.tagcloud" == 'true')
507 + #gridresult_buildTagCloudJSON($map)
508 + #end
477 477   #gridresult_buildRowsJSON($map)
478 478  #end
479 479  
... ... @@ -527,8 +527,10 @@
527 527   #elseif($propType == 'TextAreaClass' || $propType == 'UsersClass' || $propType == 'GroupsClass')
528 528   #set($tableName = 'LargeStringProperty')
529 529   #elseif($propType == 'StaticListClass' || $propType == 'DBListClass' || $propType == 'DBTreeListClass' || $propType == 'PageClass')
562 + ## The following logic is mirrored from ListClass and might need to be updated when the logic in ListClass changes.
530 530   #set($multiSelect = $propClass.get($colname).getProperty('multiSelect').getValue())
531 531   #set($relationalStorage = $propClass.get($colname).getProperty('relationalStorage').getValue())
565 + #set($largeStorage = $propClass.get($colname).getProperty('largeStorage').getValue())
532 532   #if($multiSelect == 1)
533 533   #if($relationalStorage == 1)
534 534   #set($tableName = 'DBStringListProperty')
... ... @@ -535,6 +535,8 @@
535 535   #else
536 536   #set($tableName = 'StringListProperty')
537 537   #end
572 + #elseif($largeStorage == 1)
573 + #set($tableName = 'LargeStringProperty')
538 538   #else
539 539   #set($tableName = 'StringProperty')
540 540   #end
... ... @@ -848,34 +848,79 @@
848 848   *#
849 849  #macro (livetable_filterDBStringListProperty)
850 850   ## Perform exact matching by default if no match type is specified.
851 - ## Note that for DBStringList properties we take into account only the first match type, even if multiple filter
852 - ## values are specified. Basically the first match type is used for all filter values.
887 + ## For DBStringList properties we still apply a single match type to all non-empty values, but we also allow
888 + ## combining the special "empty" match type with other match types.
853 853   #livetable_getMatchTypes($colname $filterValues.size() 'exact')
854 - #if ($matchType == 'partial' || $matchType == 'prefix')
855 - ## We need to join with the list of values in order to be able to use the LIKE operator.
856 - #set ($matchTarget = "${safe_tableAlias}_item")
857 - #if ($whereParams.entrySet())
858 - #set ($paramPrefix = "${safe_tableAlias}_item_")
890 + #livetable_getJoinOperator($colname)
891 +
892 + ## Collect non-empty filter values (those whose match type is not 'empty').
893 + #set ($nonEmptyValues = [])
894 + #set ($hasEmpty = false)
895 + #set ($matchType = 'invalid')
896 + #foreach ($filterValue in $filterValues)
897 + #if ($matchTypes.get($foreach.index) == 'empty')
898 + #set ($hasEmpty = true)
899 + ## When we want to match empty values, we can't have other match types than exact for non-empty values as for
900 + ## other match types, we need to join with the list of values, which is not compatible with checking for
901 + ## emptiness.
902 + #set ($matchType = 'exact')
903 + #elseif ("$!filterValue" != '')
904 + #set ($discard = $nonEmptyValues.add($filterValue))
905 + ## Store the first non-empty match type.
906 + #if ($matchType == 'invalid')
907 + #set ($matchType = $matchTypes.get($foreach.index))
908 + #end
909 + #end
910 + #end
911 +
912 + ## 1) Apply the non-empty constraints.
913 + #if (!$nonEmptyValues.isEmpty())
914 + #if ($matchType == 'partial' || $matchType == 'prefix')
915 + ## We need to join with the list of values in order to be able to use the LIKE operator.
916 + #set ($matchTarget = "${safe_tableAlias}_item")
917 + #if ($whereParams.entrySet())
918 + #set ($paramPrefix = "${safe_tableAlias}_item_")
919 + #else
920 + #set ($paramPrefix = $NULL)
921 + #end
922 + #set ($joinPos = $mathtool.add($fromSql.lastIndexOf(" $safe_tableAlias"), $mathtool.add($safe_tableAlias.length(), 1)))
923 + #set ($fromSql = "$fromSql.substring(0, $joinPos) join ${safe_tableAlias}.list as $matchTarget $fromSql.substring($joinPos)")
859 859   #else
860 - #set ($paramPrefix = $NULL)
925 + ## Fall-back on exact matching even if the match type is specified, when its value is not supported.
926 + #set ($matchType = 'exact')
927 + #set ($matchTarget = "${safe_tableAlias}.list")
928 + #if ($whereParams.entrySet())
929 + #set ($paramPrefix = "${safe_tableAlias}_list_")
930 + #else
931 + #set ($paramPrefix = $NULL)
932 + #end
861 861   #end
862 - #set ($joinPos = $mathtool.add($fromSql.lastIndexOf(" $safe_tableAlias"), $mathtool.add($safe_tableAlias.length(), 1)))
863 - #set ($fromSql = "$fromSql.substring(0, $joinPos) join ${safe_tableAlias}.list as $matchTarget $fromSql.substring($joinPos)")
864 - #else
865 - ## Fall-back on exact matching even if the match type is specified, when its value is not supported.
866 - #set ($matchType = 'exact')
867 - #set ($matchTarget = "${safe_tableAlias}.list")
868 - #if ($whereParams.entrySet())
869 - #set ($paramPrefix = "${safe_tableAlias}_list_")
934 +
935 + #set ($filterQuery = "#livetable_getFilterQuery($matchTarget $matchType true $nonEmptyValues.size() $paramPrefix $NULL)")
936 + #if (!$hasEmpty)
937 + ## Only non-empty values are used, combine directly with the existing constraints, otherwise, they will be
938 + ## combined later together with the empty constraint.
939 + #set ($whereSql = "$whereSql and ($filterQuery.trim())")
940 + #end
941 + #foreach ($filterValue in $nonEmptyValues)
942 + #livetable_addFilterParam($filterValue $matchType $whereParams "${paramPrefix}${foreach.count}")
943 + #end
944 + #end
945 +
946 + ## 2) Optionally add a single constraint if any match type is 'empty'.
947 + #if ($hasEmpty)
948 + ## "empty" means that there is no list item stored for this property on the filtered object.
949 + ## The proper way to check for that would be "${safe_tableAlias}.list IS EMPTY", but JSQL cannot parse "IS EMPTY"
950 + ## which means that we cannot use it without programming right.
951 + #set ($emptyConstraint = "size(${safe_tableAlias}.list) = 0")
952 + #if ($nonEmptyValues.isEmpty())
953 + ## Only 'empty' is used, combine with the existing constraints.
954 + #set ($whereSql = "${whereSql} and ${emptyConstraint}")
870 870   #else
871 - #set ($paramPrefix = $NULL)
956 + ## Combine non-empty group and empty condition using the join operator.
957 + #set ($whereSql = "${whereSql} and ($filterQuery.trim() ${joinOperator} ${emptyConstraint})")
872 872   #end
873 873   #end
874 - #set ($filterQuery = "#livetable_getFilterQuery($matchTarget $matchType true $filterValues.size() $paramPrefix $NULL)")
875 - #set ($whereSql = "$whereSql and ($filterQuery.trim())")
876 - #foreach ($filterValue in $filterValues)
877 - #livetable_addFilterParam($filterValue $matchType $whereParams "${paramPrefix}${foreach.count}")
878 - #end
879 879  #end
880 880  
881 881  
... ... @@ -970,9 +970,9 @@
970 970   #if ($matchType == 'partial' || $matchType == 'prefix')
971 971   #livetable_repeatParams("upper($column) like upper(?)", " $joinOperator ", $valueCount, $paramPrefix, $paramOffset)
972 972   #elseif($matchType == 'empty')
973 - ## Check if the value of the column is like the empty parameter (which is often the empty string), or if the value
1054 + ## Check if the value of the column is like the empty parameter (which is often the empty string), or if the value
974 974   ## of the column is null (to be compliant with Oracle which stores the empty string as a NULL value).
975 - #livetable_repeatParams("($column like ? or $column is null)", " $joinOperator ", $valueCount, $paramPrefix,
1056 + #livetable_repeatParams("($column like ? or $column is null)", " $joinOperator ", $valueCount, $paramPrefix,
976 976   $paramOffset)
977 977   #elseif ($isList)
978 978   #livetable_repeatParams("? in elements($column)", " $joinOperator ", $valueCount, $paramPrefix, $paramOffset)