|
1
|
|
|
<?php |
|
2
|
|
|
namespace Kitodo\Dlf\Plugin; |
|
3
|
|
|
|
|
4
|
|
|
/** |
|
5
|
|
|
* (c) Kitodo. Key to digital objects e.V. <[email protected]> |
|
6
|
|
|
* |
|
7
|
|
|
* This file is part of the Kitodo and TYPO3 projects. |
|
8
|
|
|
* |
|
9
|
|
|
* @license GNU General Public License version 3 or later. |
|
10
|
|
|
* For the full copyright and license information, please read the |
|
11
|
|
|
* LICENSE.txt file that was distributed with this source code. |
|
12
|
|
|
*/ |
|
13
|
|
|
|
|
14
|
|
|
use Kitodo\Dlf\Common\Document; |
|
15
|
|
|
use Kitodo\Dlf\Common\DocumentList; |
|
16
|
|
|
use Kitodo\Dlf\Common\Helper; |
|
17
|
|
|
use Kitodo\Dlf\Common\Indexer; |
|
18
|
|
|
use Kitodo\Dlf\Common\Solr; |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* Plugin 'Search' for the 'dlf' extension |
|
22
|
|
|
* |
|
23
|
|
|
* @author Sebastian Meyer <[email protected]> |
|
24
|
|
|
* @author Henrik Lochmann <[email protected]> |
|
25
|
|
|
* @author Frank Ulrich Weber <[email protected]> |
|
26
|
|
|
* @package TYPO3 |
|
27
|
|
|
* @subpackage dlf |
|
28
|
|
|
* @access public |
|
29
|
|
|
*/ |
|
30
|
|
|
class Search extends \Kitodo\Dlf\Common\AbstractPlugin { |
|
31
|
|
|
public $scriptRelPath = 'Classes/Plugin/Search.php'; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* Adds the JS files necessary for search suggestions |
|
35
|
|
|
* |
|
36
|
|
|
* @access protected |
|
37
|
|
|
* |
|
38
|
|
|
* @return void |
|
39
|
|
|
*/ |
|
40
|
|
|
protected function addAutocompleteJS() { |
|
41
|
|
|
// Check if there are any metadata to suggest. |
|
42
|
|
|
$result = $GLOBALS['TYPO3_DB']->exec_SELECTquery( |
|
43
|
|
|
'tx_dlf_metadata.*', |
|
44
|
|
|
'tx_dlf_metadata', |
|
45
|
|
|
'tx_dlf_metadata.index_autocomplete=1' |
|
46
|
|
|
.' AND tx_dlf_metadata.pid='.intval($this->conf['pages']) |
|
47
|
|
|
.Helper::whereClause('tx_dlf_metadata'), |
|
48
|
|
|
'', |
|
49
|
|
|
'', |
|
50
|
|
|
'1' |
|
51
|
|
|
); |
|
52
|
|
|
if ($GLOBALS['TYPO3_DB']->sql_num_rows($result)) { |
|
53
|
|
|
$GLOBALS['TSFE']->additionalHeaderData[$this->prefixId.'_search_suggest'] = '<script type="text/javascript" src="'.\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::siteRelPath($this->extKey).'Resources/Public/Javascript/Search/Suggester.js"></script>'; |
|
54
|
|
|
} else { |
|
55
|
|
|
Helper::devLog('No metadata fields configured for search suggestions', DEVLOG_SEVERITY_WARNING); |
|
56
|
|
|
} |
|
57
|
|
|
} |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* Adds the current collection's UID to the search form |
|
61
|
|
|
* |
|
62
|
|
|
* @access protected |
|
63
|
|
|
* |
|
64
|
|
|
* @return string HTML input fields with current document's UID and parent ID |
|
65
|
|
|
*/ |
|
66
|
|
|
protected function addCurrentCollection() { |
|
67
|
|
|
// Load current collection. |
|
68
|
|
|
$list = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(DocumentList::class); |
|
69
|
|
|
if (!empty($list->metadata['options']['source']) |
|
70
|
|
|
&& $list->metadata['options']['source'] == 'collection') { |
|
71
|
|
|
// Get collection's UID. |
|
72
|
|
|
return '<input type="hidden" name="'.$this->prefixId.'[collection]" value="'.$list->metadata['options']['select'].'" />'; |
|
73
|
|
View Code Duplication |
} elseif (!empty($list->metadata['options']['params']['filterquery'])) { |
|
|
|
|
|
|
74
|
|
|
// Get collection's UID from search metadata. |
|
75
|
|
|
foreach ($list->metadata['options']['params']['filterquery'] as $facet) { |
|
76
|
|
|
$facetKeyVal = explode(':', $facet['query'], 2); |
|
77
|
|
|
if ($facetKeyVal[0] == 'collection_faceting' |
|
78
|
|
|
&& !strpos($facetKeyVal[1], '" OR "')) { |
|
79
|
|
|
$collectionId = Helper::getUidFromIndexName(trim($facetKeyVal[1], '(")'), 'tx_dlf_collections'); |
|
80
|
|
|
} |
|
81
|
|
|
} |
|
82
|
|
|
return '<input type="hidden" name="'.$this->prefixId.'[collection]" value="'.$collectionId.'" />'; |
|
|
|
|
|
|
83
|
|
|
} |
|
84
|
|
|
return ''; |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* Adds the current document's UID or parent ID to the search form |
|
89
|
|
|
* |
|
90
|
|
|
* @access protected |
|
91
|
|
|
* |
|
92
|
|
|
* @return string HTML input fields with current document's UID and parent ID |
|
93
|
|
|
*/ |
|
94
|
|
|
protected function addCurrentDocument() { |
|
95
|
|
|
// Load current list object. |
|
96
|
|
|
$list = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(DocumentList::class); |
|
97
|
|
|
// Load current document. |
|
98
|
|
|
if (!empty($this->piVars['id']) |
|
99
|
|
|
&& \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->piVars['id'])) { |
|
100
|
|
|
$this->loadDocument(); |
|
101
|
|
|
// Get document's UID or parent ID. |
|
102
|
|
|
if ($this->doc->ready) { |
|
|
|
|
|
|
103
|
|
|
return '<input type="hidden" name="'.$this->prefixId.'[id]" value="'.($this->doc->parentId > 0 ? $this->doc->parentId : $this->doc->uid).'" />'; |
|
|
|
|
|
|
104
|
|
|
} |
|
105
|
|
View Code Duplication |
} elseif (!empty($list->metadata['options']['params']['filterquery'])) { |
|
|
|
|
|
|
106
|
|
|
// Get document's UID from search metadata. |
|
107
|
|
|
foreach ($list->metadata['options']['params']['filterquery'] as $facet) { |
|
108
|
|
|
$facetKeyVal = explode(':', $facet['query']); |
|
109
|
|
|
if ($facetKeyVal[0] == 'uid') { |
|
110
|
|
|
$documentId = (int) substr($facetKeyVal[1], 1, strpos($facetKeyVal[1], ')')); |
|
111
|
|
|
} |
|
112
|
|
|
} |
|
113
|
|
|
return '<input type="hidden" name="'.$this->prefixId.'[id]" value="'.$documentId.'" />'; |
|
|
|
|
|
|
114
|
|
|
} |
|
115
|
|
|
return ''; |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
/** |
|
119
|
|
|
* Adds the encrypted Solr core name to the search form |
|
120
|
|
|
* |
|
121
|
|
|
* @access protected |
|
122
|
|
|
* |
|
123
|
|
|
* @return string HTML input fields with encrypted core name and hash |
|
124
|
|
|
*/ |
|
125
|
|
|
protected function addEncryptedCoreName() { |
|
126
|
|
|
// Get core name. |
|
127
|
|
|
$name = Helper::getIndexNameFromUid($this->conf['solrcore'], 'tx_dlf_solrcores'); |
|
128
|
|
|
// Encrypt core name. |
|
129
|
|
|
if (!empty($name)) { |
|
130
|
|
|
$name = Helper::encrypt($name); |
|
131
|
|
|
} |
|
132
|
|
|
// Add encrypted fields to search form. |
|
133
|
|
|
if (is_array($name)) { |
|
134
|
|
|
return '<input type="hidden" name="'.$this->prefixId.'[encrypted]" value="'.$name['encrypted'].'" /><input type="hidden" name="'.$this->prefixId.'[hashed]" value="'.$name['hash'].'" />'; |
|
135
|
|
|
} else { |
|
136
|
|
|
return ''; |
|
137
|
|
|
} |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** |
|
141
|
|
|
* Returns the extended search form and adds the JS files necessary for extended search. |
|
142
|
|
|
* |
|
143
|
|
|
* @access protected |
|
144
|
|
|
* |
|
145
|
|
|
* @return string The extended search form or an empty string |
|
146
|
|
|
*/ |
|
147
|
|
|
protected function addExtendedSearch() { |
|
148
|
|
|
$extendedSearch = ''; |
|
149
|
|
|
// Quit without doing anything if no fields for extended search are selected. |
|
150
|
|
|
if (empty($this->conf['extendedSlotCount']) |
|
151
|
|
|
|| empty($this->conf['extendedFields'])) { |
|
152
|
|
|
return $extendedSearch; |
|
153
|
|
|
} |
|
154
|
|
|
// Get operator options. |
|
155
|
|
|
$operatorOptions = ''; |
|
156
|
|
|
foreach (['AND', 'OR', 'NOT'] as $operator) { |
|
157
|
|
|
$operatorOptions .= '<option class="tx-dlf-search-operator-option tx-dlf-search-operator-'.$operator.'" value="'.$operator.'">'.$this->pi_getLL($operator, '', TRUE).'</option>'; |
|
158
|
|
|
} |
|
159
|
|
|
// Get field selector options. |
|
160
|
|
|
$fieldSelectorOptions = ''; |
|
161
|
|
|
$searchFields = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->conf['extendedFields'], TRUE); |
|
162
|
|
|
foreach ($searchFields as $searchField) { |
|
163
|
|
|
$fieldSelectorOptions .= '<option class="tx-dlf-search-field-option tx-dlf-search-field-'.$searchField.'" value="'.$searchField.'">'.Helper::translate($searchField, 'tx_dlf_metadata', $this->conf['pages']).'</option>'; |
|
164
|
|
|
} |
|
165
|
|
|
for ($i = 0; $i < $this->conf['extendedSlotCount']; $i++) { |
|
166
|
|
|
$markerArray = [ |
|
167
|
|
|
'###EXT_SEARCH_OPERATOR###' => '<select class="tx-dlf-search-operator tx-dlf-search-operator-'.$i.'" name="'.$this->prefixId.'[extOperator]['.$i.']">'.$operatorOptions.'</select>', |
|
168
|
|
|
'###EXT_SEARCH_FIELDSELECTOR###' => '<select class="tx-dlf-search-field tx-dlf-search-field-'.$i.'" name="'.$this->prefixId.'[extField]['.$i.']">'.$fieldSelectorOptions.'</select>', |
|
169
|
|
|
'###EXT_SEARCH_FIELDQUERY###' => '<input class="tx-dlf-search-query tx-dlf-search-query-'.$i.'" type="text" name="'.$this->prefixId.'[extQuery]['.$i.']" />' |
|
170
|
|
|
]; |
|
171
|
|
|
$extendedSearch .= $this->cObj->substituteMarkerArray($this->cObj->getSubpart($this->template, '###EXT_SEARCH_ENTRY###'), $markerArray); |
|
|
|
|
|
|
172
|
|
|
} |
|
173
|
|
|
return $extendedSearch; |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
|
|
/** |
|
177
|
|
|
* Adds the facets menu to the search form |
|
178
|
|
|
* |
|
179
|
|
|
* @access protected |
|
180
|
|
|
* |
|
181
|
|
|
* @return string HTML output of facets menu |
|
182
|
|
|
*/ |
|
183
|
|
|
protected function addFacetsMenu() { |
|
184
|
|
|
// Check for typoscript configuration to prevent fatal error. |
|
185
|
|
|
if (empty($this->conf['facetsConf.'])) { |
|
186
|
|
|
Helper::devLog('Incomplete plugin configuration', DEVLOG_SEVERITY_WARNING); |
|
187
|
|
|
return ''; |
|
188
|
|
|
} |
|
189
|
|
|
// Quit without doing anything if no facets are selected. |
|
190
|
|
|
if (empty($this->conf['facets'])) { |
|
191
|
|
|
return ''; |
|
192
|
|
|
} |
|
193
|
|
|
// Get facets from plugin configuration. |
|
194
|
|
|
$facets = []; |
|
195
|
|
|
foreach (\TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->conf['facets'], TRUE) as $facet) { |
|
196
|
|
|
$facets[$facet.'_faceting'] = Helper::translate($facet, 'tx_dlf_metadata', $this->conf['pages']); |
|
197
|
|
|
} |
|
198
|
|
|
// Render facets menu. |
|
199
|
|
|
$TSconfig = []; |
|
200
|
|
|
$TSconfig['special'] = 'userfunction'; |
|
201
|
|
|
$TSconfig['special.']['userFunc'] = \Kitodo\Dlf\Plugin\Search::class.'->makeFacetsMenuArray'; |
|
202
|
|
|
$TSconfig['special.']['facets'] = $facets; |
|
203
|
|
|
$TSconfig['special.']['limit'] = max(intval($this->conf['limitFacets']), 1); |
|
204
|
|
|
$TSconfig = Helper::mergeRecursiveWithOverrule($this->conf['facetsConf.'], $TSconfig); |
|
205
|
|
|
return $this->cObj->cObjGetSingle('HMENU', $TSconfig); |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
/** |
|
209
|
|
|
* Adds the fulltext switch to the search form |
|
210
|
|
|
* |
|
211
|
|
|
* @access protected |
|
212
|
|
|
* |
|
213
|
|
|
* @param integer $isFulltextSearch: Is fulltext search activated? |
|
|
|
|
|
|
214
|
|
|
* |
|
215
|
|
|
* @return string HTML output of fulltext switch |
|
216
|
|
|
*/ |
|
217
|
|
|
protected function addFulltextSwitch($isFulltextSearch = 0) { |
|
218
|
|
|
$output = ''; |
|
219
|
|
|
// Check for plugin configuration. |
|
220
|
|
|
if (!empty($this->conf['fulltext'])) { |
|
221
|
|
|
$output .= ' <input class="tx-dlf-search-fulltext" id="tx-dlf-search-fulltext-no" type="radio" name="'.$this->prefixId.'[fulltext]" value="0" '.($isFulltextSearch == 0 ? 'checked="checked"' : '').' />'; |
|
222
|
|
|
$output .= ' <label for="tx-dlf-search-fulltext-no">'.$this->pi_getLL('label.inMetadata', '').'</label>'; |
|
223
|
|
|
$output .= ' <input class="tx-dlf-search-fulltext" id="tx-dlf-search-fulltext-yes" type="radio" name="'.$this->prefixId.'[fulltext]" value="1" '.($isFulltextSearch == 1 ? 'checked="checked"' : '').'/>'; |
|
224
|
|
|
$output .= ' <label for="tx-dlf-search-fulltext-yes">'.$this->pi_getLL('label.inFulltext', '').'</label>'; |
|
225
|
|
|
} |
|
226
|
|
|
return $output; |
|
227
|
|
|
} |
|
228
|
|
|
|
|
229
|
|
|
/** |
|
230
|
|
|
* Adds the logical page field to the search form |
|
231
|
|
|
* |
|
232
|
|
|
* @access protected |
|
233
|
|
|
* |
|
234
|
|
|
* @return string HTML output of logical page field |
|
235
|
|
|
*/ |
|
236
|
|
|
protected function addLogicalPage() { |
|
237
|
|
|
$output = ''; |
|
238
|
|
|
// Check for plugin configuration. |
|
239
|
|
|
if (!empty($this->conf['showLogicalPageField'])) { |
|
240
|
|
|
$output .= ' <label for="tx-dlf-search-logical-page">'.$this->pi_getLL('label.logicalPage', '').': </label>'; |
|
241
|
|
|
$output .= ' <input class="tx-dlf-search-logical-page" id="tx-dlf-search-logical-page" type="text" name="'.$this->prefixId.'[logicalPage]" />'; |
|
242
|
|
|
} |
|
243
|
|
|
return $output; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* Creates an array for a HMENU entry of a facet value. |
|
248
|
|
|
* |
|
249
|
|
|
* @access protected |
|
250
|
|
|
* |
|
251
|
|
|
* @param string $field: The facet's index_name |
|
|
|
|
|
|
252
|
|
|
* @param string $value: The facet's value |
|
|
|
|
|
|
253
|
|
|
* @param integer $count: Number of hits for this facet |
|
|
|
|
|
|
254
|
|
|
* @param array $search: The parameters of the current search query |
|
|
|
|
|
|
255
|
|
|
* @param string &$state: The state of the parent item |
|
256
|
|
|
* |
|
257
|
|
|
* @return array The array for the facet's menu entry |
|
258
|
|
|
*/ |
|
259
|
|
|
protected function getFacetsMenuEntry($field, $value, $count, $search, &$state) { |
|
260
|
|
|
$entryArray = []; |
|
261
|
|
|
// Translate value. |
|
262
|
|
|
if ($field == 'owner_faceting') { |
|
263
|
|
|
// Translate name of holding library. |
|
264
|
|
|
$entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_libraries', $this->conf['pages'])); |
|
265
|
|
|
} elseif ($field == 'type_faceting') { |
|
266
|
|
|
// Translate document type. |
|
267
|
|
|
$entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_structures', $this->conf['pages'])); |
|
268
|
|
|
} elseif ($field == 'collection_faceting') { |
|
269
|
|
|
// Translate name of collection. |
|
270
|
|
|
$entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_collections', $this->conf['pages'])); |
|
271
|
|
|
} elseif ($field == 'language_faceting') { |
|
272
|
|
|
// Translate ISO 639 language code. |
|
273
|
|
|
$entryArray['title'] = htmlspecialchars(Helper::getLanguageName($value)); |
|
274
|
|
|
} else { |
|
275
|
|
|
$entryArray['title'] = htmlspecialchars($value); |
|
276
|
|
|
} |
|
277
|
|
|
$entryArray['count'] = $count; |
|
278
|
|
|
$entryArray['doNotLinkIt'] = 0; |
|
279
|
|
|
// Check if facet is already selected. |
|
280
|
|
|
$queryColumn = array_column($search['params']['filterquery'], 'query'); |
|
281
|
|
|
$index = array_search($field.':("'.Solr::escapeQuery($value).'")', $queryColumn); |
|
282
|
|
|
if ($index !== FALSE) { |
|
283
|
|
|
// Facet is selected, thus remove it from filter. |
|
284
|
|
|
unset($queryColumn[$index]); |
|
285
|
|
|
$queryColumn = array_values($queryColumn); |
|
286
|
|
|
$entryArray['ITEM_STATE'] = 'CUR'; |
|
287
|
|
|
$state = 'ACTIFSUB'; |
|
288
|
|
|
//Reset facets |
|
289
|
|
|
if ($this->conf['resetFacets']) { |
|
290
|
|
|
//remove ($count) for selected facet in template |
|
291
|
|
|
$entryArray['count'] = FALSE; |
|
292
|
|
|
//build link to delete selected facet |
|
293
|
|
|
$entryArray['_OVERRIDE_HREF'] = $this->pi_linkTP_keepPIvars_url(['query' => $search['query'], 'fq' => $queryColumn]); |
|
294
|
|
|
$entryArray['title'] = sprintf($this->pi_getLL('resetFacet', ''), $entryArray['title']); |
|
295
|
|
|
} |
|
296
|
|
|
} else { |
|
297
|
|
|
// Facet is not selected, thus add it to filter. |
|
298
|
|
|
$queryColumn[] = $field.':("'.Solr::escapeQuery($value).'")'; |
|
299
|
|
|
$entryArray['ITEM_STATE'] = 'NO'; |
|
300
|
|
|
} |
|
301
|
|
|
$entryArray['_OVERRIDE_HREF'] = $this->pi_linkTP_keepPIvars_url(['query' => $search['query'], 'fq' => $queryColumn]); |
|
302
|
|
|
return $entryArray; |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* The main method of the PlugIn |
|
307
|
|
|
* |
|
308
|
|
|
* @access public |
|
309
|
|
|
* |
|
310
|
|
|
* @param string $content: The PlugIn content |
|
|
|
|
|
|
311
|
|
|
* @param array $conf: The PlugIn configuration |
|
|
|
|
|
|
312
|
|
|
* |
|
313
|
|
|
* @return string The content that is displayed on the website |
|
314
|
|
|
*/ |
|
315
|
|
|
public function main($content, $conf) { |
|
316
|
|
|
$this->init($conf); |
|
317
|
|
|
// Disable caching for this plugin. |
|
318
|
|
|
$this->setCache(FALSE); |
|
319
|
|
|
// Quit without doing anything if required variables are not set. |
|
320
|
|
|
if (empty($this->conf['solrcore'])) { |
|
321
|
|
|
Helper::devLog('Incomplete plugin configuration', DEVLOG_SEVERITY_WARNING); |
|
322
|
|
|
return $content; |
|
323
|
|
|
} |
|
324
|
|
|
if (!isset($this->piVars['query']) |
|
325
|
|
|
&& empty($this->piVars['extQuery'])) { |
|
326
|
|
|
// Extract query and filter from last search. |
|
327
|
|
|
$list = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(DocumentList::class); |
|
328
|
|
View Code Duplication |
if (!empty($list->metadata['searchString'])) { |
|
|
|
|
|
|
329
|
|
|
if ($list->metadata['options']['source'] == 'search') { |
|
330
|
|
|
$search['query'] = $list->metadata['searchString']; |
|
|
|
|
|
|
331
|
|
|
} |
|
332
|
|
|
$search['params'] = $list->metadata['options']['params']; |
|
|
|
|
|
|
333
|
|
|
} |
|
334
|
|
|
// Add javascript for search suggestions if enabled and jQuery autocompletion is available. |
|
335
|
|
|
if (!empty($this->conf['suggest'])) { |
|
336
|
|
|
$this->addAutocompleteJS(); |
|
337
|
|
|
} |
|
338
|
|
|
// Load template file. |
|
339
|
|
|
$this->getTemplate(); |
|
340
|
|
|
// Configure @action URL for form. |
|
341
|
|
|
$linkConf = [ |
|
342
|
|
|
'parameter' => $GLOBALS['TSFE']->id |
|
343
|
|
|
]; |
|
344
|
|
|
// Fill markers. |
|
345
|
|
|
$markerArray = [ |
|
346
|
|
|
'###ACTION_URL###' => $this->cObj->typoLink_URL($linkConf), |
|
347
|
|
|
'###LABEL_QUERY###' => (!empty($search['query']) ? htmlspecialchars($search['query']) : $this->pi_getLL('label.query')), |
|
348
|
|
|
'###LABEL_SUBMIT###' => $this->pi_getLL('label.submit'), |
|
349
|
|
|
'###FIELD_QUERY###' => $this->prefixId.'[query]', |
|
350
|
|
|
'###QUERY###' => (!empty($search['query']) ? $search['query'] : ''), |
|
351
|
|
|
'###FULLTEXTSWITCH###' => $this->addFulltextSwitch($list->metadata['fulltextSearch']), |
|
352
|
|
|
'###FIELD_DOC###' => ($this->conf['searchIn'] == 'document' || $this->conf['searchIn'] == 'all' ? $this->addCurrentDocument() : ''), |
|
353
|
|
|
'###FIELD_COLL###' => ($this->conf['searchIn'] == 'collection' || $this->conf['searchIn'] == 'all' ? $this->addCurrentCollection() : ''), |
|
354
|
|
|
'###ADDITIONAL_INPUTS###' => $this->addEncryptedCoreName(), |
|
355
|
|
|
'###FACETS_MENU###' => $this->addFacetsMenu(), |
|
356
|
|
|
'###LOGICAL_PAGE###' => $this->addLogicalPage() |
|
357
|
|
|
]; |
|
358
|
|
|
// Get additional fields for extended search. |
|
359
|
|
|
$extendedSearch = $this->addExtendedSearch(); |
|
360
|
|
|
// Display search form. |
|
361
|
|
|
$content .= $this->cObj->substituteSubpart($this->cObj->substituteMarkerArray($this->template, $markerArray), '###EXT_SEARCH_ENTRY###', $extendedSearch); |
|
|
|
|
|
|
362
|
|
|
return $this->pi_wrapInBaseClass($content); |
|
363
|
|
|
} else { |
|
364
|
|
|
// Instantiate search object. |
|
365
|
|
|
$solr = Solr::getInstance($this->conf['solrcore']); |
|
366
|
|
|
if (!$solr->ready) { |
|
|
|
|
|
|
367
|
|
|
Helper::devLog('Apache Solr not available', DEVLOG_SEVERITY_ERROR); |
|
368
|
|
|
return $content; |
|
369
|
|
|
} |
|
370
|
|
|
// Build label for result list. |
|
371
|
|
|
$label = $this->pi_getLL('search', '', TRUE); |
|
372
|
|
|
if (!empty($this->piVars['query'])) { |
|
373
|
|
|
$label .= htmlspecialchars(sprintf($this->pi_getLL('for', ''), $this->piVars['query'])); |
|
374
|
|
|
} |
|
375
|
|
|
// Prepare query parameters. |
|
376
|
|
|
$params = []; |
|
377
|
|
|
$matches = []; |
|
378
|
|
|
// Set search query. |
|
379
|
|
|
if ((!empty($this->conf['fulltext']) && !empty($this->piVars['fulltext'])) |
|
380
|
|
|
|| preg_match('/fulltext:\((.*)\)/', $this->piVars['query'], $matches)) { |
|
381
|
|
|
// If the query already is a fulltext query e.g using the facets |
|
382
|
|
|
$this->piVars['query'] = empty($matches[1]) ? $this->piVars['query'] : $matches[1]; |
|
383
|
|
|
// Search in fulltext field if applicable. query must not be empty! |
|
384
|
|
|
if (!empty($this->piVars['query'])) { |
|
385
|
|
|
$query = 'fulltext:('.Solr::escapeQuery($this->piVars['query']).')'; |
|
386
|
|
|
} |
|
387
|
|
|
} else { |
|
388
|
|
|
// Retain given search field if valid. |
|
389
|
|
|
$query = Solr::escapeQueryKeepField($this->piVars['query'], $this->conf['pages']); |
|
390
|
|
|
} |
|
391
|
|
|
// Add extended search query. |
|
392
|
|
|
if (!empty($this->piVars['extQuery']) |
|
393
|
|
|
&& is_array($this->piVars['extQuery'])) { |
|
394
|
|
|
$allowedOperators = ['AND', 'OR', 'NOT']; |
|
395
|
|
|
$allowedFields = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $this->conf['extendedFields'], TRUE); |
|
396
|
|
|
for ($i = 0; $i < count($this->piVars['extQuery']); $i++) { |
|
|
|
|
|
|
397
|
|
|
if (!empty($this->piVars['extQuery'][$i])) { |
|
398
|
|
|
if (in_array($this->piVars['extOperator'][$i], $allowedOperators) |
|
399
|
|
|
&& in_array($this->piVars['extField'][$i], $allowedFields)) { |
|
400
|
|
|
if (!empty($query)) { |
|
401
|
|
|
$query .= ' '.$this->piVars['extOperator'][$i].' '; |
|
402
|
|
|
} |
|
403
|
|
|
$query .= Indexer::getIndexFieldName($this->piVars['extField'][$i], $this->conf['pages']).':('.Solr::escapeQuery($this->piVars['extQuery'][$i]).')'; |
|
|
|
|
|
|
404
|
|
|
} |
|
405
|
|
|
} |
|
406
|
|
|
} |
|
407
|
|
|
} |
|
408
|
|
|
// Add filter query for faceting. |
|
409
|
|
|
if (!empty($this->piVars['fq'])) { |
|
410
|
|
|
foreach ($this->piVars['fq'] as $filterQuery) { |
|
411
|
|
|
$params['filterquery'][]['query'] = $filterQuery; |
|
412
|
|
|
} |
|
413
|
|
|
} |
|
414
|
|
|
// Add filter query for in-document searching. |
|
415
|
|
|
if ($this->conf['searchIn'] == 'document' |
|
416
|
|
|
|| $this->conf['searchIn'] == 'all') { |
|
417
|
|
|
if (!empty($this->piVars['id']) |
|
418
|
|
|
&& \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->piVars['id'])) { |
|
419
|
|
|
$params['filterquery'][]['query'] = 'uid:('.$this->piVars['id'].') OR partof:('.$this->piVars['id'].')'; |
|
420
|
|
|
$label .= htmlspecialchars(sprintf($this->pi_getLL('in', ''), Document::getTitle($this->piVars['id']))); |
|
421
|
|
|
} |
|
422
|
|
|
} |
|
423
|
|
|
// Add filter query for in-collection searching. |
|
424
|
|
|
if ($this->conf['searchIn'] == 'collection' |
|
425
|
|
|
|| $this->conf['searchIn'] == 'all') { |
|
426
|
|
|
if (!empty($this->piVars['collection']) |
|
427
|
|
|
&& \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->piVars['collection'])) { |
|
428
|
|
|
$index_name = Helper::getIndexNameFromUid($this->piVars['collection'], 'tx_dlf_collections', $this->conf['pages']); |
|
429
|
|
|
$params['filterquery'][]['query'] = 'collection_faceting:("'.Solr::escapeQuery($index_name).'")'; |
|
430
|
|
|
$label .= sprintf($this->pi_getLL('in', '', TRUE), Helper::translate($index_name, 'tx_dlf_collections', $this->conf['pages'])); |
|
431
|
|
|
} |
|
432
|
|
|
} |
|
433
|
|
|
// Add filter query for collection restrictions. |
|
434
|
|
|
if ($this->conf['collections']) { |
|
435
|
|
|
$collIds = explode(',', $this->conf['collections']); |
|
436
|
|
|
$collIndexNames = []; |
|
437
|
|
|
foreach ($collIds as $collId) { |
|
438
|
|
|
$collIndexNames[] = Solr::escapeQuery(Helper::getIndexNameFromUid(intval($collId), 'tx_dlf_collections', $this->conf['pages'])); |
|
439
|
|
|
} |
|
440
|
|
|
// Last value is fake and used for distinction in $this->addCurrentCollection() |
|
441
|
|
|
$params['filterquery'][]['query'] = 'collection_faceting:("'.implode('" OR "', $collIndexNames).'" OR "FakeValueForDistinction")'; |
|
442
|
|
|
} |
|
443
|
|
|
// Set some query parameters. |
|
444
|
|
|
$params['query'] = $query; |
|
445
|
|
|
$params['start'] = 0; |
|
446
|
|
|
$params['rows'] = 0; |
|
447
|
|
|
$params['sort'] = ['score' => 'desc']; |
|
448
|
|
|
// Instantiate search object. |
|
449
|
|
|
$solr = Solr::getInstance($this->conf['solrcore']); |
|
450
|
|
|
if (!$solr->ready) { |
|
|
|
|
|
|
451
|
|
|
Helper::devLog('Apache Solr not available', DEVLOG_SEVERITY_ERROR); |
|
452
|
|
|
return $content; |
|
453
|
|
|
} |
|
454
|
|
|
// Set search parameters. |
|
455
|
|
|
$solr->cPid = $this->conf['pages']; |
|
|
|
|
|
|
456
|
|
|
$solr->params = $params; |
|
|
|
|
|
|
457
|
|
|
// Perform search. |
|
458
|
|
|
$list = $solr->search(); |
|
459
|
|
|
$list->metadata = [ |
|
|
|
|
|
|
460
|
|
|
'label' => $label, |
|
461
|
|
|
'thumbnail' => '', |
|
462
|
|
|
'searchString' => $this->piVars['query'], |
|
463
|
|
|
'fulltextSearch' => (!empty($this->piVars['fulltext']) ? '1' : '0'), |
|
464
|
|
|
'options' => $list->metadata['options'] |
|
|
|
|
|
|
465
|
|
|
]; |
|
466
|
|
|
$list->save(); |
|
467
|
|
|
// Clean output buffer. |
|
468
|
|
|
ob_end_clean(); |
|
469
|
|
|
$additionalParams = []; |
|
470
|
|
|
if (!empty($this->piVars['logicalPage'])) { |
|
471
|
|
|
$additionalParams['logicalPage'] = $this->piVars['logicalPage']; |
|
472
|
|
|
} |
|
473
|
|
|
// Jump directly to the page view, if there is only one result and it is configured |
|
474
|
|
|
if ($list->metadata['options']['numberOfHits'] == 1 && !empty($this->conf['showSingleResult'])) { |
|
|
|
|
|
|
475
|
|
|
$linkConf['parameter'] = $this->conf['targetPidPageView']; |
|
|
|
|
|
|
476
|
|
|
$additionalParams['id'] = $list->current()['uid']; |
|
477
|
|
|
$additionalParams['highlight_word'] = preg_replace('/\s\s+/', ';', $list->metadata['searchString']); |
|
|
|
|
|
|
478
|
|
|
$additionalParams['page'] = count($list[0]['subparts']) == 1 ? $list[0]['subparts'][0]['page'] : 1; |
|
479
|
|
|
} else { |
|
480
|
|
|
// Keep some plugin variables. |
|
481
|
|
|
$linkConf['parameter'] = $this->conf['targetPid']; |
|
|
|
|
|
|
482
|
|
|
if (!empty($this->piVars['order'])) { |
|
483
|
|
|
$additionalParams['order'] = $this->piVars['order']; |
|
484
|
|
|
$additionalParams['asc'] = !empty($this->piVars['asc']) ? '1' : '0'; |
|
485
|
|
|
} |
|
486
|
|
|
} |
|
487
|
|
|
$linkConf['additionalParams'] = \TYPO3\CMS\Core\Utility\GeneralUtility::implodeArrayForUrl($this->prefixId, $additionalParams, '', TRUE, FALSE); |
|
488
|
|
|
// Send headers. |
|
489
|
|
|
header('Location: '.\TYPO3\CMS\Core\Utility\GeneralUtility::locationHeaderUrl($this->cObj->typoLink_URL($linkConf))); |
|
490
|
|
|
exit; |
|
491
|
|
|
} |
|
492
|
|
|
} |
|
493
|
|
|
|
|
494
|
|
|
/** |
|
495
|
|
|
* This builds a menu array for HMENU |
|
496
|
|
|
* |
|
497
|
|
|
* @access public |
|
498
|
|
|
* |
|
499
|
|
|
* @param string $content: The PlugIn content |
|
|
|
|
|
|
500
|
|
|
* @param array $conf: The PlugIn configuration |
|
|
|
|
|
|
501
|
|
|
* |
|
502
|
|
|
* @return array HMENU array |
|
503
|
|
|
*/ |
|
504
|
|
|
public function makeFacetsMenuArray($content, $conf) { |
|
|
|
|
|
|
505
|
|
|
$this->init($conf); |
|
506
|
|
|
$menuArray = []; |
|
507
|
|
|
// Set default value for facet search. |
|
508
|
|
|
$search = [ |
|
509
|
|
|
'query' => '*', |
|
510
|
|
|
'params' => [ |
|
511
|
|
|
'component' => [ |
|
512
|
|
|
'facetset' => [ |
|
513
|
|
|
'facet' => [] |
|
514
|
|
|
] |
|
515
|
|
|
] |
|
516
|
|
|
] |
|
517
|
|
|
]; |
|
518
|
|
|
// Extract query and filter from last search. |
|
519
|
|
|
$list = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(DocumentList::class); |
|
520
|
|
View Code Duplication |
if (!empty($list->metadata['options']['source'])) { |
|
|
|
|
|
|
521
|
|
|
if ($list->metadata['options']['source'] == 'search') { |
|
522
|
|
|
$search['query'] = $list->metadata['options']['select']; |
|
523
|
|
|
} |
|
524
|
|
|
$search['params'] = $list->metadata['options']['params']; |
|
525
|
|
|
} |
|
526
|
|
|
// Get applicable facets. |
|
527
|
|
|
$solr = Solr::getInstance($this->conf['solrcore']); |
|
528
|
|
|
if (!$solr->ready) { |
|
|
|
|
|
|
529
|
|
|
Helper::devLog('Apache Solr not available', DEVLOG_SEVERITY_ERROR); |
|
530
|
|
|
return []; |
|
531
|
|
|
} |
|
532
|
|
|
// Set needed parameters for facet search. |
|
533
|
|
|
if (empty($search['params']['filterquery'])) { |
|
534
|
|
|
$search['params']['filterquery'] = []; |
|
535
|
|
|
} |
|
536
|
|
|
foreach (array_keys($this->conf['facets']) as $field) { |
|
537
|
|
|
$search['params']['component']['facetset']['facet'][] = [ |
|
538
|
|
|
'type' => 'field', |
|
539
|
|
|
'key' => $field, |
|
540
|
|
|
'field' => $field, |
|
541
|
|
|
'limit' => $this->conf['limitFacets'], |
|
542
|
|
|
'sort' => isset($this->conf['sortingFacets']) ? $this->conf['sortingFacets'] : 'count' |
|
543
|
|
|
]; |
|
544
|
|
|
} |
|
545
|
|
|
// Set additional query parameters. |
|
546
|
|
|
$search['params']['start'] = 0; |
|
547
|
|
|
$search['params']['rows'] = 0; |
|
548
|
|
|
// Set query. |
|
549
|
|
|
$search['params']['query'] = $search['query']; |
|
550
|
|
|
// Perform search. |
|
551
|
|
|
$selectQuery = $solr->service->createSelect($search['params']); |
|
|
|
|
|
|
552
|
|
|
$results = $solr->service->select($selectQuery); |
|
|
|
|
|
|
553
|
|
|
$facet = $results->getFacetSet(); |
|
554
|
|
|
// Process results. |
|
555
|
|
|
foreach ($facet as $field => $values) { |
|
|
|
|
|
|
556
|
|
|
$entryArray = []; |
|
557
|
|
|
$entryArray['title'] = htmlspecialchars($this->conf['facets'][$field]); |
|
558
|
|
|
$entryArray['count'] = 0; |
|
559
|
|
|
$entryArray['_OVERRIDE_HREF'] = ''; |
|
560
|
|
|
$entryArray['doNotLinkIt'] = 1; |
|
561
|
|
|
$entryArray['ITEM_STATE'] = 'NO'; |
|
562
|
|
|
// Count number of facet values. |
|
563
|
|
|
$i = 0; |
|
564
|
|
|
foreach ($values as $value => $count) { |
|
565
|
|
|
if ($count > 0) { |
|
566
|
|
|
$entryArray['count']++; |
|
567
|
|
|
if ($entryArray['ITEM_STATE'] == 'NO') { |
|
568
|
|
|
$entryArray['ITEM_STATE'] = 'IFSUB'; |
|
569
|
|
|
} |
|
570
|
|
|
$entryArray['_SUB_MENU'][] = $this->getFacetsMenuEntry($field, $value, $count, $search, $entryArray['ITEM_STATE']); |
|
571
|
|
|
if (++$i == $this->conf['limit']) { |
|
572
|
|
|
break; |
|
573
|
|
|
} |
|
574
|
|
|
} else { |
|
575
|
|
|
break; |
|
576
|
|
|
} |
|
577
|
|
|
} |
|
578
|
|
|
$menuArray[] = $entryArray; |
|
579
|
|
|
} |
|
580
|
|
|
return $menuArray; |
|
581
|
|
|
} |
|
582
|
|
|
} |
|
583
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.