1 | <?php |
||||||
2 | |||||||
3 | /** |
||||||
4 | * The admin screen to change the search settings. Allows for the creation \ |
||||||
5 | * of search indexes and search weights |
||||||
6 | * |
||||||
7 | * @package ElkArte Forum |
||||||
8 | * @copyright ElkArte Forum contributors |
||||||
9 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||||
10 | * |
||||||
11 | * This file contains code covered by: |
||||||
12 | * copyright: 2011 Simple Machines (http://www.simplemachines.org) |
||||||
13 | * |
||||||
14 | * @version 2.0 dev |
||||||
15 | * |
||||||
16 | */ |
||||||
17 | |||||||
18 | namespace ElkArte\AdminController; |
||||||
19 | |||||||
20 | use ElkArte\AbstractController; |
||||||
21 | use ElkArte\Action; |
||||||
22 | use ElkArte\Helper\FileFunctions; |
||||||
23 | use ElkArte\Helper\Util; |
||||||
24 | use ElkArte\Languages\Txt; |
||||||
25 | use ElkArte\Search\SearchApiWrapper; |
||||||
26 | use ElkArte\SettingsForm\SettingsForm; |
||||||
27 | use Exception; |
||||||
28 | use FilesystemIterator; |
||||||
29 | use GlobIterator; |
||||||
30 | use SphinxClient; |
||||||
0 ignored issues
–
show
|
|||||||
31 | use UnexpectedValueException; |
||||||
32 | |||||||
33 | /** |
||||||
34 | * ManageSearch controller admin class. |
||||||
35 | * |
||||||
36 | * @package Search |
||||||
37 | */ |
||||||
38 | class ManageSearch extends AbstractController |
||||||
39 | { |
||||||
40 | /** |
||||||
41 | * Main entry point for the admin search settings screen. |
||||||
42 | * |
||||||
43 | * What it does: |
||||||
44 | * |
||||||
45 | * - It checks permissions, and it forwards to the appropriate function based on |
||||||
46 | * the given sub-action. |
||||||
47 | * - Defaults to sub-action 'settings'. |
||||||
48 | * - Called by ?action=admin;area=managesearch. |
||||||
49 | * - Requires the admin_forum permission. |
||||||
50 | * |
||||||
51 | * @event integrate_sa_manage_search add new search actions |
||||||
52 | * @uses ManageSearch template. |
||||||
53 | * @uses Search language file. |
||||||
54 | * @see AbstractController::action_index() |
||||||
55 | */ |
||||||
56 | public function action_index() |
||||||
57 | { |
||||||
58 | global $context, $txt; |
||||||
59 | |||||||
60 | Txt::load('Search'); |
||||||
61 | theme()->getTemplates()->load('ManageSearch'); |
||||||
62 | |||||||
63 | $subActions = array( |
||||||
64 | 'settings' => array($this, 'action_searchSettings_display', 'permission' => 'admin_forum'), |
||||||
65 | 'weights' => array($this, 'action_weight', 'permission' => 'admin_forum'), |
||||||
66 | 'method' => array($this, 'action_edit', 'permission' => 'admin_forum'), |
||||||
67 | 'createfulltext' => array($this, 'action_edit', 'permission' => 'admin_forum'), |
||||||
68 | 'removecustom' => array($this, 'action_edit', 'permission' => 'admin_forum'), |
||||||
69 | 'removefulltext' => array($this, 'action_edit', 'permission' => 'admin_forum'), |
||||||
70 | 'createmsgindex' => array($this, 'action_create', 'permission' => 'admin_forum'), |
||||||
71 | 'managesphinx' => array($this, 'action_managesphinx', 'permission' => 'admin_forum'), |
||||||
72 | 'managesphinxql' => array($this, 'action_managesphinx', 'permission' => 'admin_forum'), |
||||||
73 | ); |
||||||
74 | |||||||
75 | // Control for actions |
||||||
76 | $action = new Action('manage_search'); |
||||||
77 | |||||||
78 | // Default the sub-action to 'edit search method'. Call integrate_sa_manage_search |
||||||
79 | $subAction = $action->initialize($subActions, 'method'); |
||||||
80 | |||||||
81 | // Final bits |
||||||
82 | $context['sub_action'] = $subAction; |
||||||
83 | $context['page_title'] = $txt['search_settings_title']; |
||||||
84 | |||||||
85 | // Create the tabs |
||||||
86 | $context[$context['admin_menu_name']]['object']->prepareTabData([ |
||||||
87 | 'title' => 'manage_search', |
||||||
88 | 'description' => 'search_settings_desc', |
||||||
89 | 'prefix' => 'search', |
||||||
90 | 'help' => 'search'] |
||||||
91 | ); |
||||||
92 | |||||||
93 | // Call the right function for this sub-action. |
||||||
94 | $action->dispatch($subAction); |
||||||
95 | } |
||||||
96 | |||||||
97 | /** |
||||||
98 | * Edit some general settings related to the search function. |
||||||
99 | * |
||||||
100 | * - Called by ?action=admin;area=managesearch;sa=settings. |
||||||
101 | * - Requires the admin_forum permission. |
||||||
102 | * |
||||||
103 | * @event integrate_save_search_settings |
||||||
104 | * @uses ManageSearch template, 'modify_settings' sub-template. |
||||||
105 | */ |
||||||
106 | public function action_searchSettings_display() |
||||||
107 | { |
||||||
108 | global $txt, $context, $modSettings; |
||||||
109 | |||||||
110 | // Initialize the form |
||||||
111 | $settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER); |
||||||
112 | |||||||
113 | // Initialize it with our settings |
||||||
114 | $settingsForm->setConfigVars($this->_settings()); |
||||||
115 | |||||||
116 | $context['page_title'] = $txt['search_settings_title']; |
||||||
117 | $context['sub_template'] = 'show_settings'; |
||||||
118 | |||||||
119 | $context['search_engines'] = array(); |
||||||
120 | if (!empty($modSettings['additional_search_engines'])) |
||||||
121 | { |
||||||
122 | $context['search_engines'] = Util::unserialize($modSettings['additional_search_engines']); |
||||||
123 | } |
||||||
124 | |||||||
125 | for ($count = 0; $count < 3; $count++) |
||||||
126 | { |
||||||
127 | $context['search_engines'][] = array( |
||||||
128 | 'name' => '', |
||||||
129 | 'url' => '', |
||||||
130 | 'separator' => '', |
||||||
131 | ); |
||||||
132 | } |
||||||
133 | |||||||
134 | // A form was submitted. |
||||||
135 | if (isset($this->_req->query->save)) |
||||||
136 | { |
||||||
137 | checkSession(); |
||||||
138 | |||||||
139 | call_integration_hook('integrate_save_search_settings'); |
||||||
140 | |||||||
141 | if (empty($this->_req->post->search_results_per_page)) |
||||||
142 | { |
||||||
143 | $this->_req->post->search_results_per_page = empty($modSettings['search_results_per_page']) ? $modSettings['defaultMaxMessages'] : $modSettings['search_results_per_page']; |
||||||
144 | } |
||||||
145 | |||||||
146 | $new_engines = array(); |
||||||
147 | foreach ($this->_req->post->engine_name as $id => $searchengine) |
||||||
148 | { |
||||||
149 | $url = trim(str_replace(array('"', '<', '>'), array('"', '<', '>'), $this->_req->post->engine_url[$id])); |
||||||
150 | // If no url, forget it |
||||||
151 | if (empty($searchengine)) |
||||||
152 | { |
||||||
153 | continue; |
||||||
154 | } |
||||||
155 | |||||||
156 | if (empty($url)) |
||||||
157 | { |
||||||
158 | continue; |
||||||
159 | } |
||||||
160 | |||||||
161 | if (!filter_var($url, FILTER_VALIDATE_URL)) |
||||||
162 | { |
||||||
163 | continue; |
||||||
164 | } |
||||||
165 | |||||||
166 | $new_engines[] = array( |
||||||
167 | 'name' => trim(Util::htmlspecialchars($searchengine, ENT_COMPAT)), |
||||||
168 | 'url' => $url, |
||||||
169 | 'separator' => trim(Util::htmlspecialchars(empty($this->_req->post->engine_separator[$id]) ? '+' : $this->_req->post->engine_separator[$id], ENT_COMPAT)), |
||||||
170 | ); |
||||||
171 | } |
||||||
172 | |||||||
173 | updateSettings(array( |
||||||
174 | 'additional_search_engines' => $new_engines === [] ? '' : serialize($new_engines) |
||||||
175 | )); |
||||||
176 | |||||||
177 | $settingsForm->setConfigValues((array) $this->_req->post); |
||||||
178 | $settingsForm->save(); |
||||||
179 | redirectexit('action=admin;area=managesearch;sa=settings;' . $context['session_var'] . '=' . $context['session_id']); |
||||||
180 | } |
||||||
181 | |||||||
182 | // Prep the template! |
||||||
183 | $context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'managesearch', 'save', 'sa' => 'settings']); |
||||||
184 | 2 | $context['settings_title'] = $txt['search_settings_title']; |
|||||
185 | |||||||
186 | 2 | $settingsForm->prepare(); |
|||||
187 | } |
||||||
188 | |||||||
189 | /** |
||||||
190 | * Retrieve admin search settings |
||||||
191 | 2 | * |
|||||
192 | * @event integrate_modify_search_settings |
||||||
193 | */ |
||||||
194 | private function _settings() |
||||||
195 | 2 | { |
|||||
196 | 2 | global $txt, $modSettings; |
|||||
197 | |||||||
198 | 2 | // What are we editing anyway? |
|||||
199 | $config_vars = array( |
||||||
200 | // Permission... |
||||||
201 | array('permissions', 'search_posts', 'collapsed' => 'true'), |
||||||
202 | // Some simple settings. |
||||||
203 | array('check', 'search_dropdown'), |
||||||
204 | 2 | array('int', 'search_results_per_page'), |
|||||
205 | 2 | array('int', 'search_max_results', 'subtext' => $txt['search_max_results_disable']), |
|||||
206 | // Some limitations. |
||||||
207 | array('int', 'search_floodcontrol_time', 'subtext' => $txt['search_floodcontrol_time_desc'], 6, 'postinput' => $txt['seconds']), |
||||||
208 | 2 | array('title', 'additional_search_engines'), |
|||||
209 | array('callback', 'external_search_engines'), |
||||||
210 | 2 | ); |
|||||
211 | |||||||
212 | // Perhaps the search method wants to add some settings? |
||||||
213 | $searchAPI = new SearchApiWrapper(empty($modSettings['search_index']) ? '' : $modSettings['search_index']); |
||||||
214 | $searchAPI->searchSettings($config_vars); |
||||||
215 | |||||||
216 | 2 | // Add new settings with a nice hook, makes them available for admin settings search as well |
|||||
217 | call_integration_hook('integrate_modify_search_settings', array(&$config_vars)); |
||||||
218 | 2 | ||||||
219 | return $config_vars; |
||||||
220 | } |
||||||
221 | |||||||
222 | /** |
||||||
223 | * Return the search settings for use in admin search |
||||||
224 | */ |
||||||
225 | public function settings_search() |
||||||
226 | { |
||||||
227 | return $this->_settings(); |
||||||
228 | } |
||||||
229 | |||||||
230 | /** |
||||||
231 | * Edit the relative weight of the search factors. |
||||||
232 | * |
||||||
233 | * - Called by ?action=admin;area=managesearch;sa=weights. |
||||||
234 | * - Requires the admin_forum permission. |
||||||
235 | * |
||||||
236 | * @event integrate_modify_search_weights |
||||||
237 | * @event integrate_save_search_weights |
||||||
238 | * @uses ManageSearch template, 'modify_weights' sub-template. |
||||||
239 | */ |
||||||
240 | public function action_weight() |
||||||
241 | { |
||||||
242 | global $txt, $context, $modSettings; |
||||||
243 | |||||||
244 | $context['page_title'] = $txt['search_weights_title']; |
||||||
245 | $context['sub_template'] = 'modify_weights'; |
||||||
246 | |||||||
247 | $factors = array( |
||||||
248 | 'search_weight_frequency', |
||||||
249 | 'search_weight_age', |
||||||
250 | 'search_weight_length', |
||||||
251 | 'search_weight_subject', |
||||||
252 | 'search_weight_first_message', |
||||||
253 | 'search_weight_sticky', |
||||||
254 | 'search_weight_likes', |
||||||
255 | ); |
||||||
256 | |||||||
257 | call_integration_hook('integrate_modify_search_weights', array(&$factors)); |
||||||
258 | |||||||
259 | // A form was submitted. |
||||||
260 | if (isset($this->_req->post->save)) |
||||||
261 | { |
||||||
262 | checkSession(); |
||||||
263 | validateToken('admin-msw'); |
||||||
264 | |||||||
265 | call_integration_hook('integrate_save_search_weights'); |
||||||
266 | |||||||
267 | $changes = array(); |
||||||
268 | foreach ($factors as $factor) |
||||||
269 | { |
||||||
270 | $changes[$factor] = (int) $this->_req->post->{$factor}; |
||||||
271 | } |
||||||
272 | |||||||
273 | updateSettings($changes); |
||||||
274 | } |
||||||
275 | |||||||
276 | $context['relative_weights'] = array('total' => 0); |
||||||
277 | foreach ($factors as $factor) |
||||||
278 | { |
||||||
279 | $context['relative_weights']['total'] += $modSettings[$factor] ?? 0; |
||||||
280 | } |
||||||
281 | |||||||
282 | foreach ($factors as $factor) |
||||||
283 | { |
||||||
284 | $context['relative_weights'][$factor] = round(100 * ($modSettings[$factor] ?? 0) / $context['relative_weights']['total'], 1); |
||||||
285 | } |
||||||
286 | |||||||
287 | createToken('admin-msw'); |
||||||
288 | } |
||||||
289 | |||||||
290 | /** |
||||||
291 | * Edit the search method and search index used. |
||||||
292 | * |
||||||
293 | * What it does: |
||||||
294 | * |
||||||
295 | * - Calculates the size of the current search indexes in use. |
||||||
296 | * - Allows to create and delete a fulltext index on the messages table. |
||||||
297 | * - Allows to delete a custom index (that action_create() created). |
||||||
298 | * - Called by ?action=admin;area=managesearch;sa=method. |
||||||
299 | * - Requires the admin_forum permission. |
||||||
300 | * |
||||||
301 | * @uses ManageSearch template, 'select_search_method' sub-template. |
||||||
302 | */ |
||||||
303 | public function action_edit() |
||||||
304 | { |
||||||
305 | global $txt, $context, $modSettings; |
||||||
306 | |||||||
307 | // Need to work with some db search stuffs |
||||||
308 | $db_search = db_search(); |
||||||
309 | require_once(SUBSDIR . '/ManageSearch.subs.php'); |
||||||
310 | |||||||
311 | $context[$context['admin_menu_name']]['current_subsection'] = 'method'; |
||||||
312 | $context['page_title'] = $txt['search_method_title']; |
||||||
313 | $context['sub_template'] = 'select_search_method'; |
||||||
314 | $context['supports_fulltext'] = $db_search->search_support('fulltext'); |
||||||
315 | $context['fulltext_index'] = false; |
||||||
316 | |||||||
317 | // Load any apis. |
||||||
318 | $context['search_apis'] = $this->loadSearchAPIs(); |
||||||
319 | |||||||
320 | // Detect whether a fulltext index is set. |
||||||
321 | if ($context['supports_fulltext']) |
||||||
322 | { |
||||||
323 | $fulltext_index = detectFulltextIndex(); |
||||||
0 ignored issues
–
show
The function
detectFulltextIndex was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
324 | } |
||||||
325 | |||||||
326 | // Creating index, removing or simply changing the one in use? |
||||||
327 | $sa = $this->_req->getQuery('sa', 'trim', ''); |
||||||
328 | if ($sa === 'createfulltext') |
||||||
329 | { |
||||||
330 | checkSession('get'); |
||||||
331 | validateToken('admin-msm', 'get'); |
||||||
332 | |||||||
333 | alterFullTextIndex('{db_prefix}messages', array('body', 'subject', 'body,subject'), true); |
||||||
0 ignored issues
–
show
The function
alterFullTextIndex was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
334 | $fulltext_index = true; |
||||||
335 | } |
||||||
336 | elseif ($sa === 'removefulltext' && !empty($fulltext_index)) |
||||||
337 | { |
||||||
338 | checkSession('get'); |
||||||
339 | validateToken('admin-msm', 'get'); |
||||||
340 | |||||||
341 | alterFullTextIndex('{db_prefix}messages', $fulltext_index); |
||||||
342 | $fulltext_index = false; |
||||||
343 | |||||||
344 | // Go back to the default search method. |
||||||
345 | if (!empty($modSettings['search_index']) && $modSettings['search_index'] === 'fulltext') |
||||||
346 | { |
||||||
347 | updateSettings(array( |
||||||
348 | 'search_index' => '', |
||||||
349 | )); |
||||||
350 | } |
||||||
351 | } |
||||||
352 | elseif ($sa === 'removecustom') |
||||||
353 | { |
||||||
354 | checkSession('get'); |
||||||
355 | validateToken('admin-msm', 'get'); |
||||||
356 | |||||||
357 | drop_log_search_words(); |
||||||
0 ignored issues
–
show
The function
drop_log_search_words was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
358 | |||||||
359 | updateSettings(array( |
||||||
360 | 'search_custom_index_config' => '', |
||||||
361 | 'search_custom_index_resume' => '', |
||||||
362 | )); |
||||||
363 | |||||||
364 | // Go back to the default search method. |
||||||
365 | if (!empty($modSettings['search_index']) && $modSettings['search_index'] === 'custom') |
||||||
366 | { |
||||||
367 | updateSettings(array( |
||||||
368 | 'search_index' => '', |
||||||
369 | )); |
||||||
370 | } |
||||||
371 | } |
||||||
372 | elseif (isset($this->_req->post->save)) |
||||||
373 | { |
||||||
374 | checkSession(); |
||||||
375 | validateToken('admin-msmpost'); |
||||||
376 | |||||||
377 | updateSettings(array( |
||||||
378 | 'search_index' => empty($this->_req->post->search_index) || (!in_array($this->_req->post->search_index, array('fulltext', 'custom')) && !isset($context['search_apis'][$this->_req->post->search_index])) ? '' : $this->_req->post->search_index, |
||||||
379 | 'search_force_index' => isset($this->_req->post->search_force_index) ? '1' : '0', |
||||||
380 | 'search_match_words' => isset($this->_req->post->search_match_words) ? '1' : '0', |
||||||
381 | )); |
||||||
382 | } |
||||||
383 | |||||||
384 | $table_info_defaults = array( |
||||||
385 | 'data_length' => 0, |
||||||
386 | 'index_length' => 0, |
||||||
387 | 'fulltext_length' => 0, |
||||||
388 | 'custom_index_length' => 0, |
||||||
389 | ); |
||||||
390 | |||||||
391 | // Get some info about the messages table, to show its size and index size. |
||||||
392 | if (method_exists($db_search, 'membersTableInfo')) |
||||||
393 | { |
||||||
394 | $context['table_info'] = array_merge($table_info_defaults, $db_search->membersTableInfo()); |
||||||
395 | } |
||||||
396 | else |
||||||
397 | { |
||||||
398 | // Here may be wolves. |
||||||
399 | $context['table_info'] = array( |
||||||
400 | 'data_length' => $txt['not_applicable'], |
||||||
401 | 'index_length' => $txt['not_applicable'], |
||||||
402 | 'fulltext_length' => $txt['not_applicable'], |
||||||
403 | 'custom_index_length' => $txt['not_applicable'], |
||||||
404 | ); |
||||||
405 | } |
||||||
406 | |||||||
407 | // Format the data and index length in human readable form. |
||||||
408 | foreach ($context['table_info'] as $type => $size) |
||||||
409 | { |
||||||
410 | // If it's not numeric then just break. This database engine doesn't support size. |
||||||
411 | if (!is_numeric($size)) |
||||||
412 | { |
||||||
413 | break; |
||||||
414 | } |
||||||
415 | |||||||
416 | $context['table_info'][$type] = byte_format($context['table_info'][$type]); |
||||||
417 | } |
||||||
418 | |||||||
419 | $context['custom_index'] = !empty($modSettings['search_custom_index_config']); |
||||||
420 | $context['partial_custom_index'] = !empty($modSettings['search_custom_index_resume']) && empty($modSettings['search_custom_index_config']); |
||||||
421 | $context['double_index'] = !empty($context['fulltext_index']) && $context['custom_index']; |
||||||
422 | $context['fulltext_index'] = !empty($fulltext_index); |
||||||
423 | |||||||
424 | createToken('admin-msmpost'); |
||||||
425 | createToken('admin-msm', 'get'); |
||||||
426 | } |
||||||
427 | |||||||
428 | /** |
||||||
429 | * Get the installed Search API implementations. |
||||||
430 | * |
||||||
431 | * - This function checks for patterns in comments on top of the Search-API files! |
||||||
432 | * - It loads the search API classes if identified. |
||||||
433 | * - This function is used by action_edit to list all installed API implementations. |
||||||
434 | */ |
||||||
435 | private function loadSearchAPIs() |
||||||
436 | { |
||||||
437 | global $txt; |
||||||
438 | |||||||
439 | $apis = array(); |
||||||
440 | try |
||||||
441 | { |
||||||
442 | $files = new GlobIterator(SOURCEDIR . '/ElkArte/Search/API/*.php', FilesystemIterator::SKIP_DOTS); |
||||||
443 | foreach ($files as $file) |
||||||
444 | { |
||||||
445 | if ($file->isFile()) |
||||||
446 | { |
||||||
447 | $index_name = $file->getBasename('.php'); |
||||||
448 | $common_name = strtolower($index_name); |
||||||
449 | |||||||
450 | if ($common_name === 'searchapi') |
||||||
451 | { |
||||||
452 | continue; |
||||||
453 | } |
||||||
454 | |||||||
455 | $apis[$index_name] = array( |
||||||
456 | 'filename' => $file->getFilename(), |
||||||
457 | 'setting_index' => $index_name, |
||||||
458 | 'has_template' => in_array($common_name, array('custom', 'fulltext', 'standard')), |
||||||
459 | 'label' => $index_name && isset($txt['search_index_' . $common_name]) ? str_replace('{managesearch_url}', getUrl('admin', ['action' => 'admin', 'area' => 'managesearch', 'sa' => 'manage' . $common_name]), $txt['search_index_' . $common_name]) : '', |
||||||
460 | 'desc' => $index_name && isset($txt['search_index_' . $common_name . '_desc']) ? str_replace('{managesearch_url}', getUrl('admin', ['action' => 'admin', 'area' => 'managesearch', 'sa' => 'manage' . $common_name]), $txt['search_index_' . $common_name . '_desc']) : '', |
||||||
461 | ); |
||||||
462 | } |
||||||
463 | } |
||||||
464 | } |
||||||
465 | catch (UnexpectedValueException) |
||||||
466 | { |
||||||
467 | // @todo for now just passthrough |
||||||
468 | } |
||||||
469 | |||||||
470 | return $apis; |
||||||
471 | } |
||||||
472 | |||||||
473 | /** |
||||||
474 | * Create a custom search index for the messages table. |
||||||
475 | * |
||||||
476 | * What it does: |
||||||
477 | * |
||||||
478 | * - Called by ?action=admin;area=managesearch;sa=createmsgindex. |
||||||
479 | * - Linked from the action_edit screen. |
||||||
480 | * - Requires the admin_forum permission. |
||||||
481 | * - Depending on the size of the message table, the process is divided in steps. |
||||||
482 | * |
||||||
483 | * @uses ManageSearch template, 'create_index_progress', and 'create_index_done' |
||||||
484 | * sub-templates. |
||||||
485 | */ |
||||||
486 | public function action_create() |
||||||
487 | { |
||||||
488 | global $modSettings, $context, $txt, $db_show_debug; |
||||||
489 | |||||||
490 | // Scotty, we need more time... |
||||||
491 | detectServer()->setTimeLimit(600); |
||||||
492 | |||||||
493 | $context[$context['admin_menu_name']]['current_subsection'] = 'method'; |
||||||
494 | $context['page_title'] = $txt['search_index_custom']; |
||||||
495 | $context['sub_template'] = 'create_index_progress'; |
||||||
496 | |||||||
497 | $messages_per_batch = 75; |
||||||
498 | |||||||
499 | // Resume building an index that was not completed |
||||||
500 | if (isset($this->_req->query->resume) && !empty($modSettings['search_custom_index_resume'])) |
||||||
501 | { |
||||||
502 | $context['index_settings'] = Util::unserialize($modSettings['search_custom_index_resume']); |
||||||
503 | $context['start'] = (int) $context['index_settings']['resume_at']; |
||||||
504 | unset($context['index_settings']['resume_at']); |
||||||
505 | $context['step'] = 1; |
||||||
506 | } |
||||||
507 | else |
||||||
508 | { |
||||||
509 | $context['index_settings'] = array(); |
||||||
510 | $context['start'] = $this->_req->getPost('start', 'intval', 0); |
||||||
511 | $context['step'] = $this->_req->getPost('step', 'intval', 1); |
||||||
512 | } |
||||||
513 | |||||||
514 | checkSession('request'); |
||||||
515 | |||||||
516 | // Admin timeouts are painful when building these long indexes |
||||||
517 | $_SESSION['admin_time'] = time(); |
||||||
518 | |||||||
519 | require_once(SUBSDIR . '/ManageSearch.subs.php'); |
||||||
520 | |||||||
521 | // Logging may cause session issues with many queries |
||||||
522 | $old_db_show_debug = $db_show_debug; |
||||||
523 | $db_show_debug = false; |
||||||
524 | |||||||
525 | // Step 1: insert all the words. |
||||||
526 | if ($context['step'] === 1) |
||||||
527 | { |
||||||
528 | $context['sub_template'] = 'create_index_progress'; |
||||||
529 | [$context['start'], $context['step'], $context['percentage']] = createSearchIndex($context['start'], $messages_per_batch); |
||||||
0 ignored issues
–
show
The function
createSearchIndex was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
530 | } |
||||||
531 | |||||||
532 | // Step 2: removing the words that occur too often and are of no use. |
||||||
533 | if ($context['step'] === 2) |
||||||
534 | { |
||||||
535 | [$context['start'], $complete, $context['percentage']] = removeCommonWordsFromIndex($context['start']); |
||||||
0 ignored issues
–
show
The function
removeCommonWordsFromIndex was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
536 | if ($complete) |
||||||
537 | { |
||||||
538 | $context['step'] = 3; |
||||||
539 | } |
||||||
540 | |||||||
541 | $context['sub_template'] = 'create_index_progress'; |
||||||
542 | } |
||||||
543 | |||||||
544 | // Restore previous debug state |
||||||
545 | $db_show_debug = $old_db_show_debug; |
||||||
546 | |||||||
547 | // Step 3: everything done. |
||||||
548 | if ($context['step'] === 3) |
||||||
549 | { |
||||||
550 | $context['sub_template'] = 'create_index_done'; |
||||||
551 | |||||||
552 | updateSettings(array('search_index' => 'custom', 'search_custom_index_config' => serialize($context['index_settings']))); |
||||||
553 | removeSettings('search_custom_index_resume'); |
||||||
554 | } |
||||||
555 | } |
||||||
556 | |||||||
557 | /** |
||||||
558 | * Edit settings related to the sphinx or sphinxQL search function. |
||||||
559 | * |
||||||
560 | * - Called by ?action=admin;area=managesearch;sa=sphinx. |
||||||
561 | * - Checks if connection to search daemon is possible |
||||||
562 | */ |
||||||
563 | public function action_managesphinx() |
||||||
564 | { |
||||||
565 | global $txt, $context, $modSettings; |
||||||
566 | |||||||
567 | // Saving the settings |
||||||
568 | if (isset($this->_req->post->save)) |
||||||
569 | { |
||||||
570 | checkSession(); |
||||||
571 | validateToken('admin-mssphinx'); |
||||||
572 | $this->_saveSphinxConfig(); |
||||||
573 | } |
||||||
574 | // Checking if we can connect? |
||||||
575 | elseif (isset($this->_req->post->checkconnect)) |
||||||
576 | { |
||||||
577 | checkSession(); |
||||||
578 | validateToken('admin-mssphinx'); |
||||||
579 | |||||||
580 | // If they have not picked sphinx yet, let them know, but we can still check connections |
||||||
581 | if (empty($modSettings['search_index']) || stripos($modSettings['search_index'], 'sphinx') === false) |
||||||
582 | { |
||||||
583 | $context['settings_message'][] = $txt['sphinx_test_not_selected']; |
||||||
584 | $context['error_type'] = 'notice'; |
||||||
585 | } |
||||||
586 | |||||||
587 | // Try to connect via Sphinx API? |
||||||
588 | if (empty($modSettings['search_index']) || $modSettings['search_index'] === 'Sphinx') |
||||||
589 | { |
||||||
590 | $this->connectSphinxApi(); |
||||||
591 | } |
||||||
592 | |||||||
593 | // Try to connect via SphinxQL |
||||||
594 | if (empty($modSettings['search_index']) || $modSettings['search_index'] === 'Sphinxql') |
||||||
595 | { |
||||||
596 | $this->connectSphinxQL(); |
||||||
597 | } |
||||||
598 | } |
||||||
599 | elseif (isset($this->_req->post->createconfig)) |
||||||
600 | { |
||||||
601 | checkSession(); |
||||||
602 | validateToken('admin-mssphinx'); |
||||||
603 | $this->_saveSphinxConfig(); |
||||||
604 | |||||||
605 | require_once(SUBSDIR . '/ManageSearch.subs.php'); |
||||||
606 | |||||||
607 | createSphinxConfig(); |
||||||
0 ignored issues
–
show
The function
createSphinxConfig was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
608 | } |
||||||
609 | |||||||
610 | // Setup for the template |
||||||
611 | $context[$context['admin_menu_name']]['current_subsection'] = 'managesphinx'; |
||||||
612 | $context['page_title'] = $txt['search_sphinx']; |
||||||
613 | $context['page_description'] = $txt['sphinx_create_config']; |
||||||
614 | $context['sub_template'] = 'manage_sphinx'; |
||||||
615 | createToken('admin-mssphinx'); |
||||||
616 | } |
||||||
617 | |||||||
618 | /** |
||||||
619 | * Save the form values in modsettings |
||||||
620 | */ |
||||||
621 | private function _saveSphinxConfig() |
||||||
622 | { |
||||||
623 | updateSettings(array( |
||||||
624 | 'sphinx_index_prefix' => rtrim($this->_req->post->sphinx_index_prefix, '/'), |
||||||
625 | 'sphinx_data_path' => rtrim($this->_req->post->sphinx_data_path, '/'), |
||||||
626 | 'sphinx_log_path' => rtrim($this->_req->post->sphinx_log_path, '/'), |
||||||
627 | 'sphinx_stopword_path' => $this->_req->getPost('sphinx_stopword_path', 'trim', ''), |
||||||
628 | 'sphinx_indexer_mem' => $this->_req->getPost('sphinx_indexer_mem', 'intval', 128), |
||||||
629 | 'sphinx_searchd_server' => $this->_req->getPost('sphinx_searchd_server', 'trim', 'localhost'), |
||||||
630 | 'sphinx_searchd_port' => $this->_req->getPost('sphinx_searchd_port', 'intval', 0), |
||||||
631 | 'sphinxql_searchd_port' => $this->_req->getPost('sphinxql_searchd_port', 'intval', 0), |
||||||
632 | 'sphinx_max_results' => $this->_req->getPost('sphinx_max_results', 'intval', 0) |
||||||
633 | )); |
||||||
634 | } |
||||||
635 | |||||||
636 | /** |
||||||
637 | * Attempt to connect to sphinx using the API methods |
||||||
638 | */ |
||||||
639 | public function connectSphinxApi() |
||||||
640 | { |
||||||
641 | global $txt, $modSettings, $context; |
||||||
642 | |||||||
643 | // This is included with sphinx and not distrubuted with ElkArte |
||||||
644 | if (FileFunctions::instance()->fileExists(SOURCEDIR . '/sphinxapi.php')) |
||||||
645 | { |
||||||
646 | include_once(SOURCEDIR . '/sphinxapi.php'); |
||||||
647 | $server = empty($modSettings['sphinx_searchd_server']) ? 'localhost' : $modSettings['sphinx_searchd_server']; |
||||||
648 | $port = empty($modSettings['sphinx_searchd_port']) ? 9312 : $modSettings['sphinx_searchd_port']; |
||||||
649 | |||||||
650 | $mySphinx = new SphinxClient(); |
||||||
651 | $mySphinx->SetServer($server, (int) $port); |
||||||
652 | $mySphinx->SetLimits(0, 25, 1); |
||||||
653 | |||||||
654 | $index = (empty($modSettings['sphinx_index_prefix']) ? 'elkarte' : $modSettings['sphinx_index_prefix']) . '_index'; |
||||||
655 | $request = $mySphinx->Query('ElkArte', $index); |
||||||
656 | |||||||
657 | if ($request === false) |
||||||
658 | { |
||||||
659 | $context['settings_message'][] = $txt['sphinx_test_connect_failed']; |
||||||
660 | $context['error_type'] = 'serious'; |
||||||
661 | } |
||||||
662 | else |
||||||
663 | { |
||||||
664 | updateSettings(array('sphinx_searchd_server' => $server, 'sphinx_searchd_port' => $port)); |
||||||
665 | $context['settings_message'][] = $txt['sphinx_test_passed']; |
||||||
666 | } |
||||||
667 | |||||||
668 | return; |
||||||
669 | } |
||||||
670 | |||||||
671 | $context['settings_message'][] = $txt['sphinx_test_api_missing']; |
||||||
672 | $context['error_type'] = 'serious'; |
||||||
673 | } |
||||||
674 | |||||||
675 | /** |
||||||
676 | * Attempt to connect to Sphinx using the preferred QL way |
||||||
677 | */ |
||||||
678 | public function connectSphinxQL() |
||||||
679 | { |
||||||
680 | global $txt, $modSettings, $context; |
||||||
681 | |||||||
682 | $server = empty($modSettings['sphinx_searchd_server']) ? 'localhost' : $modSettings['sphinx_searchd_server']; |
||||||
683 | $server = $server === 'localhost' ? '127.0.0.1' : $server; |
||||||
684 | $port = empty($modSettings['sphinxql_searchd_port']) ? '9306' : $modSettings['sphinxql_searchd_port']; |
||||||
685 | |||||||
686 | set_error_handler(static function () { /* ignore errors */ }); |
||||||
687 | try |
||||||
688 | { |
||||||
689 | $result = mysqli_connect($server, '', '', '', (int) $port); |
||||||
0 ignored issues
–
show
The call to
mysqli_connect() has too few arguments starting with socket .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
690 | } |
||||||
691 | catch (Exception) |
||||||
692 | { |
||||||
693 | $result = false; |
||||||
694 | } |
||||||
695 | finally |
||||||
696 | { |
||||||
697 | restore_error_handler(); |
||||||
698 | } |
||||||
699 | |||||||
700 | if ($result === false) |
||||||
701 | { |
||||||
702 | $context['settings_message'][] = $txt['sphinxql_test_connect_failed']; |
||||||
703 | $context['error_type'] = 'serious'; |
||||||
704 | |||||||
705 | return; |
||||||
706 | } |
||||||
707 | |||||||
708 | updateSettings(array('sphinx_searchd_server' => $server, 'sphinxql_searchd_port' => $port)); |
||||||
709 | $context['settings_message'][] = $txt['sphinxql_test_passed']; |
||||||
710 | } |
||||||
711 | } |
||||||
712 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths