Wiki-Quellcode von Solr Search Macros

Version 2.1 von Daniel Herrmann am 2025/09/20 09:53

Verstecke letzte Bearbeiter
admin 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">
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'
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
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>
192 #end
193
194 #macro (displaySearchFacetBody $facetField)
195 <div id="$escapetool.xml($facetField.name)-dropdown" class="search-facet-body">
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">&hellip; $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)
245 #displaySearchFacetValue($facetValue $customQueryStringParameters $customValueDisplayer false)
246 #end
247
248 #macro (displaySearchFacetValue $facetValue $customQueryStringParameters $customValueDisplayer $displayToggler)
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>
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
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">&hellip;</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
Daniel Herrmann 2.1 801 #getAndValidateQueryLimitFromRequest('rows', 10, $rows)
admin 1.1 802 #set ($start = $numbertool.toNumber($request.firstIndex).intValue())
803 #if ("$!start" == '')
804 #set ($start = 0)
805 #end
806 ##
807 ## Sort
808 #set ($sort = $request.sort)
809 #if ("$!sort" == '')
810 #set ($sort = 'score')
811 #end
812 #set ($sortOrder = $request.sortOrder)
813 #if ("$!sortOrder" == '')
814 #set ($sortOrder = 'desc')
815 #elseif ($sortOrder != 'desc')
816 #set ($sortOrder = 'asc')
817 #end
818 ##
819 ## Result type
820 ## We store the selected result type because we need it to decide what search and sort fields to use.
821 #set ($type = $request.getParameterValues('f_type'))
822 #if ($type && $type.size() == 1)
823 #set ($type = $type.get(0))
824 #else
825 ## Extract the result type from the filter query, if specified.
826 #foreach ($item in $solrConfig.filterQuery)
827 #if ($item.startsWith('type:'))
828 #set ($type = $item.substring(5))
829 #break
830 #end
831 #end
832 #end
833 #end
834
835 #macro (displaySearchUI)
836 #set($void = $services.progress.startStep('#displaySearchUI'))
837 #set($void = $services.progress.pushLevel())
838 #set ($discard = $xwiki.ssx.use('Main.SolrSearch'))
839 #set ($discard = $xwiki.jsx.use('Main.SolrSearch'))
840 ## Disable the document extra data: comments, attachments, history...
841 #set ($displayDocExtra = false)
842 #processRequestParameters()
843 (% class="search-ui" %)(((
844 #if ($xcontext.action == 'get')
845 {{html clean="false"}}
846 ## The search UI is updated dynamically through AJAX and we need to pull the skin extensions.
847 ## We put the skin extension imports inside a <noscript> element to prevent jQuery from fetching the JavaScript
848 ## files automatically (we want to fetch only the new JavaScript files).
849 <noscript class="hidden skin-extension-imports">#skinExtensionHooks</noscript>
850 {{/html}}
851
852 #end
853 #displaySearchForm()
854 #if ($text != '')
855 #getSearchResults()
856 #if ($debug)
857 #displaySearchDebugInfo()
858 #end
859 (% class="search-results-container row" %)(((
860 #if ($facetEnabled)
861 (% class="col-xs-12 col-sm-4 col-sm-push-8 col-md-3 col-md-push-9" %)(((
862 #displaySearchFacets($searchResponse)
863 )))
864 #end
865 (% class="search-results-left col-xs-12#if ($facetEnabled) col-sm-8 col-sm-pull-4 col-md-9 col-md-pull-3#end" %)
866 (((
867 #displaySearchResultsSort()
868
869 #displaySearchResults()
870 )))
871 )))
872 #end
873 )))
874 #set($void = $services.progress.popLevel())
875 #set($void = $services.progress.endStep())
876 #end
877
878 #macro (outputRSSFeed)
879 ##
880 ## Get the search results.
881 ##
882 #processRequestParameters()
883 #getSearchResults()
884 #set ($list = [])
885 #set ($results = $searchResponse.results)
886 #foreach ($searchResult in $results)
887 #set ($searchResultDocumentReference = $services.solr.resolveDocument($searchResult))
888 #set ($discard = $list.add("$searchResultDocumentReference"))
889 #end
890 ##
891 ## Compute the feed URI.
892 ##
893 #set ($parameters = {})
894 #set ($discard = $parameters.putAll($request.getParameterMap()))
895 #set ($discard = $parameters.remove('outputSyntax'))
896 #set ($discard = $parameters.remove('media'))
897 #set ($feedURI = $doc.getExternalURL('view', $escapetool.url($parameters)))
898 ##
899 ## Configure the feed.
900 ##
901 #set ($feed = $xwiki.feed.getDocumentFeed($list, {}))
902 #set ($discard = $feed.setLink($feedURI))
903 #set ($discard = $feed.setUri($feedURI))
904 #set ($discard = $feed.setAuthor('XWiki'))
905 #set ($title = $services.localization.render('search.rss', ["[$text]"]))
906 #set ($discard = $feed.setTitle($title))
907 #set ($discard = $feed.setDescription($title))
908 #set ($discard = $feed.setLanguage("$xcontext.locale"))
909 #set ($discard = $feed.setCopyright($xwiki.getXWikiPreference('copyright')))
910 ##
911 ## Output the feed.
912 ##
913 #rawResponse($xwiki.feed.getFeedOutput($feed, 'rss_2.0'), 'application/rss+xml')
914 #end
915
916 #macro (handleSolrSearchRequest)
917 ## Preselect facet values only for the facets that are enabled.
918 #set ($discard = $solrConfig.facetQuery.keySet().retainAll($solrConfig.facetFields))
919 #if ($request.media == 'rss')
920 #outputRSSFeed()
921 #elseif ("$!request.r" == '1' || $solrConfig.facetQuery.isEmpty())
922 #displaySearchUI()
923 #else
924 ## Redirect using preselected facet values.
925 #set ($extraParams = {})
926 #foreach ($entry in $solrConfig.facetQuery.entrySet())
927 #set ($discard = $extraParams.put("f_$entry.key", $entry.value))
928 #end
929 ## Prevent redirect loop.
930 #set ($extraParams.r = 1)
931 #extendQueryString($url $extraParams)
932 $response.sendRedirect($url)
933 #end
934 #end
935 {{/velocity}}

