Modifications pour le document Macros de résultats Livetable

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

Depuis la version 6.1
modifié par Florent Charton
sur 2026/03/13 11:03
Commentaire de modification : Install extension [org.xwiki.platform:xwiki-platform-livetable-ui/17.10.4]
À 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]

Résumé

Détails

Propriétés de la Page
Auteur du document
... ... @@ -1,1 +1,1 @@
1 -xwiki:XWiki.fcharton
1 +XWiki.superadmin
Contenu
... ... @@ -35,19 +35,7 @@
35 35   ##
36 36   #set($tablelist = [])
37 37   #foreach($colname in $collist)
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 - #livetable_getPropertyClassAndType($colname)
41 - #if ($propType != '')
42 - #set ($isPasswordType = $propClass.get($colname).classType == 'Password')
43 - #set ($isEmailType = $propClass.get($colname).classType == 'Email')
44 - #set ($emailObfuscated = $services.mail.general.shouldObfuscate())
45 - #if (!($isPasswordType || ($isEmailType && $emailObfuscated)))
46 - #livetable_addColumnToQuery($colname)
47 - #end
48 - #else
49 - #livetable_addColumnToQuery($colname)
50 - #end
38 + #livetable_addColumnToQuery($colname)
51 51   #end
52 52   ##
53 53   ## Tag filtering
... ... @@ -101,8 +101,6 @@
101 101   #set($order = "$!request.sort")
102 102   #if ($order == 'doc.location')
103 103   #set ($order = 'doc.fullName')
104 - #elseif ($order == 'email' && $services.mail.general.shouldObfuscate())
105 - #set ($order = '')
106 106   #end
107 107   #set ($orderSql = '')
108 108   #if($order != '')
... ... @@ -277,13 +277,7 @@
277 277   ##
278 278   ## TagCloud matching all the documents used by the live table
279 279   ##
280 - ## If all the query parameters are the same as for $tagsMatchingFilters, no need to run the query again.
281 - ## This optimization allows to divide the time to compute the tagcloud by 2 when the table has no filters applied.
282 - #if ($allMatchingTagsFrom.trim() != $tagsMatchingFiltersFrom.trim() || $allMatchingTagsWhere.trim() != $tagsMatchingFiltersWhere.trim() || $tagsMatchingParams != $allMatchingParams)
283 - #set($allMatchingTags = $xwiki.tag.getTagCountForQuery($allMatchingTagsFrom, $allMatchingTagsWhere, $allMatchingParams))
284 - #else
285 - #set($allMatchingTags = $tagsMatchingFilters)
286 - #end
266 + #set($allMatchingTags = $xwiki.tag.getTagCountForQuery($allMatchingTagsFrom, $allMatchingTagsWhere, $allMatchingParams))
287 287   ## FIXME: We use a list of maps just because the client expects an array, but we should simply return $allMatchingTags..
288 288   #set($tags = [])
289 289   #foreach($tag in $allMatchingTags.keySet())
... ... @@ -306,7 +306,10 @@
306 306   #if(!$offset || $offset < 0)
307 307   #set($offset = 0)
308 308   #end
309 - #getAndValidateQueryLimitFromRequest('limit', 15, $limit)
289 + #set($limit = $numbertool.toNumber($request.get('limit')).intValue())
290 + #if(!$limit)
291 + #set ($limit = 15)
292 + #end
310 310   #set($query = $services.query.hql($sql))
311 311   ## Apply query filters if defined. Otherwise use default.
312 312   #foreach ($queryFilter in $stringtool.split($!request.queryFilters, ', '))
... ... @@ -315,12 +315,7 @@
315 315   #set ($query = $query.setLimit($limit).setOffset($offset).bindValues($sqlParams))
316 316   #set($items = $query.execute())
317 317   #set($discard = $map.put('totalrows', $query.count()))
318 - #if ($limit > 0)
319 - #set($discard = $map.put('returnedrows', $mathtool.min($items.size(), $limit)))
320 - #else
321 - ## When the limit is 0, it's equivalent to no limit at all and the actual number of returned results can be used.
322 - #set($discard = $map.put('returnedrows', $items.size()))
323 - #end
301 + #set($discard = $map.put('returnedrows', $mathtool.min($items.size(), $limit)))
324 324   #set($discard = $map.put('offset', $mathtool.add($offset, 1)))
325 325   #set($rows = [])
326 326   #foreach($item in $items)
... ... @@ -399,6 +399,7 @@
399 399   #set($discard = $itemDoc.use($className))
400 400   #set($discard = $row.put('doc_objectCount', $itemDoc.getObjectNumbers($className)))
401 401   #set($discard = $row.put('doc_edit_url', $itemDoc.getURL($itemDoc.defaultEditMode)))
380 + #set($discard = $row.put('doc_author_url', $xwiki.getURL($translatedDoc.author)))
402 402   #set($discard = $row.put('doc_date', $xwiki.formatDate($translatedDoc.date)))
403 403   #set($discard = $row.put('doc_title', $translatedDoc.plainTitle))
404 404   #set($rawTitle = $translatedDoc.title)
... ... @@ -405,15 +405,7 @@
405 405   #if($rawTitle != $row['doc_title'])
406 406   #set($discard = $row.put('doc_title_raw', $rawTitle))
407 407   #end
408 - #set ($metadataAuthor = $translatedDoc.authors.originalMetadataAuthor)
409 - #if ($metadataAuthor == $services.user.getGuestUserReference())
410 - ## Special handling for guest so that it displays unknown user.
411 - #set($discard = $row.put('doc_author', $xwiki.getPlainUserName($NULL)))
412 - #else
413 - #set($discard = $row.put('doc_author', $xwiki.getPlainUserName($metadataAuthor)))
414 - #end
415 -
416 - #set($discard = $row.put('doc_author_url', $xwiki.getURL($metadataAuthor)))
387 + #set($discard = $row.put('doc_author', $xwiki.getPlainUserName($translatedDoc.authorReference)))
417 417   #set($discard = $row.put('doc_creationDate', $xwiki.formatDate($translatedDoc.creationDate)))
418 418   #set($discard = $row.put('doc_creator', $xwiki.getPlainUserName($translatedDoc.creatorReference)))
419 419   #set($discard = $row.put('doc_hidden', $translatedDoc.isHidden()))
... ... @@ -466,12 +466,10 @@
466 466   #set($fieldProperty = $fieldObject.getProperty($colname))
467 467   #if ($fieldProperty.getPropertyClass().classType == 'Password')
468 468   #set($fieldValue = '********')
469 - #elseif ($fieldProperty.getPropertyClass().classType == 'Email' && $services.mail.general.shouldObfuscate())
470 - #set ($fieldValue = $services.mail.general.obfuscate("$!fieldProperty.getValue()"))
471 471   #else
472 472   #set($fieldValue = "$!fieldProperty.getValue()")
473 473   #end
474 - #set($fieldDisplayValue = "#unwrapXPropertyDisplay($itemDoc.display($colname, 'view'))")
443 + #set($fieldDisplayValue = "$!itemDoc.display($colname, 'view')")
475 475   #if($fieldDisplayValue == '')
476 476   #set($fieldDisplayValue = $services.localization.render("${request.transprefix}emptyvalue"))
477 477   #end
... ... @@ -483,7 +483,7 @@
483 483   #set($fieldUrl = '')
484 484   #end
485 485   #end
486 - #set($discard = $row.put($colname, $fieldDisplayValue))
455 + #set($discard = $row.put($colname, $fieldDisplayValue.replaceFirst($regextool.quote('{{html clean="false" wiki="false"}}'), '').replaceAll("$regextool.quote('{{/html}}')$", '')))
487 487   #set($discard = $row.put("${colname}_value", $fieldValue))
488 488   #set($discard = $row.put("${colname}_url", $fieldUrl))
489 489   ## Reset to the default class
... ... @@ -504,9 +504,7 @@
504 504   #set($discard = $map.put('params', $sqlParams))
505 505   #end
506 506   #set($discard = $map.put('reqNo', $numbertool.toNumber($request.reqNo).intValue()))
507 - #if("$!request.tagcloud" == 'true')
508 - #gridresult_buildTagCloudJSON($map)
509 - #end
476 + #gridresult_buildTagCloudJSON($map)
510 510   #gridresult_buildRowsJSON($map)
511 511  #end
512 512  
... ... @@ -560,10 +560,8 @@
560 560   #elseif($propType == 'TextAreaClass' || $propType == 'UsersClass' || $propType == 'GroupsClass')
561 561   #set($tableName = 'LargeStringProperty')
562 562   #elseif($propType == 'StaticListClass' || $propType == 'DBListClass' || $propType == 'DBTreeListClass' || $propType == 'PageClass')
563 - ## The following logic is mirrored from ListClass and might need to be updated when the logic in ListClass changes.
564 564   #set($multiSelect = $propClass.get($colname).getProperty('multiSelect').getValue())
565 565   #set($relationalStorage = $propClass.get($colname).getProperty('relationalStorage').getValue())
566 - #set($largeStorage = $propClass.get($colname).getProperty('largeStorage').getValue())
567 567   #if($multiSelect == 1)
568 568   #if($relationalStorage == 1)
569 569   #set($tableName = 'DBStringListProperty')
... ... @@ -570,8 +570,6 @@
570 570   #else
571 571   #set($tableName = 'StringListProperty')
572 572   #end
573 - #elseif($largeStorage == 1)
574 - #set($tableName = 'LargeStringProperty')
575 575   #else
576 576   #set($tableName = 'StringProperty')
577 577   #end
... ... @@ -693,19 +693,6 @@
693 693   #elseif ($colName == 'doc.date' || $colName == 'doc.creationDate' || $colName == 'doc.contentUpdateDate')
694 694   #livetable_getTableAlias($colName)
695 695   #livetable_filterDateProperty()
696 - #elseif ($colName == 'doc.hidden' || $colName == 'doc.minorEdit1' || $colName == 'doc.enforceRequiredRights')
697 - ## Boolean document fields need special handling to work across all databases
698 - ## (HSQLDB/PostgreSQL use true/false, MySQL/MariaDB/Oracle use 1/0).
699 - ## Support both true/false and 1/0 as Live Data uses the former while LiveTable uses the latter.
700 - #set ($booleanValue = ($filterValue.toLowerCase() == 'true' || $filterValue == '1'))
701 - ## No need to clean the column name since it's only one of the given values.
702 - #if ($whereParams.entrySet())
703 - #set ($whereSql = "${whereSql} and $colName = :${colName.replace('.', '_')}_filter")
704 - #set ($discard = $whereParams.put("${colName.replace('.', '_')}_filter", $booleanValue))
705 - #else
706 - #set ($whereSql = "${whereSql} and $colName = ?")
707 - #set ($discard = $whereParams.add($booleanValue))
708 - #end
709 709   #else
710 710   #set ($safeColName = $colName.replaceAll('[^a-zA-Z0-9_.]', '').replace('_', '.'))
711 711   #if ($whereParams.entrySet())
... ... @@ -898,79 +898,34 @@
898 898   *#
899 899  #macro (livetable_filterDBStringListProperty)
900 900   ## Perform exact matching by default if no match type is specified.
901 - ## For DBStringList properties we still apply a single match type to all non-empty values, but we also allow
902 - ## combining the special "empty" match type with other match types.
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.
903 903   #livetable_getMatchTypes($colname $filterValues.size() 'exact')
904 - #livetable_getJoinOperator($colname)
905 -
906 - ## Collect non-empty filter values (those whose match type is not 'empty').
907 - #set ($nonEmptyValues = [])
908 - #set ($hasEmpty = false)
909 - #set ($matchType = 'invalid')
910 - #foreach ($filterValue in $filterValues)
911 - #if ($matchTypes.get($foreach.index) == 'empty')
912 - #set ($hasEmpty = true)
913 - ## When we want to match empty values, we can't have other match types than exact for non-empty values as for
914 - ## other match types, we need to join with the list of values, which is not compatible with checking for
915 - ## emptiness.
916 - #set ($matchType = 'exact')
917 - #elseif ("$!filterValue" != '')
918 - #set ($discard = $nonEmptyValues.add($filterValue))
919 - ## Store the first non-empty match type.
920 - #if ($matchType == 'invalid')
921 - #set ($matchType = $matchTypes.get($foreach.index))
922 - #end
923 - #end
924 - #end
925 -
926 - ## 1) Apply the non-empty constraints.
927 - #if (!$nonEmptyValues.isEmpty())
928 - #if ($matchType == 'partial' || $matchType == 'prefix')
929 - ## We need to join with the list of values in order to be able to use the LIKE operator.
930 - #set ($matchTarget = "${safe_tableAlias}_item")
931 - #if ($whereParams.entrySet())
932 - #set ($paramPrefix = "${safe_tableAlias}_item_")
933 - #else
934 - #set ($paramPrefix = $NULL)
935 - #end
936 - #set ($joinPos = $mathtool.add($fromSql.lastIndexOf(" $safe_tableAlias"), $mathtool.add($safe_tableAlias.length(), 1)))
937 - #set ($fromSql = "$fromSql.substring(0, $joinPos) join ${safe_tableAlias}.list as $matchTarget $fromSql.substring($joinPos)")
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_")
938 938   #else
939 - ## Fall-back on exact matching even if the match type is specified, when its value is not supported.
940 - #set ($matchType = 'exact')
941 - #set ($matchTarget = "${safe_tableAlias}.list")
942 - #if ($whereParams.entrySet())
943 - #set ($paramPrefix = "${safe_tableAlias}_list_")
944 - #else
945 - #set ($paramPrefix = $NULL)
946 - #end
860 + #set ($paramPrefix = $NULL)
947 947   #end
948 -
949 - #set ($filterQuery = "#livetable_getFilterQuery($matchTarget $matchType true $nonEmptyValues.size() $paramPrefix $NULL)")
950 - #if (!$hasEmpty)
951 - ## Only non-empty values are used, combine directly with the existing constraints, otherwise, they will be
952 - ## combined later together with the empty constraint.
953 - #set ($whereSql = "$whereSql and ($filterQuery.trim())")
954 - #end
955 - #foreach ($filterValue in $nonEmptyValues)
956 - #livetable_addFilterParam($filterValue $matchType $whereParams "${paramPrefix}${foreach.count}")
957 - #end
958 - #end
959 -
960 - ## 2) Optionally add a single constraint if any match type is 'empty'.
961 - #if ($hasEmpty)
962 - ## "empty" means that there is no list item stored for this property on the filtered object.
963 - ## The proper way to check for that would be "${safe_tableAlias}.list IS EMPTY", but JSQL cannot parse "IS EMPTY"
964 - ## which means that we cannot use it without programming right.
965 - #set ($emptyConstraint = "size(${safe_tableAlias}.list) = 0")
966 - #if ($nonEmptyValues.isEmpty())
967 - ## Only 'empty' is used, combine with the existing constraints.
968 - #set ($whereSql = "${whereSql} and ${emptyConstraint}")
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_")
969 969   #else
970 - ## Combine non-empty group and empty condition using the join operator.
971 - #set ($whereSql = "${whereSql} and ($filterQuery.trim() ${joinOperator} ${emptyConstraint})")
871 + #set ($paramPrefix = $NULL)
972 972   #end
973 973   #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
974 974  #end
975 975  
976 976  
... ... @@ -1065,9 +1065,9 @@
1065 1065   #if ($matchType == 'partial' || $matchType == 'prefix')
1066 1066   #livetable_repeatParams("upper($column) like upper(?)", " $joinOperator ", $valueCount, $paramPrefix, $paramOffset)
1067 1067   #elseif($matchType == 'empty')
1068 - ## Check if the value of the column is like the empty parameter (which is often the empty string), or if the value
973 + ## Check if the value of the column is like the empty parameter (which is often the empty string), or if the value
1069 1069   ## of the column is null (to be compliant with Oracle which stores the empty string as a NULL value).
1070 - #livetable_repeatParams("($column like ? or $column is null)", " $joinOperator ", $valueCount, $paramPrefix,
975 + #livetable_repeatParams("($column like ? or $column is null)", " $joinOperator ", $valueCount, $paramPrefix,
1071 1071   $paramOffset)
1072 1072   #elseif ($isList)
1073 1073   #livetable_repeatParams("? in elements($column)", " $joinOperator ", $valueCount, $paramPrefix, $paramOffset)