Wiki source code of Solr Search Macros
Last modified by Bimit Administrator on 05.12.2023, 13:23
Hide last authors
author | version | line-number | content |
---|---|---|---|
![]() |
1.1 | 1 | {{template name="hierarchy_macros.vm" /}} |
2 | |||
3 | {{velocity output='false'}} | ||
4 | #set ($rangePattern = $regextool.compile('^[\[{](.+) TO (.+)[\]}]$')) | ||
5 | #set ($wildcardPattern = $regextool.compile('^\(.*\*.*\)$')) | ||
6 | |||
7 | #macro (displaySearchForm) | ||
8 | #set($void = $services.progress.startStep('#displaySearchForm')) | ||
9 | {{html clean="false"}} | ||
10 | <form class="search-form row" action="$doc.getURL()" role="search"> | ||
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 | <input type="hidden" name="highlight" value="$highlightEnabled"/> | ||
15 | <input type="hidden" name="facet" value="$facetEnabled"/> | ||
16 | ## The parameter used to determine if the request has been redirected with default search filters. | ||
17 | <input type="hidden" name="r" value="$!escapetool.xml($request.r)"/> | ||
18 | #if ("$!request.debug" != '') | ||
19 | <input type="hidden" name="debug" value="$escapetool.xml($request.debug)"/> | ||
20 | #end | ||
21 | ## Preserve the current facet values when submitting a new search query. | ||
22 | #foreach ($entry in $request.parameterMap.entrySet()) | ||
23 | #if ($entry.key.startsWith('f_') || $entry.key.startsWith('l_')) | ||
24 | #foreach ($value in $entry.value) | ||
25 | <input type="hidden" name="$escapetool.xml($entry.key)" value="$escapetool.xml($value)"/> | ||
26 | #end | ||
27 | #end | ||
28 | #end | ||
29 | </div> | ||
30 | <div class="col-xs-12 col-sm-6"> | ||
31 | <div class="input-group"> | ||
![]() |
2.1 | 32 | <label class='sr-only' for='search-page-bar-input'> |
33 | $services.localization.render('search.page.bar.query.title') | ||
34 | </label> | ||
35 | <input id='search-page-bar-input' type='search' name='text' class='form-control withTip useTitleAsTip' | ||
![]() |
1.1 | 36 | title="$services.localization.render('search.page.bar.query.title')" value="$escapetool.xml($text)"/> |
37 | <span class="input-group-btn"> | ||
38 | <button type="submit" class="btn btn-primary"> | ||
39 | $services.icon.renderHTML('search') | ||
40 | <span class="sr-only">$services.localization.render('search.page.bar.submit')</span> | ||
41 | </button> | ||
42 | </span> | ||
43 | </div> | ||
44 | </div> | ||
45 | </form> | ||
46 | {{/html}} | ||
47 | #set($void = $services.progress.endStep()) | ||
48 | #end | ||
49 | |||
50 | #macro (displaySearchDebugInfo) | ||
51 | (% class="search-debug" %)((( | ||
52 | === Debug Information === | ||
53 | #set ($debugMap = $searchResponse.debugMap) | ||
54 | #if ($debugMap) | ||
55 | |||
56 | {{html clean="false"}} | ||
57 | <dl> | ||
58 | <dt>Query Parser</dt> | ||
59 | <dd>$!escapetool.xml($debugMap.get('QParser'))</dd> | ||
60 | <dt>Parsed Query</dt> | ||
61 | <dd>$!escapetool.xml($debugMap.get('parsedquery_toString'))</dd> | ||
62 | <dt>Filter Queries</dt> | ||
63 | <dd> | ||
64 | <ul> | ||
65 | #foreach ($filterQuery in $debugMap.get('filter_queries')) | ||
66 | <li>$!escapetool.xml($filterQuery)</li> | ||
67 | #end | ||
68 | </ul> | ||
69 | </dd> | ||
70 | <dt>Processing Time</dt> | ||
71 | <dd> | ||
72 | #displayProcessingTime($debugMap.get('timing')) | ||
73 | </dd> | ||
74 | </dl> | ||
75 | {{/html}} | ||
76 | #end | ||
77 | ))) | ||
78 | #end | ||
79 | |||
80 | #macro (displayProcessingTime $timing) | ||
81 | <ul> | ||
82 | ## The timing is not a Map but a NamedList. | ||
83 | #foreach ($entry in $timing) | ||
84 | <li> | ||
85 | $!escapetool.xml($entry.key): | ||
86 | #if ($entry.value.time && $entry.value.size() > 1) | ||
87 | #displayProcessingTime($entry.value) | ||
88 | #else | ||
89 | $!escapetool.xml($entry.value) | ||
90 | #end | ||
91 | </li> | ||
92 | #end | ||
93 | </ul> | ||
94 | #end | ||
95 | |||
96 | #macro (displaySearchFacets $searchResponse) | ||
97 | #set($void = $services.progress.startStep('#displaySearchFacets')) | ||
98 | (% class="search-facets collapsed-xs xform" %)((( | ||
99 | (% class="search-facets-header" %)((( | ||
100 | **{{translation key="solr.facets.title"/}}** (% class="pull-right visible-xs" %)$services.icon.render('search-plus') | ||
101 | |||
102 | (% class="xHint" %) | ||
103 | {{translation key="solr.facets.hint"/}} | ||
104 | ))) | ||
105 | (% class="search-facets-actions" %)((( | ||
106 | #set ($resetParameters = {}) | ||
107 | #foreach ($parameter in $request.parameterMap.entrySet()) | ||
108 | #if ($parameter.key.startsWith('f_') || $parameter.key.startsWith('l_')) | ||
109 | #set ($discard = $resetParameters.put($parameter.key, [])) | ||
110 | #end | ||
111 | #end | ||
112 | #extendQueryString($url $resetParameters) | ||
113 | [[{{translation key="solr.facets.resetAll"}}>>path:$url | ||
114 | ||class="search-facets-action-reset"]]## Continue in the same paragraph. | ||
115 | {{html clean="false"}} | ||
116 | <a href="#" class="search-facets-action-collapseAll hidden"> | ||
117 | $escapetool.xml($services.localization.render('solr.facets.collapseAll')) | ||
118 | </a> | ||
119 | <a href="#" class="search-facets-action-expandAll hidden"> | ||
120 | $escapetool.xml($services.localization.render('solr.facets.expandAll')) | ||
121 | </a> | ||
122 | <span class="clearfloats"></span> | ||
123 | {{/html}} | ||
124 | ))) | ||
125 | {{html clean="false"}} | ||
126 | #foreach ($facetField in $searchResponse.facetFields) | ||
127 | #displaySearchFacet($facetField) | ||
128 | #end | ||
129 | {{/html}} | ||
130 | ))) | ||
131 | #set($void = $services.progress.endStep()) | ||
132 | #end | ||
133 | |||
134 | #macro (displaySearchFacet $facetField) | ||
135 | #set ($facetRequestParameter = "f_$facetField.name") | ||
136 | #set ($facetRequestValues = $request.getParameterValues($facetRequestParameter)) | ||
137 | #set ($facetValues = []) | ||
138 | #foreach ($facetValue in $facetField.values) | ||
139 | ## Keep only the values that have at least one match or that are specified on the request. | ||
140 | #if ($facetValue.count > 0 || ($facetRequestValues && $facetRequestValues.contains($facetValue.name))) | ||
141 | #set ($discard = $facetValues.add($facetValue)) | ||
142 | #end | ||
143 | #end | ||
144 | ## Facets that perform a 'facet.prefix'-based drill down (see https://wiki.apache.org/solr/HierarchicalFaceting) don't | ||
145 | ## have any values (not even with 0 count) when the prefix specified on the request doesn't have any "sub-values", but | ||
146 | ## we still want to display them to allow the user to reset the filter. | ||
147 | #if ($facetValues.size() > 0 || $facetRequestValues) | ||
148 | ## Show active facets (that have selected values or that have an explicit limit on the number of values, i.e. | ||
149 | ## pagination) as expanded. Collapse the rest, otherwise you have to scroll to see all the available facets. | ||
150 | #set ($facetValuesLimit = $request.getParameter("l_$facetField.name")) | ||
151 | <div class="search-facet#if ($facetRequestValues || $facetValuesLimit) expanded#end" data-name="$facetField.name"> | ||
152 | #displaySearchFacetHeader($facetField) | ||
153 | #displaySearchFacetBody($facetField) | ||
154 | </div> | ||
155 | #end | ||
156 | #end | ||
157 | |||
158 | #macro (getXClassProperty $solrFieldName $property $classPropertyReference) | ||
159 | ## Remove the 'property.' prefix and the data type suffix. | ||
160 | #set ($stringReference = $stringtool.substringBeforeLast($solrFieldName.substring(9), '_')) | ||
161 | ## Note that the class property reference is resolved relative to the current wiki. This means the class must be | ||
162 | ## available on the wiki where the search is performed. | ||
163 | #set ($classPropertyReference = $NULL) | ||
164 | #setVariable("$classPropertyReference" $services.model.resolveClassProperty($stringReference, 'solr')) | ||
165 | #set ($classDocument = $xwiki.getDocument($classPropertyReference.parent)) | ||
166 | #set ($property = $NULL) | ||
167 | #setVariable("$property" $classDocument.xWikiClass.get($classPropertyReference.name)) | ||
168 | #end | ||
169 | |||
170 | #macro (displaySearchFacetHeader $facetField) | ||
171 | #set ($facetPrettyNameKey = "solr.field.$facetField.name") | ||
172 | #if ($services.localization.get($facetPrettyNameKey)) | ||
173 | #set ($facetPrettyName = $services.localization.render($facetPrettyNameKey)) | ||
174 | #elseif ($facetField.name.startsWith('property.')) | ||
175 | ## Display the translated property pretty name. | ||
176 | #getXClassProperty($facetField.name $property $classPropertyReference) | ||
177 | #set ($facetPrettyName = $property.translatedPrettyName) | ||
178 | #if ("$!facetPrettyName" == '') | ||
179 | #set ($facetPrettyName = $classPropertyReference.name) | ||
180 | #end | ||
181 | #else | ||
182 | #set ($facetPrettyName = $facetField.name) | ||
183 | #end | ||
![]() |
2.1 | 184 | <div class="search-facet-header"> |
185 | <span id="$escapetool.xml($facetField.name)-toggler-hint">$escapetool.xml($facetPrettyName)</span> | ||
186 | <button class="btn btn-xs facet-toggler" | ||
187 | aria-controls="$escapetool.xml($facetField.name)-dropdown" | ||
188 | aria-labelledby="$escapetool.xml($facetField.name)-toggler-hint"> | ||
189 | $services.icon.renderHTML('caret-down') | ||
190 | </button> | ||
191 | </div> | ||
![]() |
1.1 | 192 | #end |
193 | |||
194 | #macro (displaySearchFacetBody $facetField) | ||
![]() |
2.1 | 195 | <div id="$escapetool.xml($facetField.name)-dropdown" class="search-facet-body"> |
![]() |
1.1 | 196 | #set ($facetDisplayer = $solrConfig.facetDisplayers.get($facetField.name)) |
197 | #if (!$facetDisplayer && $facetField.name.startsWith('property.')) | ||
198 | ## Choose a facet displayer based on the property type. | ||
199 | #getXClassProperty($facetField.name $property) | ||
200 | ## We rely on configuration instead of using a naming convention like "Main.Solr${property.classType}Facet" | ||
201 | ## because most of the property types don't need a custom facet displayer. | ||
202 | #set ($facetDisplayer = $solrConfig.facetDisplayersByPropertyType.get($property.classType)) | ||
203 | #end | ||
204 | #if ($facetDisplayer) | ||
205 | #set ($facetDisplayer = $xwiki.getDocument($facetDisplayer)) | ||
206 | #if ("$!facetDisplayer.content" != '') | ||
207 | $!facetDisplayer.getRenderedContent(false) | ||
208 | #else | ||
209 | #displaySearchFacetValues($facetValues) | ||
210 | #end | ||
211 | #else | ||
212 | #displaySearchFacetValues($facetValues) | ||
213 | #end | ||
214 | </div> | ||
215 | #end | ||
216 | |||
217 | #macro (displaySearchFacetValues $facetValues $customQueryStringParameters $customValueDisplayer) | ||
218 | #if ($facetValues.size() > 0) | ||
219 | <ul> | ||
220 | #displaySearchFacetValuesLimited($facetValues $customQueryStringParameters $customValueDisplayer) | ||
221 | </ul> | ||
222 | #end | ||
223 | #end | ||
224 | |||
225 | #macro (displaySearchFacetValuesLimited $facetValues $customQueryStringParameters $customValueDisplayer) | ||
226 | #set ($limitRequestParameter = "l_$facetField.name") | ||
227 | #set ($limit = $numbertool.toNumber($request.getParameter($limitRequestParameter)).intValue()) | ||
228 | #if ("$!limit" == '') | ||
229 | #set ($limit = $solrConfig.facetPaginationStep) | ||
230 | #end | ||
231 | #set ($limit = $mathtool.max($mathtool.min($limit, $facetValues.size()), 0)) | ||
232 | #foreach ($facetValue in $facetValues) | ||
233 | #if ($foreach.index < $limit) | ||
234 | <li>#displaySearchFacetValue($facetValue $customQueryStringParameters $customValueDisplayer)</li> | ||
235 | #else | ||
236 | #extendQueryString($url {$limitRequestParameter: [$mathtool.add($limit, $solrConfig.facetPaginationStep)]}) | ||
237 | <li><a href="$url" class="more">… $escapetool.xml($services.localization.render( | ||
238 | 'solr.facets.moreValues', [$mathtool.sub($facetValues.size(), $limit)]))</a></li> | ||
239 | #break | ||
240 | #end | ||
241 | #end | ||
242 | #end | ||
243 | |||
244 | #macro (displaySearchFacetValue $facetValue $customQueryStringParameters $customValueDisplayer) | ||
![]() |
2.1 | 245 | #displaySearchFacetValue($facetValue $customQueryStringParameters $customValueDisplayer false) |
246 | #end | ||
247 | |||
248 | #macro (displaySearchFacetValue $facetValue $customQueryStringParameters $customValueDisplayer $displayToggler) | ||
![]() |
1.1 | 249 | #set ($selectedValues = []) |
250 | #if ($facetRequestValues) | ||
251 | #set ($discard = $selectedValues.addAll($facetRequestValues.subList(0, $facetRequestValues.size()))) | ||
252 | #end | ||
253 | #set ($selected = $selectedValues.remove($facetValue.name)) | ||
254 | #if (!$selected) | ||
255 | #set ($discard = $selectedValues.add($facetValue.name)) | ||
256 | #end | ||
257 | ## Reset the pagination because the number of results can change when a facet is applied. | ||
258 | #set ($queryStringParameters = {$facetRequestParameter: $selectedValues, 'firstIndex': []}) | ||
259 | #if ($customQueryStringParameters) | ||
260 | #set ($discard = $queryStringParameters.putAll($customQueryStringParameters)) | ||
261 | #end | ||
262 | #extendQueryString($url $queryStringParameters) | ||
263 | <a href="$url" class="itemName#if ($selected) selected#end#if ($facetValue.name == '') empty#end"> | ||
264 | #if ($facetValue.name == '') | ||
265 | #set ($facetPrettyValueKey = "solr.field.${facetField.name}.emptyValue") | ||
266 | #if (!$services.localization.get($facetPrettyValueKey)) | ||
267 | #set ($facetPrettyValueKey = "solr.facets.emptyValue") | ||
268 | #end | ||
269 | #set ($facetPrettyValue = $services.localization.render($facetPrettyValueKey)) | ||
270 | #else | ||
271 | #set ($facetPrettyValue = $facetValue.name) | ||
272 | #end | ||
273 | #if ($customValueDisplayer) | ||
274 | #evaluate("${escapetool.h}${customValueDisplayer}(${escapetool.d}facetPrettyValue)") | ||
275 | #else | ||
276 | $escapetool.xml($facetPrettyValue) | ||
277 | #end | ||
278 | </a> | ||
![]() |
2.1 | 279 | <div class="itemCount">$facetValue.count</div> |
280 | #if ($displayToggler) | ||
281 | <button class="btn btn-xs facet-value-toggler"> | ||
282 | <span class='sr-only'>$escapetool.xml($facetPrettyValue)</span> | ||
283 | $services.icon.renderHTML('caret-down') | ||
284 | </button> | ||
285 | #end | ||
![]() |
1.1 | 286 | #end |
287 | |||
288 | #** | ||
289 | * If the facet has values specified on the request then keep only those that are included in the list of matched facet | ||
290 | * values. Don't use this macro for date or range facets because in this case the values specified on the request are | ||
291 | * never found as is in the list of facet values (e.g. a range will match multiple facet values). This macro ensures | ||
292 | * that the URL to select/unselect a facet value doesn't keep unmatched values (otherwise the URL will have values that | ||
293 | * you cannot remove using the facet UI). | ||
294 | *# | ||
295 | #macro (retainMatchedRequestValues) | ||
296 | #if ($facetRequestValues) | ||
297 | #set ($matchedValues = []) | ||
298 | #foreach ($facetValue in $facetValues) | ||
299 | #set ($discard = $matchedValues.add($facetValue.name)) | ||
300 | #end | ||
301 | #set ($matchedRequestValues = []) | ||
302 | #set ($discard = $matchedRequestValues.addAll($facetRequestValues.subList(0, $facetRequestValues.size()))) | ||
303 | #set ($discard = $matchedRequestValues.retainAll($matchedValues)) | ||
304 | #set ($facetRequestValues = $matchedRequestValues) | ||
305 | #end | ||
306 | #end | ||
307 | |||
308 | #macro (displaySearchResultsSort) | ||
309 | #set ($defaultSortOrder = $solrConfig.sortFields.get($type)) | ||
310 | #if (!$defaultSortOrder) | ||
311 | #set ($defaultSortOrder = {'score': 'desc'}) | ||
312 | #end | ||
313 | #set ($sortOrderSymbol = { | ||
314 | 'asc': $services.icon.render('caret-up'), | ||
315 | 'desc': $services.icon.render('caret-down') | ||
316 | }) | ||
317 | (% class="search-options" %) | ||
318 | * {{translation key="solr.options"/}} | ||
319 | #if($highlightEnabled)#extendQueryString($url {'highlight': [false]})#else#extendQueryString($url {'highlight': [true]})#end | ||
320 | * [[{{translation key="solr.options.highlight"/}}>>path:${url}||class="options-item#if($highlightEnabled) active#end" title="$services.localization.render('solr.options.highlight.title')"]] | ||
321 | #if($facetEnabled)#extendQueryString($url {'facet': [false]})#else#extendQueryString($url {'facet': [true]})#end | ||
322 | * [[{{translation key="solr.options.facet"/}}>>path:${url}||class="options-item#if($facetEnabled) active#end" title="$services.localization.render('solr.options.facet.title')"]] | ||
323 | |||
324 | (% class="search-results-sort" %) | ||
325 | * {{translation key="solr.sortBy"/}} | ||
326 | #foreach ($entry in $defaultSortOrder.entrySet()) | ||
327 | #set ($class = 'sort-item') | ||
328 | #set ($sortOrderIndicator = $NULL) | ||
329 | #set ($targetSortOrder = $entry.value) | ||
330 | #if ($sort == $entry.key) | ||
331 | #set ($class = "$class active") | ||
332 | #set ($sortOrderHint = $services.localization.render("solr.sortOrder.$sortOrder")) | ||
333 | #set ($sortOrderIndicator = "(% class=""sort-item-order"" title=""$sortOrderHint"" %)$sortOrderSymbol.get($sortOrder)(%%)") | ||
334 | #set ($targetSortOrder = "#if ($sortOrder == 'asc')desc#{else}asc#end") | ||
335 | #end | ||
336 | #extendQueryString($url {'sort': [$entry.key], 'sortOrder': [$targetSortOrder]}) | ||
337 | * [[{{translation key="solr.sortBy.$entry.key"/}}$!sortOrderIndicator>>path:${url}||class="$class"]] | ||
338 | #end | ||
339 | #end | ||
340 | |||
341 | #macro (extendQueryString $url $extraParameters) | ||
342 | #set ($parameters = {}) | ||
343 | #set ($discard = $parameters.putAll($request.getParameterMap())) | ||
344 | #set ($discard = $parameters.putAll($extraParameters)) | ||
345 | #set ($queryString = $escapetool.url($parameters)) | ||
346 | #set ($url = $NULL) | ||
347 | #setVariable("$url" $doc.getURL('view', $queryString)) | ||
348 | #end | ||
349 | |||
350 | #macro (displaySearchResults) | ||
351 | #set ($results = $searchResponse.results) | ||
352 | #set ($paginationParameters = { | ||
353 | 'url': $doc.getURL('view', "$!request.queryString.replaceAll('firstIndex=[0-9]*', '')"), | ||
354 | 'totalItems': $results.numFound, | ||
355 | 'defaultItemsPerPage': $rows, | ||
356 | 'position': 'top' | ||
357 | }) | ||
358 | {{html clean="false"}}#pagination($paginationParameters){{/html}} | ||
359 | (% class="search-results" %)((( | ||
360 | #foreach ($searchResult in $results) | ||
361 | #displaySearchResult($searchResult) | ||
362 | #end | ||
363 | ))) | ||
364 | #set ($discard = $paginationParameters.put('position', 'bottom')) | ||
365 | {{html clean="false"}}#pagination($paginationParameters){{/html}} | ||
366 | |||
367 | #displayRSSLink() | ||
368 | #end | ||
369 | |||
370 | #macro (displayRSSLink) | ||
371 | {{html clean="false"}} | ||
372 | #set ($parameters = {}) | ||
373 | ## We keep most of the current request parameters so that the RSS feed matches the current search query and filters. | ||
374 | #set ($discard = $parameters.putAll($request.getParameterMap())) | ||
375 | ## The feed will provide the most recent results that match the search query and filters. | ||
376 | #set ($discard = $parameters.put('sort', 'date')) | ||
377 | #set ($discard = $parameters.put('sortOrder', 'desc')) | ||
378 | ## Reset the pagination so that only the top results are included. | ||
379 | #set ($discard = $parameters.remove('firstIndex')) | ||
380 | ## Add the parameters required to output the RSS feed instead of the search UI. | ||
381 | #set ($discard = $parameters.put('outputSyntax', 'plain')) | ||
382 | #set ($discard = $parameters.put('media', 'rss')) | ||
383 | <a href="$doc.getURL('get', $escapetool.url($parameters))" class="hasIcon iconRSS"> | ||
384 | $services.localization.render('search.rss', ["[$escapetool.xml($text)]"]) | ||
385 | </a> | ||
386 | {{/html}} | ||
387 | #end | ||
388 | |||
389 | #macro (displaySearchResult $searchResult) | ||
390 | #set ($searchResultReference = $services.solr.resolve($searchResult)) | ||
391 | (% class="search-result type-$searchResult.type.toLowerCase()" %)((( | ||
392 | ## We use the HTML macro here mainly because we don't have a way to escape the wiki syntax in the data provided by the user. | ||
393 | {{html clean="false"}} | ||
394 | #evaluate("${escapetool.h}displaySearchResult_$searchResult.type.toLowerCase()(${escapetool.d}searchResult)") | ||
395 | #displaySearchResultHighlighting($searchResult) | ||
396 | {{/html}} | ||
397 | #if ($debug) | ||
398 | |||
399 | ## Scoring debug data. | ||
400 | ## The reason we used a separate HTML block with no cleaning is because the scoring debug data may contain some | ||
401 | ## characters that are considered invalid by JDOM library which is used for parsing the HTML when cleaning is on. | ||
402 | ## E.g. "0x0 is not a legal XML character" (org.jdom.IllegalDataException). | ||
403 | {{html clean="false"}} | ||
404 | <div class="search-result-debug">$!escapetool.xml($searchResponse.explainMap.get($searchResult.id))</div> | ||
405 | {{/html}} | ||
406 | #end | ||
407 | ))) | ||
408 | #end | ||
409 | |||
410 | #macro (displaySearchResult_document $searchResult) | ||
411 | #displaySearchResultTitle() | ||
412 | #displaySearchResultLocation() | ||
413 | <div class="search-result-author"> | ||
414 | $services.localization.render('core.footer.modification', [ | ||
415 | "#displayUserProfileLink($searchResult.author $searchResult.author_display)", | ||
416 | $xwiki.formatDate($searchResult.date) | ||
417 | ]) | ||
418 | </div> | ||
419 | #end | ||
420 | |||
421 | #macro (displaySearchResult_attachment $searchResult) | ||
422 | <h2 class="search-result-title"> | ||
423 | $services.icon.renderHTML('attach') | ||
424 | #set ($attachmentURL = $xwiki.getURL($searchResultReference)) | ||
425 | #set ($downloadHint = $services.localization.render('core.viewers.attachments.download')) | ||
426 | <a href="$attachmentURL" title="$escapetool.xml($downloadHint)"> | ||
427 | $escapetool.xml($searchResultReference.name) | ||
428 | </a> | ||
429 | #set ($attachmentHistoryURL = $xwiki.getURL($searchResultReference, 'viewattachrev', $NULL)) | ||
430 | #set ($historyHint = $services.localization.render('core.viewers.attachments.showHistory')) | ||
431 | <a href="$attachmentHistoryURL" title="$escapetool.xml($historyHint)" class="search-result-version"> | ||
432 | $escapetool.xml($searchResult.attversion) | ||
433 | </a> | ||
434 | </h2> | ||
435 | #displaySearchResultLocation($searchResult) | ||
436 | <div class="search-result-uploader"> | ||
437 | #set ($uploader = "#displayUserProfileLink($searchResult.attauthor.get(0) $searchResult.attauthor_display.get(0))") | ||
438 | #set ($uploadDate = $xwiki.formatDate($searchResult.attdate.get(0))) | ||
439 | #set ($fileSize = "#dynamicsize($searchResult.attsize.get(0))") | ||
440 | $services.localization.render('solr.result.uploadedBy', [$uploader, $uploadDate, $fileSize]) | ||
441 | </div> | ||
442 | <div class="search-result-mediaType">$services.localization.render('solr.result.mediaType', | ||
443 | [$escapetool.xml($searchResult.mimetype.get(0))])</div> | ||
444 | #end | ||
445 | |||
446 | #macro (displaySearchResult_object $searchResult) | ||
447 | <h2 class="search-result-title"> | ||
448 | $services.icon.renderHTML('cubes') | ||
449 | $escapetool.xml("${searchResult.get('class').get(0)}[$searchResult.number]") | ||
450 | </h2> | ||
451 | #displaySearchResultLocation($searchResult) | ||
452 | #end | ||
453 | |||
454 | #macro (displaySearchResult_object_property $searchResult) | ||
455 | <h2 class="search-result-title"> | ||
456 | $services.icon.renderHTML('cube') $escapetool.xml($searchResult.propertyname) | ||
457 | </h2> | ||
458 | #displaySearchResultLocation($searchResult) | ||
459 | #end | ||
460 | |||
461 | #macro (displaySearchResultTitle) | ||
462 | #set ($showLocale = $searchResult.locale != '' && $searchResult.locale != "$xcontext.locale") | ||
463 | #set ($titleURL = $xwiki.getURL($searchResultReference)) | ||
464 | #if ($showLocale) | ||
465 | #set ($titleURL = $xwiki.getURL($searchResultReference, 'view', "language=$searchResult.locale")) | ||
466 | #end | ||
467 | <h2 class="search-result-title"> | ||
468 | $services.icon.renderHTML('file-white') | ||
469 | <a href="$titleURL">$escapetool.xml($searchResult.title_)</a> | ||
470 | #if ($showLocale) | ||
471 | <span title="$escapetool.xml($services.localization.render('solr.result.language'))" | ||
472 | class="search-result-language" >($escapetool.xml($searchResult.locale))</span> | ||
473 | #end | ||
474 | </h2> | ||
475 | #end | ||
476 | |||
477 | #macro (displaySearchResultLocation $searchResult) | ||
478 | <div class="search-result-location"> | ||
479 | $services.localization.render('solr.result.locatedIn') | ||
480 | #set ($locationOptions = { | ||
481 | 'excludeSelf': true, | ||
482 | 'limit': 6 | ||
483 | }) | ||
484 | #hierarchy($searchResultReference $locationOptions) | ||
485 | </div> | ||
486 | #end | ||
487 | |||
488 | #macro (displayUserProfileLink $userReference $userName) | ||
489 | #if ($userReference) | ||
490 | ## We could test if the specified user exists but we want to speed up the search. | ||
491 | <a href="$xwiki.getURL($userReference)">$escapetool.xml($userName)</a>## | ||
492 | #else | ||
493 | $services.localization.render('core.users.unknownUser')## | ||
494 | #end | ||
495 | #end | ||
496 | |||
497 | #macro (displaySearchResultHighlighting $searchResult) | ||
498 | #getSearchResultHighlighting($searchResult $highlighting) | ||
499 | #if ($highlighting.size() > 0) | ||
500 | <dl class="search-result-highlights"> | ||
501 | #foreach ($entry in $highlighting) | ||
502 | <dt> | ||
503 | #if ($services.localization.get("solr.field.$entry.field")) | ||
504 | $services.localization.render("solr.field.$entry.field") | ||
505 | #elseif ($entry.field.startsWith('property.')) | ||
506 | #getXClassProperty($entry.field $property $classPropertyReference) | ||
507 | #set ($propertyPrettyName = $property.translatedPrettyName) | ||
508 | #if ("$!propertyPrettyName" == '') | ||
509 | #set ($propertyPrettyName = $classPropertyReference.name) | ||
510 | #end | ||
511 | $propertyPrettyName | ||
512 | #else | ||
513 | $entry.field | ||
514 | #end | ||
515 | </dt> | ||
516 | <dd>#displaySearchResultMatches($entry.matches)</dd> | ||
517 | #end | ||
518 | </dl> | ||
519 | #if ($highlighting.size() > 1) | ||
520 | ## We wrap the link in a DIV because otherwise the HTML cleaning generates a paragraph. | ||
521 | <div> | ||
522 | <a href="#" class="search-result-highlightAll hidden"> | ||
523 | $escapetool.xml($services.localization.render('solr.result.highlightAll')) | ||
524 | </a> | ||
525 | </div> | ||
526 | #end | ||
527 | #end | ||
528 | #end | ||
529 | |||
530 | #macro (displaySearchResultMatches $matches) | ||
531 | #foreach ($match in $matches) | ||
532 | #if ($foreach.count > 1) | ||
533 | <span class="separator">…</span> | ||
534 | #end | ||
535 | <blockquote class="search-result-highlight">$match</blockquote> | ||
536 | #end | ||
537 | #end | ||
538 | |||
539 | #macro (getSearchResultHighlighting $searchResult $return) | ||
540 | #set ($highlighting = $searchResponse.highlighting.get($searchResult.id)) | ||
541 | #set ($highlightingByLanguage = {}) | ||
542 | #foreach ($entry in $highlighting.entrySet()) | ||
543 | ## Remove the language suffix (e.g. __, _en, _fr, _de) from the field name. | ||
544 | #set ($field = $stringtool.removeEnd($entry.key, '__')) | ||
545 | #set ($language = $stringtool.substringAfterLast($field, '_')) | ||
546 | #if ($services.localization.toLocale($language)) | ||
547 | #set ($field = $stringtool.substringBeforeLast($field, '_')) | ||
548 | #else | ||
549 | #set ($language = '') | ||
550 | #end | ||
551 | #set ($matchesByLanguage = $highlightingByLanguage.get($field)) | ||
552 | #if (!$matchesByLanguage) | ||
553 | #set ($matchesByLanguage = {}) | ||
554 | #set ($discard = $highlightingByLanguage.put($field, $matchesByLanguage)) | ||
555 | #end | ||
556 | #set ($discard = $matchesByLanguage.put($language, $entry.value)) | ||
557 | #end | ||
558 | ## Keep only the matches correspoding to the search result locale. | ||
559 | #set ($highlighting = []) | ||
560 | ## Fields with a higher index will be displayed first. Fields that are not included will be displayed at the end. | ||
561 | #set ($fieldPriority = ['filename', 'attcontent', 'objcontent', 'comment', 'propertyname', 'propertyvalue', 'title', 'doccontent']) | ||
562 | #foreach ($entry in $highlightingByLanguage.entrySet()) | ||
563 | #set ($matches = $entry.value.get($searchResult.locale)) | ||
564 | #if (!$matches) | ||
565 | ## This should not happen but let's play safe. | ||
566 | #set ($matches = $entry.value.entrySet().iterator().next().value) | ||
567 | #end | ||
568 | ## Sanitize the matches. | ||
569 | #foreach ($match in $matches) | ||
570 | #set ($match = $match.replace('<span class="search-text-highlight">', "\u0011")) | ||
571 | #set ($match = $match.replace('<span class="search-text-highlight-stop"></span></span>', "\u0013")) | ||
572 | #set ($match = $escapetool.xml($match)) | ||
573 | #set ($match = $match.replace("\u0011", '<span class="search-text-highlight">')) | ||
574 | #set ($match = $match.replace("\u0013", '</span>')) | ||
575 | #set ($discard = $matches.set($mathtool.sub($foreach.count, 1), $match)) | ||
576 | #end | ||
577 | #set ($discard = $highlighting.add({ | ||
578 | 'field': $entry.key, | ||
579 | 'priority': $fieldPriority.indexOf($entry.key), | ||
580 | 'matches': $matches | ||
581 | })) | ||
582 | #end | ||
583 | #set ($highlighting = $collectiontool.sort($highlighting, 'priority:desc')) | ||
584 | #set ($return = $NULL) | ||
585 | #setVariable("$return" $highlighting) | ||
586 | #end | ||
587 | |||
588 | #macro (getSearchResults) | ||
589 | #set ($queryString = "$!{text}") | ||
590 | ## | ||
591 | ## Create the query and set the query string. | ||
592 | #set ($query = $services.query.createQuery($queryString, 'solr')) | ||
593 | ## | ||
594 | ## Set query parameters. | ||
595 | #set ($discard = $query.setLimit($rows)) | ||
596 | #set ($discard = $query.setOffset($start)) | ||
597 | #set ($discard = $query.bindValue('sort', "${sort} ${sortOrder}")) | ||
598 | #set ($discard = $query.bindValue('tie', $solrConfig.tieBreaker)) | ||
599 | #set ($discard = $query.bindValue('mm', $solrConfig.minShouldMatch)) | ||
600 | #setQueryFields($query) | ||
601 | #setPhraseFields($query) | ||
602 | #setFacetFields($query) | ||
603 | #setFilterQuery($query) | ||
604 | #setHighlightQuery($query) | ||
605 | #if ($debug) | ||
606 | #set ($discard = $query.bindValue('debugQuery', 'on')) | ||
607 | #end | ||
608 | ## | ||
609 | ## Execute the query. | ||
610 | #set ($searchResponse = $query.execute()[0]) | ||
611 | #end | ||
612 | |||
613 | #macro (setQueryFields $query) | ||
614 | ## Specify which index fields are matched when a free text search is performed. | ||
615 | #if ($boost == '') | ||
616 | #if ($solrConfig.queryFields.substring(0, 0) == '') | ||
617 | ## If the value of the 'queryFields' parameter is a string then it means that the same query fields are used for | ||
618 | ## all result types. | ||
619 | #set ($boost = $solrConfig.queryFields) | ||
620 | #else | ||
621 | ## There are different query fields for each result type. | ||
622 | #set ($boost = $solrConfig.queryFields.get($type)) | ||
623 | #end | ||
624 | #end | ||
625 | #if ("$!boost" != '') | ||
626 | #set ($discard = $query.bindValue('qf', $boost)) | ||
627 | #end | ||
628 | #end | ||
629 | |||
630 | #macro (setPhraseFields $query) | ||
631 | ## Set the main phrase field parameter boosts so that queries with all search terms | ||
632 | ## in close proximity have high relevance | ||
633 | #if ($solrConfig.phraseFields.substring(0, 0) == '') | ||
634 | ## If the value of the 'phraseFields' parameter is a string then it means that the | ||
635 | ## same query fields are used for all result types. | ||
636 | #set ($phraseFieldsBoost = $solrConfig.phraseFields) | ||
637 | #else | ||
638 | ## There are different phrase fields for each result type. | ||
639 | ## Including type = null, which will result from all facets being deselected | ||
640 | #set ($phraseFieldsBoost = $solrConfig.phraseFields.get("$!type")) | ||
641 | #end | ||
642 | #if ("$!phraseFieldsBoost" != '') | ||
643 | #set ($discard = $query.bindValue('pf', $phraseFieldsBoost)) | ||
644 | #set ($discard = $query.bindValue('ps', $solrConfig.phraseFieldSlop)) | ||
645 | #end | ||
646 | ## Set the bigram phrase field parameter boosts so that queries with groups of two | ||
647 | ## search terms in close proximity have high relevance | ||
648 | #if ($solrConfig.bigramPhraseFields.substring(0, 0) == '') | ||
649 | ## If the value of the 'bigramPhraseFields' parameter is a string then it means that the | ||
650 | ## same query fields are used for all result types. | ||
651 | #set ($bigramPhraseFieldsBoost = $solrConfig.bigramPhraseFields) | ||
652 | #else | ||
653 | ## There are different phrase fields for each result type. | ||
654 | ## Including type = null, which will result from all facets being deselected | ||
655 | #set ($bigramPhraseFieldsBoost = $solrConfig.bigramPhraseFields.get("$!type")) | ||
656 | #end | ||
657 | #if ("$!bigramPhraseFieldsBoost" != '') | ||
658 | #set ($discard = $query.bindValue('pf2', $bigramPhraseFieldsBoost)) | ||
659 | #set ($discard = $query.bindValue('ps2', $solrConfig.bigramPhraseFieldSlop)) | ||
660 | #end | ||
661 | ## Set the trigram phrase field parameter boosts so that queries with groups of three | ||
662 | ## search terms in close proximity have high relevance. | ||
663 | ## Generally (pf boost) > (pf3 boost) > (pf2 boost) | ||
664 | #if ($solrConfig.trigramPhraseFields.substring(0, 0) == '') | ||
665 | ## If the value of the 'trigramPhraseFields' parameter is a string then it means that the | ||
666 | ## same query fields are used for all result types. | ||
667 | #set ($trigramPhraseFieldsBoost = $solrConfig.trigramPhraseFields) | ||
668 | #else | ||
669 | ## There are different phrase fields for each result type. | ||
670 | ## including type = null, which will result from all facets being deselected | ||
671 | #set ($trigramPhraseFieldsBoost = $solrConfig.trigramPhraseFields.get("$!type")) | ||
672 | #end | ||
673 | #if ("$!trigramPhraseFieldsBoost" != '') | ||
674 | #set ($discard = $query.bindValue('pf3', $trigramPhraseFieldsBoost)) | ||
675 | #set ($discard = $query.bindValue('ps3', $solrConfig.trigramPhraseFieldSlop)) | ||
676 | #end | ||
677 | #end | ||
678 | |||
679 | #macro (setFacetFields $query) | ||
680 | #set ($discard = $query.bindValue('facet', $facetEnabled)) | ||
681 | #if ($facetEnabled) | ||
682 | ## The facets are displayed in this order so keep the most important facets first. | ||
683 | #set ($facetFields = $solrConfig.facetFields) | ||
684 | ## In order to support multi-select faceting we need to exclude the corresponding filters when faceting. | ||
685 | ## See http://wiki.apache.org/solr/SimpleFacetParameters#Multi-Select_Faceting_and_LocalParams | ||
686 | #set ($facetFieldsWithFilterExcludes = []) | ||
687 | ## The type facet doesn't support multiple selection because we use different query fields for different result | ||
688 | ## types so the number of matches for the type facet changes when a result type is selected/unselected. | ||
689 | ## We don't allow multiple selection on the space facet because we perform a 'facet.prefix'-based drill down. | ||
690 | #set ($singleSelectionFacets = ['type', 'space_facet']) | ||
691 | #foreach ($facet in $facetFields) | ||
692 | #set ($excludeTaggedFilter = '') | ||
693 | #if (!$singleSelectionFacets.contains($facet)) | ||
694 | #set ($excludeTaggedFilter = "{!ex=$facet}") | ||
695 | #end | ||
696 | #set ($discard = $facetFieldsWithFilterExcludes.add("$excludeTaggedFilter$facet")) | ||
697 | #end | ||
698 | #set ($discard = $query.bindValue('facet.field', $facetFieldsWithFilterExcludes)) | ||
699 | #end | ||
700 | #end | ||
701 | |||
702 | #macro (setFilterQuery $query) | ||
703 | ## | ||
704 | ## Collect the query filters. | ||
705 | #set ($filters = {}) | ||
706 | ## Add the default filters if not specified in the configuration. | ||
707 | #if (!$solrConfig.filterQuery || $solrConfig.filterQuery.isEmpty()) | ||
708 | ## Uncomment the following line of code if you want to search by default also in: | ||
709 | ## * the default translation of documents that are not translated in the current locale | ||
710 | ## * the "xx" translation if the current locale "xx_YY" doesn't have a translation available | ||
711 | ## (e.g. "pt" when "pt_BR" is not available) | ||
712 | ## See the discussion on XWIKI-9977. | ||
713 | ##set ($discard = $filters.put('locales', ["$xcontext.locale"])) | ||
714 | #if (!$xcontext.isMainWiki()) | ||
715 | ## Subwikis search by default in their content only. | ||
716 | #set ($discard = $filters.put('wiki', [$xcontext.database])) | ||
717 | #elseif ($solrConfig.wikisSearchableFromMainWiki) | ||
718 | ## The list of wikis that are searched by default can be configured. | ||
719 | #set ($discard = $filters.put('wiki', $solrConfig.wikisSearchableFromMainWiki)) | ||
720 | #end | ||
721 | #if ($xwiki.getUserPreference('displayHiddenDocuments') != 1) | ||
722 | #set ($discard = $filters.put('hidden', [false])) | ||
723 | #end | ||
724 | #end | ||
725 | ## Add the facets. | ||
726 | #set ($prefixFacets = ['space_facet']) | ||
727 | #foreach ($parameter in $request.parameterMap.entrySet()) | ||
728 | #if ($parameter.key.startsWith('f_')) | ||
729 | #set ($fieldName = $parameter.key.substring(2)) | ||
730 | #set ($escapedValues = []) | ||
731 | #foreach ($value in $parameter.value) | ||
732 | #set ($discard = $escapedValues.add("#escapeFilterValue($value)")) | ||
733 | #end | ||
734 | #set ($discard = $filters.put($fieldName, $escapedValues)) | ||
735 | #if ($prefixFacets.contains($fieldName)) | ||
736 | #set ($parts = $parameter.value.get(0).split('/', 2)) | ||
737 | #set ($length = $numbertool.toNumber($parts.get(0)).intValue() + 1) | ||
738 | #set ($prefix = "$length/$parts.get(1)") | ||
739 | #set ($discard = $query.bindValue("f.${fieldName}.facet.prefix", $prefix)) | ||
740 | #set ($discard = $prefixFacets.remove($fieldName)) | ||
741 | #end | ||
742 | #end | ||
743 | #end | ||
744 | ## Specify the initial prefix for the remaining prefix facets. | ||
745 | #foreach ($facet in $prefixFacets) | ||
746 | #set ($discard = $query.bindValue("f.${facet}.facet.prefix", '0/')) | ||
747 | #end | ||
748 | ## | ||
749 | ## Build the filter query. | ||
750 | #set ($filterQuery = []) | ||
751 | #if ($solrConfig.filterQuery) | ||
752 | #set ($discard = $filterQuery.addAll($solrConfig.filterQuery)) | ||
753 | #end | ||
754 | #foreach ($filter in $filters.entrySet()) | ||
755 | ## Use OR between different values of the same filter/facet. | ||
756 | ## Tag the filter so that we can exclude it when faceting in order to support multi-select faceting. | ||
757 | #set ($discard = $filterQuery.add("{!tag=$filter.key}$filter.key:($!stringtool.join($filter.value, ' OR '))")) | ||
758 | #end | ||
759 | #set ($discard = $query.bindValue('fq', $filterQuery)) | ||
760 | #end | ||
761 | |||
762 | #macro(setHighlightQuery $query) | ||
763 | #set ($discard = $query.bindValue('hl', $highlightEnabled)) | ||
764 | #end | ||
765 | |||
766 | #macro (escapeFilterValue $value) | ||
767 | ## Check if the given value is a range. | ||
768 | #if ($rangePattern.matcher($value).matches() || $wildcardPattern.matcher($value).matches())## | ||
769 | $value## | ||
770 | #else## | ||
771 | "$stringtool.replaceEach($value, ['\', '"'], ['\\', '\"'])"## | ||
772 | #end## | ||
773 | #end | ||
774 | |||
775 | #macro (processRequestParameters) | ||
776 | #set ($text = "$!request.text") | ||
777 | #set ($boost = "$!request.boost") | ||
778 | #set ($debug = "$!request.debug" != '') | ||
779 | ## | ||
780 | ## Highlight enabled | ||
781 | ## First check the request, then the configuration and enable it by default | ||
782 | #if ($request.highlight) | ||
783 | #set ($highlightEnabled = $request.highlight != 'false') | ||
784 | #elseif ($solrConfig.containsKey('highlightEnabled')) | ||
785 | #set ($highlightEnabled = $solrConfig.highlightEnabled) | ||
786 | #else | ||
787 | #set ($highlightEnabled = true) | ||
788 | #end | ||
789 | ## | ||
790 | ## Facet enabled | ||
791 | ## First check the request, then the configuration and enable it by default | ||
792 | #if ($request.facet) | ||
793 | #set ($facetEnabled = $request.facet != 'false') | ||
794 | #elseif ($solrConfig.containsKey('facetEnabled')) | ||
795 | #set ($facetEnabled = $solrConfig.facetEnabled) | ||
796 | #else | ||
797 | #set ($facetEnabled = true) | ||
798 | #end | ||
799 | ## | ||
800 | ## Pagination | ||
801 | #set ($rows = $numbertool.toNumber($request.rows).intValue()) | ||
802 | #if ("$!rows" == '') | ||
803 | #set ($rows = 10) | ||
804 | #end | ||
805 | #set ($start = $numbertool.toNumber($request.firstIndex).intValue()) | ||
806 | #if ("$!start" == '') | ||
807 | #set ($start = 0) | ||
808 | #end | ||
809 | ## | ||
810 | ## Sort | ||
811 | #set ($sort = $request.sort) | ||
812 | #if ("$!sort" == '') | ||
813 | #set ($sort = 'score') | ||
814 | #end | ||
815 | #set ($sortOrder = $request.sortOrder) | ||
816 | #if ("$!sortOrder" == '') | ||
817 | #set ($sortOrder = 'desc') | ||
818 | #elseif ($sortOrder != 'desc') | ||
819 | #set ($sortOrder = 'asc') | ||
820 | #end | ||
821 | ## | ||
822 | ## Result type | ||
823 | ## We store the selected result type because we need it to decide what search and sort fields to use. | ||
824 | #set ($type = $request.getParameterValues('f_type')) | ||
825 | #if ($type && $type.size() == 1) | ||
826 | #set ($type = $type.get(0)) | ||
827 | #else | ||
828 | ## Extract the result type from the filter query, if specified. | ||
829 | #foreach ($item in $solrConfig.filterQuery) | ||
830 | #if ($item.startsWith('type:')) | ||
831 | #set ($type = $item.substring(5)) | ||
832 | #break | ||
833 | #end | ||
834 | #end | ||
835 | #end | ||
836 | #end | ||
837 | |||
838 | #macro (displaySearchUI) | ||
839 | #set($void = $services.progress.startStep('#displaySearchUI')) | ||
840 | #set($void = $services.progress.pushLevel()) | ||
841 | #set ($discard = $xwiki.ssx.use('Main.SolrSearch')) | ||
842 | #set ($discard = $xwiki.jsx.use('Main.SolrSearch')) | ||
843 | ## Disable the document extra data: comments, attachments, history... | ||
844 | #set ($displayDocExtra = false) | ||
845 | #processRequestParameters() | ||
846 | (% class="search-ui" %)((( | ||
847 | #if ($xcontext.action == 'get') | ||
848 | {{html clean="false"}} | ||
849 | ## The search UI is updated dynamically through AJAX and we need to pull the skin extensions. | ||
850 | ## We put the skin extension imports inside a <noscript> element to prevent jQuery from fetching the JavaScript | ||
851 | ## files automatically (we want to fetch only the new JavaScript files). | ||
852 | <noscript class="hidden skin-extension-imports">#skinExtensionHooks</noscript> | ||
853 | {{/html}} | ||
854 | |||
855 | #end | ||
856 | #displaySearchForm() | ||
857 | #if ($text != '') | ||
858 | #getSearchResults() | ||
859 | #if ($debug) | ||
860 | #displaySearchDebugInfo() | ||
861 | #end | ||
862 | (% class="search-results-container row" %)((( | ||
863 | #if ($facetEnabled) | ||
864 | (% class="col-xs-12 col-sm-4 col-sm-push-8 col-md-3 col-md-push-9" %)((( | ||
865 | #displaySearchFacets($searchResponse) | ||
866 | ))) | ||
867 | #end | ||
868 | (% class="search-results-left col-xs-12#if ($facetEnabled) col-sm-8 col-sm-pull-4 col-md-9 col-md-pull-3#end" %) | ||
869 | ((( | ||
870 | #displaySearchResultsSort() | ||
871 | |||
872 | #displaySearchResults() | ||
873 | ))) | ||
874 | ))) | ||
875 | #end | ||
876 | ))) | ||
877 | #set($void = $services.progress.popLevel()) | ||
878 | #set($void = $services.progress.endStep()) | ||
879 | #end | ||
880 | |||
881 | #macro (outputRSSFeed) | ||
882 | ## | ||
883 | ## Get the search results. | ||
884 | ## | ||
885 | #processRequestParameters() | ||
886 | #getSearchResults() | ||
887 | #set ($list = []) | ||
888 | #set ($results = $searchResponse.results) | ||
889 | #foreach ($searchResult in $results) | ||
890 | #set ($searchResultDocumentReference = $services.solr.resolveDocument($searchResult)) | ||
891 | #set ($discard = $list.add("$searchResultDocumentReference")) | ||
892 | #end | ||
893 | ## | ||
894 | ## Compute the feed URI. | ||
895 | ## | ||
896 | #set ($parameters = {}) | ||
897 | #set ($discard = $parameters.putAll($request.getParameterMap())) | ||
898 | #set ($discard = $parameters.remove('outputSyntax')) | ||
899 | #set ($discard = $parameters.remove('media')) | ||
900 | #set ($feedURI = $doc.getExternalURL('view', $escapetool.url($parameters))) | ||
901 | ## | ||
902 | ## Configure the feed. | ||
903 | ## | ||
904 | #set ($feed = $xwiki.feed.getDocumentFeed($list, {})) | ||
905 | #set ($discard = $feed.setLink($feedURI)) | ||
906 | #set ($discard = $feed.setUri($feedURI)) | ||
907 | #set ($discard = $feed.setAuthor('XWiki')) | ||
908 | #set ($title = $services.localization.render('search.rss', ["[$text]"])) | ||
909 | #set ($discard = $feed.setTitle($title)) | ||
910 | #set ($discard = $feed.setDescription($title)) | ||
911 | #set ($discard = $feed.setLanguage("$xcontext.locale")) | ||
912 | #set ($discard = $feed.setCopyright($xwiki.getXWikiPreference('copyright'))) | ||
913 | ## | ||
914 | ## Output the feed. | ||
915 | ## | ||
916 | #set ($discard = $response.setContentType('application/rss+xml')) | ||
917 | $xwiki.feed.getFeedOutput($feed, 'rss_2.0') | ||
918 | #end | ||
919 | |||
920 | #macro (handleSolrSearchRequest) | ||
921 | ## Preselect facet values only for the facets that are enabled. | ||
922 | #set ($discard = $solrConfig.facetQuery.keySet().retainAll($solrConfig.facetFields)) | ||
923 | #if ($request.media == 'rss') | ||
924 | #outputRSSFeed() | ||
925 | #elseif ("$!request.r" == '1' || $solrConfig.facetQuery.isEmpty()) | ||
926 | #displaySearchUI() | ||
927 | #else | ||
928 | ## Redirect using preselected facet values. | ||
929 | #set ($extraParams = {}) | ||
930 | #foreach ($entry in $solrConfig.facetQuery.entrySet()) | ||
931 | #set ($discard = $extraParams.put("f_$entry.key", $entry.value)) | ||
932 | #end | ||
933 | ## Prevent redirect loop. | ||
934 | #set ($extraParams.r = 1) | ||
935 | #extendQueryString($url $extraParams) | ||
936 | $response.sendRedirect($url) | ||
937 | #end | ||
938 | #end | ||
939 | {{/velocity}} |