Community

https://wiki.makerspace-darmstadt.de/bin/download/Panels/MKSP%20Slack/Slack_MKSP.png

Wir benutzen Slack, um miteinander zu kommunizieren. Melde Dich an und werde Teil unserer Maker-Community!

Zum Slack Workspace

Frage? FAQ!

Du hast eine Frage, die sich nicht direkt im Wiki findet?

Natürlich kannst Du die Frage jederzeit gerne in der Slack Community stellen. Bevor Du das machst, schau doch bitte einmal auf unserer Homepage bei den Häufig gestellten Fragen vorbei. Wir versuchen diese Fragen stets aktuell zu halten, eventuell hilft Dir das ja schon weiter.

Offene Werkstatt

Ohne Anmeldung einfach vorbei kommen. Am besten bringst Du direkt den ausgefüllten Haftungsauschluss mit.

Jeden Donnerstag ab 19 Uhr

Während der offenen Werkstatt kannst Du einfach vorbei kommen und an Deinem Projekt arbeiten. Bitte beachte aber, dass zur Verwendung der Maschinen eine Einweisung erforderlich ist, die Du gegebenfalls vorher absolvieren musst. Wenn ein Mitglied mit entsprechender Einweisung vor Ort ist und Zeit hat, helfen wir natürlich gerne aus. Dies können wir aber nicht garantieren, da Rundgänge Priorität haben.

Sprich Dich idealerweise schon vor der offenen Werkstatt mit einem Mitglied in unserem Slack ab. So kannst Du sicherstellen, dass Du auf jeden Fall arbeiten kannst.

Übrigens: Du kannst Dich mit einem Mitglied gerne auch außerhalb der offenen Werkstattzeiten zum Arbeiten verabreden!

Führungen und Rundgänge

Im Rahmen der offenen Werkstatt bieten wir euch auch gerne einen Rundgang durch unsere Werkstatt. Hier könnt ihr den Verein und unser Konzept kennenlernen sowie die Maschinen und Möglichkeiten der Werkstatt gezeigt bekommen.

Jeden Donnerstag wird eine Führung angeboten:

  • Um 19:15 Uhr (bitte um 19:00 Uhr da sein)

Der Rundgang dauert ca. 45 Minuten und ihr habt natürlich auch die Möglichkeit, eure Fragen loszuwerden.

Bitte beachtet folgendes: Die Werkstatt beinhaltet gefährliche Maschinen. Bringt daher bitte nach Möglichkeit den ausgefüllten und unterschrieben Haftungsauschluss schon mit. Dieser kann aber auch vor Ort ausgefüllt werden, das verzögert allerdings die Abläufe.