Modifications pour le document Solr Search Macros

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

Depuis la version 3.1
modifié par Florent Charton
sur 2026/01/08 10:59
Commentaire de modification : Install extension [org.xwiki.platform:xwiki-platform-search-solr-ui/17.4.8]
À la version 5.1
modifié par Florent Charton
sur 2026/03/13 11:04
Commentaire de modification : Install extension [org.xwiki.platform:xwiki-platform-search-solr-ui/17.10.4]

Résumé

Détails

Propriétés de la Page
Contenu
... ... @@ -4,13 +4,11 @@
4 4  #set ($rangePattern = $regextool.compile('^[\[{](.+) TO (.+)[\]}]$'))
5 5  #set ($wildcardPattern = $regextool.compile('^\(.*\*.*\)$'))
6 6  
7 -#macro (displaySearchForm)
7 +#macro (_displaySearchFormBegin)
8 8   #set($void = $services.progress.startStep('#displaySearchForm'))
9 9   {{html clean="false"}}
10 - <form class="search-form row" action="$doc.getURL()" role="search">
10 + <form class="search-form" action="$doc.getURL()" role="search">
11 11   <div class="hidden">
12 - <input type="hidden" name="sort" value="$!escapetool.xml($sort)"/>
13 - <input type="hidden" name="sortOrder" value="$!escapetool.xml($sortOrder)"/>
14 14   <input type="hidden" name="highlight" value="$highlightEnabled"/>
15 15   <input type="hidden" name="facet" value="$facetEnabled"/>
16 16   ## The parameter used to determine if the request has been redirected with default search filters.
... ... @@ -27,27 +27,36 @@
27 27   #end
28 28   #end
29 29   </div>
30 - <div class="col-xs-12 col-sm-6">
28 + <div class="search-bar">
31 31   <div class="input-group">
30 + <input id="search-page-bar-input" type="search" name="text" class="form-control"
31 + title="$escapetool.xml($services.localization.render('search.page.bar.query.title'))"
32 + placeholder="$escapetool.xml($services.localization.render('search.page.bar.query.title'))"
33 + value="$escapetool.xml($text)"/>
32 32   <label class='sr-only' for='search-page-bar-input'>
33 - $services.localization.render('search.page.bar.query.title')
35 + $escapetool.xml($services.localization.render('search.page.bar.query.title'))
34 34   </label>
35 - <input id='search-page-bar-input' type='search' name='text' class='form-control'
36 - placeholder="$services.localization.render('search.page.bar.query.title')"
37 - title="$services.localization.render('search.page.bar.query.title')" value="$escapetool.xml($text)"/>
38 38   <span class="input-group-btn">
39 39   <button type="submit" class="btn btn-primary">
40 40   $services.icon.renderHTML('search')
41 - <span class="sr-only">$services.localization.render('search.page.bar.submit')</span>
40 + <span>$escapetool.xml($services.localization.render('search.page.bar.submit'))</span>
42 42   </button>
43 43   </span>
44 44   </div>
45 45   </div>
46 - </form>
47 47   {{/html}}
48 48   #set($void = $services.progress.endStep())
49 49  #end
50 50  
49 +## We make sure the html block in this macro is not considered as inline to avoid generating extra `p` tags.
50 +#macro (_displaySearchFormEnd)
51 +
52 + {{html clean="false"}}
53 + </form>
54 + {{/html}}
55 +
56 +#end
57 +
51 51  #macro (displaySearchDebugInfo)
52 52   (% class="search-debug" %)(((
53 53   === Debug Information ===
... ... @@ -112,12 +112,12 @@
112 112   #end
113 113   #extendQueryString($url $resetParameters)
114 114   [[{{translation key="solr.facets.resetAll"}}>>path:$url
115 - ||class="search-facets-action-reset"]]## Continue in the same paragraph.
122 + ||class="search-facets-action-reset force-no-underline"]]## Continue in the same paragraph.
116 116   {{html clean="false"}}
117 - <a href="#" class="search-facets-action-collapseAll hidden">
124 + <a href="#" class="search-facets-action-collapseAll hidden force-no-underline">
118 118   $escapetool.xml($services.localization.render('solr.facets.collapseAll'))
119 119   </a>
120 - <a href="#" class="search-facets-action-expandAll hidden">
127 + <a href="#" class="search-facets-action-expandAll hidden force-no-underline">
121 121   $escapetool.xml($services.localization.render('solr.facets.expandAll'))
122 122   </a>
123 123   <span class="clearfloats"></span>
... ... @@ -149,9 +149,10 @@
149 149   ## Show active facets (that have selected values or that have an explicit limit on the number of values, i.e.
150 150   ## pagination) as expanded. Collapse the rest, otherwise you have to scroll to see all the available facets.
151 151   #set ($facetValuesLimit = $request.getParameter("l_$facetField.name"))
152 - <div class="search-facet#if ($facetRequestValues || $facetValuesLimit) expanded#end" data-name="$facetField.name">
153 - #displaySearchFacetHeader($facetField)
154 - #displaySearchFacetBody($facetField)
159 + <div class="search-facet" data-name="$facetField.name">
160 + #set ($expanded = ($facetRequestValues || $facetValuesLimit))
161 + #displaySearchFacetHeader($facetField $expanded)
162 + #displaySearchFacetBody($facetField $expanded)
155 155   </div>
156 156   #end
157 157  #end
... ... @@ -168,7 +168,7 @@
168 168   #setVariable("$property" $classDocument.xWikiClass.get($classPropertyReference.name))
169 169  #end
170 170  
171 -#macro (displaySearchFacetHeader $facetField)
179 +#macro (displaySearchFacetHeader $facetField $expanded)
172 172   #set ($facetPrettyNameKey = "solr.field.$facetField.name")
173 173   #if ($services.localization.get($facetPrettyNameKey))
174 174   #set ($facetPrettyName = $services.localization.render($facetPrettyNameKey))
... ... @@ -183,17 +183,21 @@
183 183   #set ($facetPrettyName = $facetField.name)
184 184   #end
185 185   <div class="search-facet-header">
186 - <span id="$escapetool.xml($facetField.name)-toggler-hint">$escapetool.xml($facetPrettyName)</span>
187 - <button class="btn btn-xs facet-toggler"
188 - aria-controls="$escapetool.xml($facetField.name)-dropdown"
189 - aria-labelledby="$escapetool.xml($facetField.name)-toggler-hint">
194 + <label>$escapetool.xml($facetPrettyName)
195 + <button class="btn btn-xs facet-toggle#if(!$expanded) collapsed#end"
196 + type="button"
197 + data-toggle="collapse"
198 + data-target="#$escapetool.xml($facetField.name)-dropdown"
199 + aria-expanded="$expanded"
200 + aria-controls="$escapetool.xml($facetField.name)-dropdown">
190 190   $services.icon.renderHTML('caret-down')
191 191   </button>
203 + </label>
192 192   </div>
193 193  #end
194 194  
195 -#macro (displaySearchFacetBody $facetField)
196 - <div id="$escapetool.xml($facetField.name)-dropdown" class="search-facet-body">
207 +#macro (displaySearchFacetBody $facetField $expanded)
208 + <div id="$escapetool.xml($facetField.name)-dropdown" class="search-facet-body collapse#if($expanded) in#end">
197 197   #set ($facetDisplayer = $solrConfig.facetDisplayers.get($facetField.name))
198 198   #if (!$facetDisplayer && $facetField.name.startsWith('property.'))
199 199   ## Choose a facet displayer based on the property type.
... ... @@ -246,7 +246,7 @@
246 246   #displaySearchFacetValue($facetValue $customQueryStringParameters $customValueDisplayer false)
247 247  #end
248 248  
249 -#macro (displaySearchFacetValue $facetValue $customQueryStringParameters $customValueDisplayer $displayToggler)
261 +#macro (displaySearchFacetValue $facetValue $customQueryStringParameters $customValueDisplayer $displayToggle)
250 250   #set ($selectedValues = [])
251 251   #if ($facetRequestValues)
252 252   #set ($discard = $selectedValues.addAll($facetRequestValues.subList(0, $facetRequestValues.size())))
... ... @@ -261,7 +261,8 @@
261 261   #set ($discard = $queryStringParameters.putAll($customQueryStringParameters))
262 262   #end
263 263   #extendQueryString($url $queryStringParameters)
264 - <a href="$url" class="itemName#if ($selected) selected#end#if ($facetValue.name == '') empty#end">
276 + <a href="$url" class="itemName#if ($selected) selected#end#if ($facetValue.name == '') empty#end"
277 + #if ($facetValue.name != '')data-facetvalue="$escapetool.xml($facetValue.name)"#end>
265 265   #if ($facetValue.name == '')
266 266   #set ($facetPrettyValueKey = "solr.field.${facetField.name}.emptyValue")
267 267   #if (!$services.localization.get($facetPrettyValueKey))
... ... @@ -278,8 +278,8 @@
278 278   #end
279 279   </a>
280 280   <div class="itemCount">$facetValue.count</div>
281 - #if ($displayToggler)
282 - <button class="btn btn-xs facet-value-toggler">
294 + #if ($displayToggle)
295 + <button class="btn btn-xs facet-value-toggle">
283 283   <span class='sr-only'>$escapetool.xml($facetPrettyValue)</span>
284 284   $services.icon.renderHTML('caret-down')
285 285   </button>
... ... @@ -306,7 +306,7 @@
306 306   #end
307 307  #end
308 308  
309 -#macro (displaySearchResultsSort)
322 +#macro (_displaySearchResultsControls)
310 310   #set ($defaultSortOrder = $solrConfig.sortFields.get($type))
311 311   #if (!$defaultSortOrder)
312 312   #set ($defaultSortOrder = {'score': 'desc'})
... ... @@ -315,28 +315,63 @@
315 315   'asc': $services.icon.render('caret-up'),
316 316   'desc': $services.icon.render('caret-down')
317 317   })
318 - (% class="search-options" %)
319 - * {{translation key="solr.options"/}}
320 - #if($highlightEnabled)#extendQueryString($url {'highlight': [false]})#else#extendQueryString($url {'highlight': [true]})#end
321 - * [[{{translation key="solr.options.highlight"/}}>>path:${url}||class="options-item#if($highlightEnabled) active#end" title="$services.localization.render('solr.options.highlight.title')"]]
322 - #if($facetEnabled)#extendQueryString($url {'facet': [false]})#else#extendQueryString($url {'facet': [true]})#end
323 - * [[{{translation key="solr.options.facet"/}}>>path:${url}||class="options-item#if($facetEnabled) active#end" title="$services.localization.render('solr.options.facet.title')"]]
331 + (% class='search-results-controls' %)
332 + (((
324 324  
325 - (% class="search-results-sort" %)
326 - * {{translation key="solr.sortBy"/}}
327 - #foreach ($entry in $defaultSortOrder.entrySet())
328 - #set ($class = 'sort-item')
329 - #set ($sortOrderIndicator = $NULL)
330 - #set ($targetSortOrder = $entry.value)
331 - #if ($sort == $entry.key)
332 - #set ($class = "$class active")
333 - #set ($sortOrderHint = $services.localization.render("solr.sortOrder.$sortOrder"))
334 - #set ($sortOrderIndicator = "(% class=""sort-item-order"" title=""$sortOrderHint"" %)$sortOrderSymbol.get($sortOrder)(%%)")
335 - #set ($targetSortOrder = "#if ($sortOrder == 'asc')desc#{else}asc#end")
336 - #end
337 - #extendQueryString($url {'sort': [$entry.key], 'sortOrder': [$targetSortOrder]})
338 - * [[{{translation key="solr.sortBy.$entry.key"/}}$!sortOrderIndicator>>path:${url}||class="$class"]]
339 - #end
334 + {{html clean="false"}}
335 + <div class="search-results-sort">
336 + <label for="sort-by-input" class="sr-only">$escapetool.xml($services.localization.render('search.solr.sortBy.hint'))</label>##
337 + <select id="sort-by-input" name="sort">
338 + #foreach ($entry in $defaultSortOrder.entrySet())
339 + <option class="sort-item" value="$entry.key" #if($sort == $entry.key)selected='selected'#end>
340 + #set ($sortOptionNameList = $entry.key.split('_'))
341 + #set ($camelCasedSortOptionName = $sortOptionNameList.get(0))
342 + #foreach ($namePart in $sortOptionNameList.subList(1, $sortOptionNameList.size()))
343 + #set ($camelCasedSortOptionName = "${camelCasedSortOptionName}$stringtool.capitalize($namePart)")
344 + #end
345 + $escapetool.xml($services.localization.render("search.solr.sortBy.field.$camelCasedSortOptionName"))
346 + </option>
347 + #end
348 + </select>##
349 + <label class="form-control" title="$escapetool.xml($services.localization.render("search.solr.sortOrder.$sortOrder"))">##
350 + <input id="sort-order-input" type="checkbox" name="sortOrder" value="asc" #if ("$!sortOrder" == 'asc')checked="checked"#end/>##
351 + $services.icon.renderHTML('sort-descending')##
352 + $services.icon.renderHTML('sort-ascending')##
353 + <span class="sr-only">$escapetool.xml($services.localization.render("search.solr.sortOrder.$sortOrder"))</span>##
354 + </label>##
355 + </div>
356 + <div class="search-options">
357 + <ul>##
358 + <li>##
359 + <label>##
360 + <input id="option-highlight-input" type="checkbox" class="options-item" value="true"
361 + data-query-name="highlight"
362 + aria-describedby="option-highlight-description"
363 + title="$escapetool.xml($services.localization.render('solr.options.highlight.title'))"
364 + #if($highlightEnabled)checked#end/>##
365 + $escapetool.xml($services.localization.render('search.solr.options.showHighlight'))##
366 + </label>##
367 + <span id="option-highlight-description" class="sr-only">
368 + $escapetool.xml($services.localization.render('solr.options.highlight.title'))
369 + </span>##
370 + </li>##
371 + <li>##
372 + <label>##
373 + <input id="option-facet-input" type="checkbox" class="options-item" value="true" data-query-name="facet"
374 + aria-describedby="option-facet-description"
375 + title="$escapetool.xml($services.localization.render('solr.options.facet.title'))"
376 + #if($facetEnabled)checked#end/>##
377 + $escapetool.xml($services.localization.render('search.solr.options.showFacet'))
378 + </label>##
379 + <span id="option-facet-description" class="sr-only">
380 + $escapetool.xml($services.localization.render('solr.options.facet.title'))
381 + </span>##
382 + </li>##
383 + </ul>##
384 + </div>
385 + {{/html}}
386 +
387 + )))
340 340  #end
341 341  
342 342  #macro (extendQueryString $url $extraParameters)
... ... @@ -381,7 +381,8 @@
381 381   ## Add the parameters required to output the RSS feed instead of the search UI.
382 382   #set ($discard = $parameters.put('outputSyntax', 'plain'))
383 383   #set ($discard = $parameters.put('media', 'rss'))
384 - <a href="$doc.getURL('get', $escapetool.url($parameters))" class="hasIcon iconRSS">
432 + <a href="$doc.getURL('get', $escapetool.url($parameters))">
433 + $services.icon.renderHTML('rss')
385 385   $services.localization.render('search.rss', ["[$escapetool.xml($text)]"])
386 386   </a>
387 387   {{/html}}
... ... @@ -413,7 +413,7 @@
413 413   #displaySearchResultLocation()
414 414   <div class="search-result-author">
415 415   $services.localization.render('core.footer.modification', [
416 - "#displayUserProfileLink($searchResult.author $searchResult.author_display)",
465 + "#displayUser($searchResult.author {'useInlineHTML': true})",
417 417   $xwiki.formatDate($searchResult.date)
418 418   ])
419 419   </div>
... ... @@ -435,7 +435,7 @@
435 435   </h2>
436 436   #displaySearchResultLocation($searchResult)
437 437   <div class="search-result-uploader">
438 - #set ($uploader = "#displayUserProfileLink($searchResult.attauthor.get(0) $searchResult.attauthor_display.get(0))")
487 + #set ($uploader = "#displayUser($searchResult.attauthor.get(0) {'useInlineHTML': true})")
439 439   #set ($uploadDate = $xwiki.formatDate($searchResult.attdate.get(0)))
440 440   #set ($fileSize = "#dynamicsize($searchResult.attsize.get(0))")
441 441   $services.localization.render('solr.result.uploadedBy', [$uploader, $uploadDate, $fileSize])
... ... @@ -486,15 +486,6 @@
486 486   </div>
487 487  #end
488 488  
489 -#macro (displayUserProfileLink $userReference $userName)
490 -#if ($userReference)
491 -## We could test if the specified user exists but we want to speed up the search.
492 -<a href="$xwiki.getURL($userReference)">$escapetool.xml($userName)</a>##
493 -#else
494 -$services.localization.render('core.users.unknownUser')##
495 -#end
496 -#end
497 -
498 498  #macro (displaySearchResultHighlighting $searchResult)
499 499   #getSearchResultHighlighting($searchResult $highlighting)
500 500   #if ($highlighting.size() > 0)
... ... @@ -518,12 +518,10 @@
518 518   #end
519 519   </dl>
520 520   #if ($highlighting.size() > 1)
521 - ## We wrap the link in a DIV because otherwise the HTML cleaning generates a paragraph.
522 - <div>
523 - <a href="#" class="search-result-highlightAll hidden">
524 - $escapetool.xml($services.localization.render('solr.result.highlightAll'))
525 - </a>
526 - </div>
561 + <button class="search-result-highlightAll btn btn-xs btn-default hidden">
562 + $escapetool.xml($services.localization.render('solr.result.highlightAll'))
563 + $services.icon.renderHTML('right')
564 + </button>
527 527   #end
528 528   #end
529 529  #end
... ... @@ -595,9 +595,10 @@
595 595   ## Set query parameters.
596 596   #set ($discard = $query.setLimit($rows))
597 597   #set ($discard = $query.setOffset($start))
636 + #set ($discard = $query.addFilter('searchExclusions/solr'))
598 598   #set ($discard = $query.bindValue('sort', "${sort} ${sortOrder}"))
599 599   #set ($discard = $query.bindValue('tie', $solrConfig.tieBreaker))
600 - #set ($discard = $query.bindValue('mm', $solrConfig.minShouldMatch))
639 + #set ($discard = $query.bindValue('mm', $solrConfig.minShouldMatch))
601 601   #setQueryFields($query)
602 602   #setPhraseFields($query)
603 603   #setFacetFields($query)
... ... @@ -810,6 +810,8 @@
810 810   #if ("$!sort" == '')
811 811   #set ($sort = 'score')
812 812   #end
852 + ## If at any point this default behavior is changed, be extra careful with "#sort-order-input" initialization.
853 + ## We assume here that empty values are mapped to "desc" (meaning that we use "asc" as the checkbox value).
813 813   #set ($sortOrder = $request.sortOrder)
814 814   #if ("$!sortOrder" == '')
815 815   #set ($sortOrder = 'desc')
... ... @@ -851,12 +851,15 @@
851 851   {{/html}}
852 852  
853 853   #end
854 - #displaySearchForm()
895 + #_displaySearchFormBegin()
855 855   #if ($text != '')
856 856   #getSearchResults()
898 + #_displaySearchResultsControls()
899 + #_displaySearchFormEnd()
857 857   #if ($debug)
858 858   #displaySearchDebugInfo()
859 859   #end
903 +
860 860   (% class="search-results-container row" %)(((
861 861   #if ($facetEnabled)
862 862   (% class="col-xs-12 col-sm-4 col-sm-push-8 col-md-3 col-md-push-9" %)(((
... ... @@ -865,11 +865,11 @@
865 865   #end
866 866   (% class="search-results-left col-xs-12#if ($facetEnabled) col-sm-8 col-sm-pull-4 col-md-9 col-md-pull-3#end" %)
867 867   (((
868 - #displaySearchResultsSort()
869 -
870 870   #displaySearchResults()
871 871   )))
872 872   )))
915 + #else
916 + #_displaySearchFormEnd()
873 873   #end
874 874   )))
875 875   #set($void = $services.progress.popLevel())