1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * This file takes care of all administration of smileys. |
||||
5 | * |
||||
6 | * @package ElkArte Forum |
||||
7 | * @copyright ElkArte Forum contributors |
||||
8 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||
9 | * |
||||
10 | * This file contains code covered by: |
||||
11 | * copyright: 2011 Simple Machines (http://www.simplemachines.org) |
||||
12 | * |
||||
13 | * @version 2.0 dev |
||||
14 | * |
||||
15 | */ |
||||
16 | |||||
17 | namespace ElkArte\AdminController; |
||||
18 | |||||
19 | use BBC\ParserWrapper; |
||||
20 | use ElkArte\AbstractController; |
||||
21 | use ElkArte\Action; |
||||
22 | use ElkArte\Cache\Cache; |
||||
23 | use ElkArte\Errors\Errors; |
||||
0 ignored issues
–
show
|
|||||
24 | use ElkArte\Exceptions\Exception; |
||||
25 | use ElkArte\Helper\FileFunctions; |
||||
26 | use ElkArte\Helper\Util; |
||||
27 | use ElkArte\Languages\Txt; |
||||
28 | use ElkArte\Packages\PackageChmod; |
||||
29 | use ElkArte\Packages\PackageParser; |
||||
30 | use ElkArte\SettingsForm\SettingsForm; |
||||
31 | |||||
32 | /** |
||||
33 | * This class is in charge with administration of smileys and message icons. |
||||
34 | * It handles actions from the Smileys pages in admin panel. |
||||
35 | */ |
||||
36 | class ManageSmileys extends AbstractController |
||||
37 | { |
||||
38 | /** @var array Contextual information about smiley sets. */ |
||||
39 | private $_smiley_context = []; |
||||
40 | |||||
41 | /** @var string[] allowed extensions for smiles */ |
||||
42 | private $_smiley_types = ['jpg', 'gif', 'jpeg', 'png', 'webp', 'svg']; |
||||
43 | |||||
44 | /** |
||||
45 | * This is the dispatcher of smileys administration. |
||||
46 | * |
||||
47 | * @uses ManageSmileys language |
||||
48 | * @uses ManageSmileys template |
||||
49 | * @see AbstractController::action_index() |
||||
50 | */ |
||||
51 | public function action_index() |
||||
52 | { |
||||
53 | global $context, $txt, $modSettings; |
||||
54 | |||||
55 | Txt::load('ManageSmileys'); |
||||
56 | theme()->getTemplates()->load('ManageSmileys'); |
||||
57 | |||||
58 | $subActions = [ |
||||
59 | 'addsmiley' => [$this, 'action_addsmiley', 'permission' => 'manage_smileys'], |
||||
60 | 'editicon' => [$this, 'action_editicon', 'enabled' => !empty($modSettings['messageIcons_enable']), 'permission' => 'manage_smileys'], |
||||
61 | 'editicons' => [$this, 'action_editicon', 'enabled' => !empty($modSettings['messageIcons_enable']), 'permission' => 'manage_smileys'], |
||||
62 | 'editsets' => [$this, 'action_edit', 'permission' => 'admin_forum'], |
||||
63 | 'editsmileys' => [$this, 'action_editsmiley', 'permission' => 'manage_smileys'], |
||||
64 | 'import' => [$this, 'action_edit', 'permission' => 'manage_smileys'], |
||||
65 | 'modifyset' => [$this, 'action_edit', 'permission' => 'manage_smileys'], |
||||
66 | 'modifysmiley' => [$this, 'action_editsmiley', 'permission' => 'manage_smileys'], |
||||
67 | 'setorder' => [$this, 'action_setorder', 'permission' => 'manage_smileys'], |
||||
68 | 'settings' => [$this, 'action_smileySettings_display', 'permission' => 'manage_smileys'], |
||||
69 | 'install' => [$this, 'action_install', 'permission' => 'manage_smileys'] |
||||
70 | ]; |
||||
71 | |||||
72 | // Action controller |
||||
73 | $action = new Action('manage_smileys'); |
||||
74 | |||||
75 | // Set the smiley context. |
||||
76 | $this->_initSmileyContext(); |
||||
77 | |||||
78 | // Load up all the tabs... |
||||
79 | $context[$context['admin_menu_name']]['object']->prepareTabData([ |
||||
80 | 'title' => 'smileys_manage', |
||||
81 | 'help' => 'smileys', |
||||
82 | 'description' => 'smiley_settings_explain', |
||||
83 | 'prefix' => 'smiley', |
||||
84 | 'tabs' => [ |
||||
85 | 'editicons' => [ |
||||
86 | 'description' => $txt['icons_edit_icons_explain'], |
||||
87 | ], |
||||
88 | ], |
||||
89 | ]); |
||||
90 | |||||
91 | // Default the sub-action to 'edit smiley settings'. call integrate_sa_manage_smileys |
||||
92 | $subAction = $action->initialize($subActions, 'editsets'); |
||||
93 | |||||
94 | // Set up the template |
||||
95 | $context['page_title'] = $txt['smileys_manage']; |
||||
96 | $context['sub_action'] = $subAction; |
||||
97 | |||||
98 | // Some settings may not be enabled, disallow these from the tabs as appropriate. |
||||
99 | if (empty($modSettings['messageIcons_enable'])) |
||||
100 | { |
||||
101 | $context[$context['admin_menu_name']]['tab_data']['tabs']['editicons']['disabled'] = true; |
||||
102 | } |
||||
103 | |||||
104 | // Call the right function for this sub-action. |
||||
105 | $action->dispatch($subAction); |
||||
106 | } |
||||
107 | |||||
108 | /** |
||||
109 | * Sets our internal smiley context. |
||||
110 | */ |
||||
111 | private function _initSmileyContext() |
||||
112 | { |
||||
113 | global $modSettings; |
||||
114 | |||||
115 | // Validate the smiley set name. |
||||
116 | $set_paths = explode(',', $modSettings['smiley_sets_known']); |
||||
117 | $set_names = explode("\n", $modSettings['smiley_sets_names']); |
||||
118 | |||||
119 | foreach ($set_paths as $i => $set) |
||||
120 | { |
||||
121 | $this->_smiley_context[$set] = $set_names[$i]; |
||||
122 | } |
||||
123 | } |
||||
124 | |||||
125 | /** |
||||
126 | * Displays and allows modification to smileys settings. |
||||
127 | * |
||||
128 | * @uses show_settings sub template |
||||
129 | */ |
||||
130 | public function action_smileySettings_display() |
||||
131 | { |
||||
132 | global $context; |
||||
133 | |||||
134 | // initialize the form |
||||
135 | $settingsForm = new SettingsForm(SettingsForm::DB_ADAPTER); |
||||
136 | $settingsForm->setConfigVars($this->_settings()); |
||||
137 | |||||
138 | // For the basics of the settings. |
||||
139 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||||
140 | $context['sub_template'] = 'show_settings'; |
||||
141 | |||||
142 | // Finish up the form... |
||||
143 | $context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'save', 'sa' => 'settings']); |
||||
144 | |||||
145 | // Saving the settings? |
||||
146 | if (isset($this->_req->query->save)) |
||||
147 | { |
||||
148 | checkSession(); |
||||
149 | |||||
150 | $this->_req->post->smiley_sets_default = $this->_req->getPost('smiley_sets_default', 'trim|strval', 'default'); |
||||
151 | |||||
152 | call_integration_hook('integrate_save_smiley_settings'); |
||||
153 | |||||
154 | // Save away |
||||
155 | $settingsForm->setConfigValues((array) $this->_req->post); |
||||
156 | $settingsForm->save(); |
||||
157 | |||||
158 | // Flush the cache so the new settings take effect |
||||
159 | $this->clearSmileyCache(); |
||||
160 | |||||
161 | redirectexit('action=admin;area=smileys;sa=settings'); |
||||
162 | } |
||||
163 | |||||
164 | $settingsForm->prepare(); |
||||
165 | } |
||||
166 | |||||
167 | /** |
||||
168 | * Retrieve and return smiley administration settings. |
||||
169 | */ |
||||
170 | private function _settings() |
||||
171 | { |
||||
172 | global $txt, $modSettings, $context; |
||||
173 | |||||
174 | // The directories... |
||||
175 | $context['smileys_dir'] = empty($modSettings['smileys_dir']) ? BOARDDIR . '/smileys' : $modSettings['smileys_dir']; |
||||
176 | $context['smileys_dir_found'] = FileFunctions::instance()->isDir($context['smileys_dir']); |
||||
177 | |||||
178 | // All the settings for the page... |
||||
179 | $config_vars = [ |
||||
180 | ['title', 'settings'], |
||||
181 | ['select', 'smiley_sets_default', $this->_smiley_context], |
||||
182 | ['text', 'smileys_url', 40], |
||||
183 | ['text', 'smileys_dir', 'invalid' => !$context['smileys_dir_found'], 40], |
||||
184 | '', |
||||
185 | // Message icons. |
||||
186 | ['check', 'messageIcons_enable', 'subtext' => $txt['setting_messageIcons_enable_note']], |
||||
187 | // Inline permissions. |
||||
188 | '', |
||||
189 | ['permissions', 'manage_smileys'], |
||||
190 | ]; |
||||
191 | |||||
192 | call_integration_hook('integrate_modify_smiley_settings', [&$config_vars]); |
||||
193 | 2 | ||||
194 | return $config_vars; |
||||
195 | 2 | } |
|||
196 | |||||
197 | /** |
||||
198 | 2 | * Clear the cache to avoid changes not immediately appearing |
|||
199 | 2 | */ |
|||
200 | protected function clearSmileyCache() |
||||
201 | { |
||||
202 | Cache::instance()->remove('parsing_smileys'); |
||||
203 | 2 | Cache::instance()->remove('posting_smileys'); |
|||
204 | |||||
205 | $this->createCustomTagsFile(); |
||||
206 | 2 | } |
|||
207 | 2 | ||||
208 | /** |
||||
209 | 2 | * For any :emoji: codes in the current set, that have an image in the smile set directory, |
|||
210 | * write the information to custom_tags.js so the emoji selector reflects the new or replacement image |
||||
211 | 2 | * |
|||
212 | 2 | * @return void |
|||
213 | */ |
||||
214 | 2 | protected function createCustomTagsFile() |
|||
215 | { |
||||
216 | global $context, $settings; |
||||
217 | 2 | ||||
218 | $fileFunc = FileFunctions::instance(); |
||||
219 | 2 | $custom = []; |
|||
220 | $smileys = list_getSmileys(0, 2000, 'code'); |
||||
221 | foreach ($smileys as $smile) |
||||
222 | { |
||||
223 | if (preg_match('~:[\w-]{2,}:~u', $smile['code'], $match) === 1) |
||||
224 | { |
||||
225 | // If the emoji IS an image file in the currently selected set |
||||
226 | $filename = $context['smiley_dir'] . $smile['filename'] . '.' . $context['smiley_extension']; |
||||
227 | if ($fileFunc->fileExists($filename)) |
||||
228 | { |
||||
229 | $custom[] = [ |
||||
230 | 'name' => trim($smile['code'], ':'), |
||||
231 | 2 | 'key' => $smile['filename'], |
|||
232 | 'type' => $context['smiley_extension'] |
||||
233 | 2 | ]; |
|||
234 | } |
||||
235 | } |
||||
236 | } |
||||
237 | |||||
238 | // Whatever we have, save it |
||||
239 | $header = '/*! |
||||
240 | * Auto-generated file, DO NOT manually edit |
||||
241 | * |
||||
242 | * @package ElkArte Forum |
||||
243 | * @copyright ElkArte Forum contributors |
||||
244 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||
245 | * |
||||
246 | */ |
||||
247 | |||||
248 | let custom = [ |
||||
249 | '; |
||||
250 | foreach ($custom as $emoji) |
||||
251 | { |
||||
252 | $header .= "\t" . '{name: \'' . $emoji['name'] . '\', key: \'' . $emoji['key'] . '\', type: \'' . $emoji['type'] . "'},\n"; |
||||
253 | } |
||||
254 | |||||
255 | $header .= '];'; |
||||
256 | |||||
257 | file_put_contents($settings['default_theme_dir'] . '/scripts/custom_tags.js', $header); |
||||
258 | } |
||||
259 | |||||
260 | /** |
||||
261 | * Return the smiley settings for use in admin search |
||||
262 | */ |
||||
263 | public function settings_search() |
||||
264 | { |
||||
265 | return $this->_settings(); |
||||
266 | } |
||||
267 | |||||
268 | /** |
||||
269 | * List, add, remove, modify smileys sets. |
||||
270 | * |
||||
271 | * @event integrate_list_smiley_set_list |
||||
272 | */ |
||||
273 | public function action_edit() |
||||
274 | { |
||||
275 | global $modSettings, $context, $txt; |
||||
276 | |||||
277 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||||
278 | |||||
279 | // Set the right tab to be selected. |
||||
280 | $context[$context['admin_menu_name']]['current_subsection'] = 'editsets'; |
||||
281 | $context['sub_template'] = $context['sub_action']; |
||||
282 | |||||
283 | // Have they submitted a form? |
||||
284 | $this->_subActionSubmit(); |
||||
285 | |||||
286 | // Load all available smileysets... |
||||
287 | $this->loadSmileySets(); |
||||
288 | |||||
289 | // Importing any smileys from an existing set? |
||||
290 | $this->_subActionImport(); |
||||
291 | |||||
292 | // If we're modifying or adding a smileyset, some context info needs to be set. |
||||
293 | $this->_subActionModifySet(); |
||||
294 | |||||
295 | // This is our save haven. |
||||
296 | createToken('admin-mss', 'request'); |
||||
297 | |||||
298 | $listOptions = [ |
||||
299 | 'id' => 'smiley_set_list', |
||||
300 | 'title' => $txt['smiley_sets'], |
||||
301 | 'no_items_label' => $txt['smiley_sets_none'], |
||||
302 | 'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editsets']), |
||||
303 | 'default_sort_col' => 'name', |
||||
304 | 'get_items' => [ |
||||
305 | 'function' => 'list_getSmileySets', |
||||
306 | ], |
||||
307 | 'get_count' => [ |
||||
308 | 'function' => 'list_getNumSmileySets', |
||||
309 | ], |
||||
310 | 'columns' => [ |
||||
311 | 'default' => [ |
||||
312 | 'header' => [ |
||||
313 | 'value' => $txt['smiley_sets_default'], |
||||
314 | 'class' => 'centertext', |
||||
315 | ], |
||||
316 | 'data' => [ |
||||
317 | 'function' => static fn($rowData) => $rowData['selected'] ? '<i class="icon i-check"></i>' : '', |
||||
318 | 'class' => 'centertext', |
||||
319 | ], |
||||
320 | 'sort' => [ |
||||
321 | 'default' => 'selected', |
||||
322 | 'reverse' => 'selected DESC', |
||||
323 | ], |
||||
324 | ], |
||||
325 | 'name' => [ |
||||
326 | 'header' => [ |
||||
327 | 'value' => $txt['smiley_sets_name'], |
||||
328 | ], |
||||
329 | 'data' => [ |
||||
330 | 'db_htmlsafe' => 'name', |
||||
331 | ], |
||||
332 | 'sort' => [ |
||||
333 | 'default' => 'name', |
||||
334 | 'reverse' => 'name DESC', |
||||
335 | ], |
||||
336 | ], |
||||
337 | 'ext' => [ |
||||
338 | 'header' => [ |
||||
339 | 'value' => $txt['smiley_sets_ext'], |
||||
340 | ], |
||||
341 | 'data' => [ |
||||
342 | 'db_htmlsafe' => 'ext', |
||||
343 | ], |
||||
344 | 'sort' => [ |
||||
345 | 'default' => 'ext', |
||||
346 | 'reverse' => 'ext DESC', |
||||
347 | ], |
||||
348 | ], |
||||
349 | 'url' => [ |
||||
350 | 'header' => [ |
||||
351 | 'value' => $txt['smiley_sets_url'], |
||||
352 | ], |
||||
353 | 'data' => [ |
||||
354 | 'sprintf' => [ |
||||
355 | 'format' => $modSettings['smileys_url'] . '/<strong>%1$s</strong>/...', |
||||
356 | 'params' => [ |
||||
357 | 'path' => true, |
||||
358 | ], |
||||
359 | ], |
||||
360 | ], |
||||
361 | 'sort' => [ |
||||
362 | 'default' => 'path', |
||||
363 | 'reverse' => 'path DESC', |
||||
364 | ], |
||||
365 | ], |
||||
366 | 'modify' => [ |
||||
367 | 'header' => [ |
||||
368 | 'value' => $txt['smiley_set_modify'], |
||||
369 | ], |
||||
370 | 'data' => [ |
||||
371 | 'sprintf' => [ |
||||
372 | 'format' => '<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'modifyset', 'set' => '']) . '%1$d">' . $txt['smiley_set_modify'] . '</a>', |
||||
373 | 'params' => [ |
||||
374 | 'id' => true, |
||||
375 | ], |
||||
376 | ], |
||||
377 | ], |
||||
378 | ], |
||||
379 | 'check' => [ |
||||
380 | 'header' => [ |
||||
381 | 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />', |
||||
382 | 'class' => 'centertext', |
||||
383 | ], |
||||
384 | 'data' => [ |
||||
385 | 'function' => static fn($rowData) => $rowData['id'] == 0 ? '' : sprintf('<input type="checkbox" name="smiley_set[%1$d]" class="input_check" />', $rowData['id']), |
||||
386 | 'class' => 'centertext', |
||||
387 | ], |
||||
388 | ], |
||||
389 | ], |
||||
390 | 'form' => [ |
||||
391 | 'href' => getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editsets']), |
||||
392 | 'token' => 'admin-mss', |
||||
393 | ], |
||||
394 | 'additional_rows' => [ |
||||
395 | [ |
||||
396 | 'position' => 'below_table_data', |
||||
397 | 'class' => 'submitbutton', |
||||
398 | 'value' => ' |
||||
399 | <a class="linkbutton" href="' . getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'modifyset']) . '">' . $txt['smiley_sets_add'] . '</a> |
||||
400 | <input type="submit" name="delete_set" value="' . $txt['smiley_sets_delete'] . '" onclick="return confirm(\'' . $txt['smiley_sets_confirm'] . '\');" />', |
||||
401 | ], |
||||
402 | ], |
||||
403 | ]; |
||||
404 | |||||
405 | createList($listOptions); |
||||
406 | } |
||||
407 | |||||
408 | /** |
||||
409 | * Submitted a smiley form, determine what actions are required. |
||||
410 | * |
||||
411 | * - Handle deleting of a smiley set |
||||
412 | * - Adding a new set |
||||
413 | * - Modifying an existing set, such as setting it as the default |
||||
414 | */ |
||||
415 | private function _subActionSubmit() |
||||
416 | { |
||||
417 | global $context, $modSettings; |
||||
418 | |||||
419 | if (isset($this->_req->post->smiley_save) || isset($this->_req->post->delete_set)) |
||||
420 | { |
||||
421 | // Security first |
||||
422 | checkSession(); |
||||
423 | validateToken('admin-mss', 'request'); |
||||
424 | |||||
425 | // Delete selected smiley sets. |
||||
426 | if (!empty($this->_req->post->delete_set) && !empty($this->_req->post->smiley_set)) |
||||
427 | { |
||||
428 | $set_paths = explode(',', $modSettings['smiley_sets_known']); |
||||
429 | $set_names = explode("\n", $modSettings['smiley_sets_names']); |
||||
430 | $set_extensions = explode(',', $modSettings['smiley_sets_extensions']); |
||||
431 | foreach ($this->_req->post->smiley_set as $id => $val) |
||||
432 | { |
||||
433 | if (!isset($set_paths[$id], $set_names[$id])) |
||||
434 | { |
||||
435 | continue; |
||||
436 | } |
||||
437 | if (empty($id)) |
||||
438 | { |
||||
439 | continue; |
||||
440 | } |
||||
441 | unset($set_paths[$id], $set_names[$id], $set_extensions[$id]); |
||||
442 | } |
||||
443 | |||||
444 | // Update the modSettings with the new values |
||||
445 | updateSettings([ |
||||
446 | 'smiley_sets_known' => implode(',', $set_paths), |
||||
447 | 'smiley_sets_names' => implode("\n", $set_names), |
||||
448 | 'smiley_sets_extensions' => implode(',', $set_extensions), |
||||
449 | 'smiley_sets_default' => in_array($modSettings['smiley_sets_default'], $set_paths, true) ? $modSettings['smiley_sets_default'] : $set_paths[0], |
||||
450 | ]); |
||||
451 | } |
||||
452 | // Add a new smiley set. |
||||
453 | elseif (!empty($this->_req->post->add)) |
||||
454 | { |
||||
455 | $context['sub_action'] = 'modifyset'; |
||||
456 | } |
||||
457 | // Create or modify a smiley set. |
||||
458 | elseif (isset($this->_req->post->set)) |
||||
459 | { |
||||
460 | $set = $this->_req->getPost('set', 'intval', 0); |
||||
461 | $set_paths = explode(',', $modSettings['smiley_sets_known']); |
||||
462 | $set_names = explode("\n", $modSettings['smiley_sets_names']); |
||||
463 | |||||
464 | // Create a new smiley set. |
||||
465 | if ($set === -1 && isset($this->_req->post->smiley_sets_path)) |
||||
466 | { |
||||
467 | if (in_array($this->_req->post->smiley_sets_path, $set_paths, true)) |
||||
468 | { |
||||
469 | throw new Exception('smiley_set_already_exists', false); |
||||
470 | } |
||||
471 | |||||
472 | updateSettings([ |
||||
473 | 'smiley_sets_known' => $modSettings['smiley_sets_known'] . ',' . $this->_req->post->smiley_sets_path, |
||||
474 | 'smiley_sets_names' => $modSettings['smiley_sets_names'] . "\n" . $this->_req->post->smiley_sets_name, |
||||
475 | 'smiley_sets_extensions' => $modSettings['smiley_sets_extensions'] . ',' . $this->_req->post->smiley_sets_ext, |
||||
476 | 'smiley_sets_default' => empty($this->_req->post->smiley_sets_default) ? $modSettings['smiley_sets_default'] : $this->_req->post->smiley_sets_path, |
||||
477 | ]); |
||||
478 | } |
||||
479 | // Modify an existing smiley set. |
||||
480 | else |
||||
481 | { |
||||
482 | // Make sure the smiley set exists. |
||||
483 | if (!isset($set_paths[$set], $set_names[$set])) |
||||
484 | { |
||||
485 | throw new Exception('smiley_set_not_found', false); |
||||
486 | } |
||||
487 | |||||
488 | // Make sure the path is not yet used by another smileyset. |
||||
489 | if (in_array($this->_req->post->smiley_sets_path, $set_paths, true) && $this->_req->post->smiley_sets_path !== $set_paths[$set]) |
||||
490 | { |
||||
491 | throw new Exception('smiley_set_path_already_used', false); |
||||
492 | } |
||||
493 | |||||
494 | $set_paths[$set] = $this->_req->post->smiley_sets_path; |
||||
495 | $set_names[$set] = $this->_req->post->smiley_sets_name; |
||||
496 | updateSettings([ |
||||
497 | 'smiley_sets_known' => implode(',', $set_paths), |
||||
498 | 'smiley_sets_names' => implode("\n", $set_names), |
||||
499 | 'smiley_sets_default' => empty($this->_req->post->smiley_sets_default) ? $modSettings['smiley_sets_default'] : $this->_req->post->smiley_sets_path |
||||
500 | ]); |
||||
501 | } |
||||
502 | |||||
503 | // The user might have checked to also import smileys. |
||||
504 | if (!empty($this->_req->post->smiley_sets_import)) |
||||
505 | { |
||||
506 | $this->importSmileys($this->_req->post->smiley_sets_path); |
||||
507 | } |
||||
508 | } |
||||
509 | |||||
510 | // Reset $context as the default set may have changed |
||||
511 | loadSmileyEmojiData(); |
||||
512 | |||||
513 | // No matter what, reset the cache |
||||
514 | $this->clearSmileyCache(); |
||||
515 | } |
||||
516 | } |
||||
517 | |||||
518 | /** |
||||
519 | * A function to import new smileys from an existing directory into the database. |
||||
520 | * |
||||
521 | * @param string $smileyPath |
||||
522 | * |
||||
523 | * @throws \ElkArte\Exceptions\Exception smiley_set_unable_to_import |
||||
524 | */ |
||||
525 | public function importSmileys($smileyPath) |
||||
526 | { |
||||
527 | global $modSettings; |
||||
528 | |||||
529 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||||
530 | $fileFunc = FileFunctions::instance(); |
||||
531 | |||||
532 | if (empty($modSettings['smileys_dir']) || !$fileFunc->isDir($modSettings['smileys_dir'] . '/' . $smileyPath)) |
||||
533 | { |
||||
534 | throw new Exception('smiley_set_unable_to_import', false); |
||||
535 | } |
||||
536 | |||||
537 | // Fetch all smileys in the directory |
||||
538 | $foundSmileys = $fileFunc->listTree($modSettings['smileys_dir'] . '/' . $smileyPath); |
||||
539 | |||||
540 | // Exclude the smileys that are already in the database. |
||||
541 | $smileys = $this->_getUniqueSmileys($foundSmileys); |
||||
542 | |||||
543 | $smiley_order = getMaxSmileyOrder(); |
||||
544 | |||||
545 | $new_smileys = []; |
||||
546 | foreach ($smileys as $smiley) |
||||
547 | { |
||||
548 | if (strlen($smiley) <= 48) |
||||
549 | { |
||||
550 | $new_smileys[] = [':' . strtok($smiley, '.') . ':', $smiley, strtok($smiley, '.'), 0, ++$smiley_order]; |
||||
551 | } |
||||
552 | } |
||||
553 | |||||
554 | if (!empty($new_smileys)) |
||||
555 | { |
||||
556 | addSmiley($new_smileys); |
||||
557 | |||||
558 | $this->clearSmileyCache(); |
||||
559 | } |
||||
560 | } |
||||
561 | |||||
562 | /** |
||||
563 | * Returns smile names in a directory that *do not* exist in the DB |
||||
564 | * |
||||
565 | * @param array $foundSmileys array as returned from file functions listTree |
||||
566 | * @return array |
||||
567 | * @throws \ElkArte\Exceptions\Exception |
||||
568 | */ |
||||
569 | private function _getUniqueSmileys($foundSmileys) |
||||
570 | { |
||||
571 | global $context; |
||||
572 | |||||
573 | $smileys = []; |
||||
574 | foreach ($foundSmileys as $smile) |
||||
575 | { |
||||
576 | if (in_array($smile['filename'], ['.', '..', '.htaccess', 'index.php'], true)) |
||||
577 | { |
||||
578 | continue; |
||||
579 | } |
||||
580 | |||||
581 | // Get the name and extension |
||||
582 | $filename = pathinfo($smile['filename'], PATHINFO_FILENAME); |
||||
583 | if (in_array(strtolower(pathinfo($smile['filename'], PATHINFO_EXTENSION)), $this->_smiley_types, true)) |
||||
0 ignored issues
–
show
It seems like
pathinfo($smile['filenam...ler\PATHINFO_EXTENSION) can also be of type array ; however, parameter $string of strtolower() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
584 | { |
||||
585 | $smileys[strtolower($filename)] = $filename; |
||||
586 | } |
||||
587 | } |
||||
588 | |||||
589 | if (empty($smileys)) |
||||
590 | { |
||||
591 | throw new Exception('smiley_set_dir_not_found', false, [$context['current_set']['name']]); |
||||
592 | } |
||||
593 | |||||
594 | // Exclude the smileys that are already in the database. |
||||
595 | $duplicates = smileyExists($smileys); |
||||
596 | foreach ($duplicates as $duplicate) |
||||
597 | { |
||||
598 | if (isset($smileys[strtolower($duplicate)])) |
||||
599 | { |
||||
600 | unset($smileys[strtolower($duplicate)]); |
||||
601 | } |
||||
602 | } |
||||
603 | |||||
604 | return $smileys; |
||||
605 | } |
||||
606 | |||||
607 | /** |
||||
608 | * Importing smileys from an existing smiley set |
||||
609 | */ |
||||
610 | private function _subActionImport() |
||||
611 | { |
||||
612 | global $context; |
||||
613 | |||||
614 | // Importing any smileys from an existing set? |
||||
615 | if ($context['sub_action'] === 'import') |
||||
616 | { |
||||
617 | checkSession('get'); |
||||
618 | validateToken('admin-mss', 'request'); |
||||
619 | |||||
620 | $set = (int) $this->_req->query->set; |
||||
621 | |||||
622 | // Sanity check - then import. |
||||
623 | if (isset($context['smiley_sets'][$set])) |
||||
624 | { |
||||
625 | $this->importSmileys(un_htmlspecialchars($context['smiley_sets'][$set]['path'])); |
||||
626 | } |
||||
627 | |||||
628 | // Force the process to continue. |
||||
629 | $context['sub_action'] = 'modifyset'; |
||||
630 | $context['sub_template'] = 'modifyset'; |
||||
631 | } |
||||
632 | } |
||||
633 | |||||
634 | /** |
||||
635 | * If we're modifying or adding a smileyset, or if we imported from another |
||||
636 | * set, then some context info needs to be set. |
||||
637 | * |
||||
638 | * @throws \ElkArte\Exceptions\Exception in superfluity |
||||
639 | */ |
||||
640 | private function _subActionModifySet() |
||||
641 | { |
||||
642 | global $context, $txt, $modSettings; |
||||
643 | |||||
644 | $fileFunc = FileFunctions::instance(); |
||||
645 | |||||
646 | if ($context['sub_action'] === 'modifyset') |
||||
647 | { |
||||
648 | $set = $this->_req->getQuery('set', 'intval', -1); |
||||
649 | if ($set === -1 || !isset($context['smiley_sets'][$set])) |
||||
650 | { |
||||
651 | $context['current_set'] = [ |
||||
652 | 'id' => '-1', |
||||
653 | 'path' => '', |
||||
654 | 'name' => '', |
||||
655 | 'ext' => '', |
||||
656 | 'selected' => false, |
||||
657 | 'is_new' => true, |
||||
658 | ]; |
||||
659 | } |
||||
660 | else |
||||
661 | { |
||||
662 | $context['current_set'] = &$context['smiley_sets'][$set]; |
||||
663 | $context['current_set']['is_new'] = false; |
||||
664 | $context['smileys_dir'] = empty($modSettings['smileys_dir']) ? BOARDDIR . '/smileys' : $modSettings['smileys_dir']; |
||||
665 | $context['selected_set'] = $modSettings['smiley_sets_default']; |
||||
666 | |||||
667 | // Calculate whether there are any smileys in the directory that can be imported. |
||||
668 | if (!empty($modSettings['smileys_dir']) |
||||
669 | && $fileFunc->isDir($modSettings['smileys_dir'] . '/' . $context['current_set']['path'])) |
||||
670 | { |
||||
671 | $foundSmileys = $fileFunc->listTree($modSettings['smileys_dir'] . '/' . $context['current_set']['path']); |
||||
672 | $smileys = $this->_getUniqueSmileys($foundSmileys); |
||||
673 | $context['current_set']['can_import'] = count($smileys); |
||||
674 | |||||
675 | // Setup this string to look nice. |
||||
676 | $txt['smiley_set_import_multiple'] = sprintf($txt['smiley_set_import_multiple'], $context['current_set']['can_import']); |
||||
677 | } |
||||
678 | } |
||||
679 | |||||
680 | // Retrieve all potential smiley set directories. |
||||
681 | $context['smiley_set_dirs'] = []; |
||||
682 | if (!empty($modSettings['smileys_dir']) && $fileFunc->isDir($modSettings['smileys_dir'])) |
||||
683 | { |
||||
684 | // Do not include our emoji directories |
||||
685 | $disallow = ['.', '..', 'open-moji', 'tw-emoji', 'noto-emoji']; |
||||
686 | |||||
687 | $dir = dir($modSettings['smileys_dir']); |
||||
688 | while (($entry = $dir->read()) !== false) |
||||
689 | { |
||||
690 | if (!in_array($entry, $disallow, true) |
||||
691 | && $fileFunc->isDir($modSettings['smileys_dir'] . '/' . $entry)) |
||||
692 | { |
||||
693 | $context['smiley_set_dirs'][] = [ |
||||
694 | 'id' => $entry, |
||||
695 | 'path' => $modSettings['smileys_dir'] . '/' . $entry, |
||||
696 | 'selectable' => $entry === $context['current_set']['path'] || !in_array($entry, explode(',', $modSettings['smiley_sets_known']), true), |
||||
697 | 'current' => $entry === $context['current_set']['path'], |
||||
698 | ]; |
||||
699 | } |
||||
700 | } |
||||
701 | |||||
702 | $dir->close(); |
||||
703 | } |
||||
704 | } |
||||
705 | } |
||||
706 | |||||
707 | /** |
||||
708 | * Add a smiley, that's right. |
||||
709 | */ |
||||
710 | public function action_addsmiley() |
||||
711 | { |
||||
712 | global $modSettings, $context, $txt; |
||||
713 | |||||
714 | $fileFunc = FileFunctions::instance(); |
||||
715 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||||
716 | |||||
717 | // Get a list of all known smiley sets. |
||||
718 | $context['smileys_dir'] = empty($modSettings['smileys_dir']) ? BOARDDIR . '/smileys' : $modSettings['smileys_dir']; |
||||
719 | $context['sub_template'] = 'addsmiley'; |
||||
720 | |||||
721 | $this->loadSmileySets(); |
||||
722 | |||||
723 | // Submitting a form? |
||||
724 | if (isset($this->_req->post->{$context['session_var']}, $this->_req->post->smiley_code)) |
||||
725 | { |
||||
726 | checkSession(); |
||||
727 | |||||
728 | // Some useful arrays... types we allow - and ports we don't! |
||||
729 | $disabledFiles = ['con', 'com1', 'com2', 'com3', 'com4', 'prn', 'aux', 'lpt1', '.htaccess', 'index.php']; |
||||
730 | |||||
731 | $this->_req->post->smiley_code = $this->_req->getPost('smiley_code', '\\ElkArte\\Helper\\Util::htmltrim', ''); |
||||
732 | $this->_req->post->smiley_filename = $this->_req->getPost('smiley_filename', '\\ElkArte\\Helper\\Util::htmltrim', ''); |
||||
733 | $this->_req->post->smiley_location = $this->_req->getPost('smiley_location', 'intval', 0); |
||||
734 | $this->_req->post->smiley_location = min(max($this->_req->post->smiley_location, 0), 2); |
||||
735 | |||||
736 | // Make sure some code was entered. |
||||
737 | if (empty($this->_req->post->smiley_code)) |
||||
738 | { |
||||
739 | throw new Exception('smiley_has_no_code', false); |
||||
740 | } |
||||
741 | |||||
742 | // Check whether the new code has duplicates. It should be unique. |
||||
743 | if (validateDuplicateSmiley($this->_req->post->smiley_code)) |
||||
744 | { |
||||
745 | throw new Exception('smiley_not_unique', false); |
||||
746 | } |
||||
747 | |||||
748 | // If we are uploading - check all the smiley sets are writable! |
||||
749 | if ($this->_req->post->method !== 'existing') |
||||
750 | { |
||||
751 | $writeErrors = []; |
||||
752 | foreach ($context['smiley_sets'] as $set) |
||||
753 | { |
||||
754 | if (!$fileFunc->isWritable($context['smileys_dir'] . '/' . un_htmlspecialchars($set['path']))) |
||||
755 | { |
||||
756 | $writeErrors[] = $set['path']; |
||||
757 | } |
||||
758 | } |
||||
759 | |||||
760 | if (!empty($writeErrors)) |
||||
761 | { |
||||
762 | throw new Exception('smileys_upload_error_notwritable', true, [implode(', ', $writeErrors)]); |
||||
763 | } |
||||
764 | } |
||||
765 | |||||
766 | // Uploading just one smiley for all of them? |
||||
767 | if (isset($this->_req->post->sameall, $_FILES['uploadSmiley']['name']) && $_FILES['uploadSmiley']['name'] != '') |
||||
768 | { |
||||
769 | if (!is_uploaded_file($_FILES['uploadSmiley']['tmp_name']) |
||||
770 | || (ini_get('open_basedir') === '' && !$fileFunc->fileExists($_FILES['uploadSmiley']['tmp_name']))) |
||||
771 | { |
||||
772 | throw new Exception('smileys_upload_error'); |
||||
773 | } |
||||
774 | |||||
775 | // Sorry, no spaces, dots, or anything else but letters allowed. |
||||
776 | $_FILES['uploadSmiley']['name'] = preg_replace(['/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'], ['_', '.', ''], $_FILES['uploadSmiley']['name']); |
||||
777 | |||||
778 | // We only allow image files - it's THAT simple - no messing around here... |
||||
779 | if (!in_array(strtolower(pathinfo($_FILES['uploadSmiley']['name'], PATHINFO_EXTENSION)), $this->_smiley_types)) |
||||
780 | { |
||||
781 | throw new Exception('smileys_upload_error_types', false, [implode(', ', $this->_smiley_types)]); |
||||
782 | } |
||||
783 | |||||
784 | // We only need the filename... |
||||
785 | $destName = basename($_FILES['uploadSmiley']['name']); |
||||
786 | |||||
787 | // Make sure they aren't trying to upload a nasty file - for their own good here! |
||||
788 | if (in_array(strtolower($destName), $disabledFiles)) |
||||
789 | { |
||||
790 | throw new Exception('smileys_upload_error_illegal'); |
||||
791 | } |
||||
792 | |||||
793 | // Check if the file already exists... and if not move it to EVERY smiley set directory. |
||||
794 | $i = 0; |
||||
795 | |||||
796 | // Keep going until we find a set the file doesn't exist in. (or maybe it exists in all of them?) |
||||
797 | while (isset($context['smiley_sets'][$i]) |
||||
798 | && $fileFunc->fileExists($context['smileys_dir'] . '/' . un_htmlspecialchars($context['smiley_sets'][$i]['path']) . '/' . $destName)) |
||||
799 | { |
||||
800 | $i++; |
||||
801 | } |
||||
802 | |||||
803 | // Okay, we're going to put the smiley right here, since it's not there yet! |
||||
804 | if (isset($context['smiley_sets'][$i]['path'])) |
||||
805 | { |
||||
806 | $smileyLocation = $context['smileys_dir'] . '/' . un_htmlspecialchars($context['smiley_sets'][$i]['path']) . '/' . $destName; |
||||
807 | move_uploaded_file($_FILES['uploadSmiley']['tmp_name'], $smileyLocation); |
||||
808 | $fileFunc->chmod($smileyLocation); |
||||
809 | |||||
810 | // Now, we want to move it from there to all the other sets. |
||||
811 | for ($n = count($context['smiley_sets']); $i < $n; $i++) |
||||
812 | { |
||||
813 | $currentPath = $context['smileys_dir'] . '/' . un_htmlspecialchars($context['smiley_sets'][$i]['path']) . '/' . $destName; |
||||
814 | |||||
815 | // The file is already there! Don't overwrite it! |
||||
816 | if ($fileFunc->fileExists($currentPath)) |
||||
817 | { |
||||
818 | continue; |
||||
819 | } |
||||
820 | |||||
821 | // Okay, so copy the first one we made to here. |
||||
822 | copy($smileyLocation, $currentPath); |
||||
823 | $fileFunc->chmod($currentPath); |
||||
824 | } |
||||
825 | } |
||||
826 | |||||
827 | // Finally, make sure it's saved correctly! |
||||
828 | $this->_req->post->smiley_filename = $destName; |
||||
829 | } |
||||
830 | // What about uploading several files? |
||||
831 | elseif ($this->_req->post->method !== 'existing') |
||||
832 | { |
||||
833 | $newName = ''; |
||||
834 | foreach ($_FILES as $file) |
||||
835 | { |
||||
836 | if ($file['name'] === '') |
||||
837 | { |
||||
838 | throw new Exception('smileys_upload_error_blank', false); |
||||
839 | } |
||||
840 | |||||
841 | if (empty($newName)) |
||||
842 | { |
||||
843 | $newName = basename($file['name']); |
||||
844 | } |
||||
845 | elseif (basename($file['name']) !== $newName) |
||||
846 | { |
||||
847 | throw new Exception('smileys_upload_error_name', false); |
||||
848 | } |
||||
849 | } |
||||
850 | |||||
851 | foreach ($context['smiley_sets'] as $set) |
||||
852 | { |
||||
853 | $set['name'] = un_htmlspecialchars($set['name']); |
||||
854 | $set['path'] = un_htmlspecialchars($set['path']); |
||||
855 | |||||
856 | if (!isset($_FILES['individual_' . $set['name']]['name']) || $_FILES['individual_' . $set['name']]['name'] === '') |
||||
857 | { |
||||
858 | continue; |
||||
859 | } |
||||
860 | |||||
861 | // Got one... |
||||
862 | if (!is_uploaded_file($_FILES['individual_' . $set['name']]['tmp_name']) || (ini_get('open_basedir') === '' |
||||
863 | && !$fileFunc->fileExists($_FILES['individual_' . $set['name']]['tmp_name']))) |
||||
864 | { |
||||
865 | throw new Exception('smileys_upload_error'); |
||||
866 | } |
||||
867 | |||||
868 | // Sorry, no spaces, dots, or anything else but letters allowed. |
||||
869 | $_FILES['individual_' . $set['name']]['name'] = preg_replace(['/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'], ['_', '.', ''], $_FILES['individual_' . $set['name']]['name']); |
||||
870 | |||||
871 | // We only allow image files - it's THAT simple - no messing around here... |
||||
872 | if (!in_array(strtolower(pathinfo($_FILES['individual_' . $set['name']]['name'], PATHINFO_EXTENSION)), $this->_smiley_types)) |
||||
873 | { |
||||
874 | throw new Exception('smileys_upload_error_types', false, [implode(', ', $this->_smiley_types)]); |
||||
875 | } |
||||
876 | |||||
877 | // We only need the filename... |
||||
878 | $destName = basename($_FILES['individual_' . $set['name']]['name']); |
||||
879 | |||||
880 | // Make sure they aren't trying to upload a nasty file - for their own good here! |
||||
881 | if (in_array(strtolower($destName), $disabledFiles)) |
||||
882 | { |
||||
883 | throw new Exception('smileys_upload_error_illegal'); |
||||
884 | } |
||||
885 | |||||
886 | // If the file exists - ignore it. |
||||
887 | $smileyLocation = $context['smileys_dir'] . '/' . $set['path'] . '/' . $destName; |
||||
888 | if ($fileFunc->fileExists($smileyLocation)) |
||||
889 | { |
||||
890 | continue; |
||||
891 | } |
||||
892 | |||||
893 | // Finally - move the image! |
||||
894 | move_uploaded_file($_FILES['individual_' . $set['name']]['tmp_name'], $smileyLocation); |
||||
895 | $fileFunc->chmod($smileyLocation); |
||||
896 | |||||
897 | // Should always be saved correctly! |
||||
898 | $this->_req->post->smiley_filename = $destName; |
||||
899 | } |
||||
900 | } |
||||
901 | |||||
902 | // Also make sure a filename was given. |
||||
903 | if (empty($this->_req->post->smiley_filename)) |
||||
904 | { |
||||
905 | throw new Exception('smiley_has_no_filename', false); |
||||
906 | } |
||||
907 | |||||
908 | // Find the position on the right. |
||||
909 | $smiley_order = '0'; |
||||
910 | if ($this->_req->post->smiley_location != 1) |
||||
911 | { |
||||
912 | $this->_req->post->smiley_location = (int) $this->_req->post->smiley_location; |
||||
913 | $smiley_order = nextSmileyLocation($this->_req->post->smiley_location); |
||||
914 | |||||
915 | if (empty($smiley_order)) |
||||
916 | { |
||||
917 | $smiley_order = '0'; |
||||
918 | } |
||||
919 | } |
||||
920 | |||||
921 | $param = [ |
||||
922 | $this->_req->post->smiley_code, |
||||
923 | $this->_req->post->smiley_filename, |
||||
924 | $this->_req->post->smiley_description, |
||||
925 | (int) $this->_req->post->smiley_location, |
||||
926 | $smiley_order, |
||||
927 | ]; |
||||
928 | addSmiley($param); |
||||
929 | |||||
930 | $this->clearSmileyCache(); |
||||
931 | |||||
932 | // No errors? Out of here! |
||||
933 | redirectexit('action=admin;area=smileys;sa=editsmileys'); |
||||
934 | } |
||||
935 | |||||
936 | $context['selected_set'] = $modSettings['smiley_sets_default']; |
||||
937 | |||||
938 | // Get all possible filenames for the smileys. |
||||
939 | $context['filenames'] = $this->getAllPossibleFilenamesForTheSmileys($context['smiley_sets']); |
||||
940 | |||||
941 | // Create a new smiley from scratch. |
||||
942 | $context['filenames'] = array_values($context['filenames']); |
||||
943 | $context['current_smiley'] = [ |
||||
944 | 'id' => 0, |
||||
945 | 'code' => '', |
||||
946 | 'filename' => $context['filenames'][0]['id'], |
||||
947 | 'description' => $txt['smileys_default_description'], |
||||
948 | 'location' => 0, |
||||
949 | 'is_new' => true, |
||||
950 | ]; |
||||
951 | } |
||||
952 | |||||
953 | /** |
||||
954 | * Add, remove, edit smileys. |
||||
955 | * |
||||
956 | * @event integrate_list_smiley_list |
||||
957 | */ |
||||
958 | public function action_editsmiley() |
||||
959 | { |
||||
960 | global $modSettings, $context, $txt; |
||||
961 | |||||
962 | $fileFunc = FileFunctions::instance(); |
||||
963 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||||
964 | |||||
965 | // Force the correct tab to be displayed. |
||||
966 | $context[$context['admin_menu_name']]['current_subsection'] = 'editsmileys'; |
||||
967 | $context['sub_template'] = $context['sub_action']; |
||||
968 | |||||
969 | // Submitting a form? |
||||
970 | if (isset($this->_req->post->smiley_save) || isset($this->_req->post->smiley_action)) |
||||
971 | { |
||||
972 | checkSession(); |
||||
973 | |||||
974 | // Changing the selected smileys? |
||||
975 | if (isset($this->_req->post->smiley_action) && !empty($this->_req->post->checked_smileys)) |
||||
976 | { |
||||
977 | foreach ($this->_req->post->checked_smileys as $id => $smiley_id) |
||||
978 | { |
||||
979 | $this->_req->post->checked_smileys[$id] = (int) $smiley_id; |
||||
980 | } |
||||
981 | |||||
982 | if ($this->_req->post->smiley_action === 'delete') |
||||
983 | { |
||||
984 | deleteSmileys($this->_req->post->checked_smileys); |
||||
985 | } |
||||
986 | // Changing the status of the smiley? |
||||
987 | else |
||||
988 | { |
||||
989 | // Check it's a valid type. |
||||
990 | $displayTypes = [ |
||||
991 | 'post' => 0, |
||||
992 | 'hidden' => 1, |
||||
993 | 'popup' => 2 |
||||
994 | ]; |
||||
995 | if (isset($displayTypes[$this->_req->post->smiley_action])) |
||||
996 | { |
||||
997 | updateSmileyDisplayType($this->_req->post->checked_smileys, $displayTypes[$this->_req->post->smiley_action]); |
||||
998 | } |
||||
999 | } |
||||
1000 | } |
||||
1001 | // Create/modify a smiley. |
||||
1002 | elseif (isset($this->_req->post->smiley)) |
||||
1003 | { |
||||
1004 | $this->_req->post->smiley = (int) $this->_req->post->smiley; |
||||
1005 | |||||
1006 | // Is it a delete? |
||||
1007 | if (!empty($this->_req->post->deletesmiley)) |
||||
1008 | { |
||||
1009 | deleteSmileys([$this->_req->post->smiley]); |
||||
1010 | } |
||||
1011 | // Otherwise an edit. |
||||
1012 | else |
||||
1013 | { |
||||
1014 | $this->_req->post->smiley_code = $this->_req->getPost('smiley_code', '\\ElkArte\\Helper\\Util::htmltrim', ''); |
||||
1015 | $this->_req->post->smiley_filename = $this->_req->getPost('smiley_filename', '\\ElkArte\\Helper\\Util::htmltrim', ''); |
||||
1016 | $this->_req->post->smiley_location = empty($this->_req->post->smiley_location) |
||||
1017 | || $this->_req->post->smiley_location > 2 |
||||
1018 | || $this->_req->post->smiley_location < 0 ? 0 : (int) $this->_req->post->smiley_location; |
||||
1019 | |||||
1020 | // Make sure some code was entered. |
||||
1021 | if (empty($this->_req->post->smiley_code)) |
||||
1022 | { |
||||
1023 | throw new Exception('smiley_has_no_code', false); |
||||
1024 | } |
||||
1025 | |||||
1026 | // Also make sure a filename was given. |
||||
1027 | if (empty($this->_req->post->smiley_filename)) |
||||
1028 | { |
||||
1029 | throw new Exception('smiley_has_no_filename', false); |
||||
1030 | } |
||||
1031 | |||||
1032 | // Check whether the new code has duplicates. It should be unique. |
||||
1033 | if (validateDuplicateSmiley($this->_req->post->smiley_code, $this->_req->post->smiley)) |
||||
1034 | { |
||||
1035 | throw new Exception('smiley_not_unique', false); |
||||
1036 | } |
||||
1037 | |||||
1038 | $param = [ |
||||
1039 | 'smiley_location' => $this->_req->post->smiley_location, |
||||
1040 | 'smiley' => $this->_req->post->smiley, |
||||
1041 | 'smiley_code' => $this->_req->post->smiley_code, |
||||
1042 | 'smiley_filename' => $this->_req->post->smiley_filename, |
||||
1043 | 'smiley_description' => $this->_req->post->smiley_description, |
||||
1044 | ]; |
||||
1045 | updateSmiley($param); |
||||
1046 | } |
||||
1047 | } |
||||
1048 | |||||
1049 | $this->clearSmileyCache(); |
||||
1050 | } |
||||
1051 | |||||
1052 | // Load all known smiley sets. |
||||
1053 | $this->loadSmileySets(); |
||||
1054 | |||||
1055 | // Prepare overview of all (custom) smileys. |
||||
1056 | if ($context['sub_action'] === 'editsmileys') |
||||
1057 | { |
||||
1058 | theme()->addJavascriptVar([ |
||||
1059 | 'txt_remove' => JavaScriptEscape($txt['smileys_confirm']), |
||||
1060 | ]); |
||||
1061 | |||||
1062 | // Determine the language specific sort order of smiley locations. |
||||
1063 | $smiley_locations = [ |
||||
1064 | $txt['smileys_location_form'], |
||||
1065 | $txt['smileys_location_hidden'], |
||||
1066 | $txt['smileys_location_popup'], |
||||
1067 | ]; |
||||
1068 | asort($smiley_locations); |
||||
1069 | |||||
1070 | // Create a list of options for selecting smiley sets. |
||||
1071 | $smileyset_option_list = ' |
||||
1072 | <select id="set" name="set" onchange="changeSet(this.options[this.selectedIndex].value);">'; |
||||
1073 | |||||
1074 | foreach ($context['smiley_sets'] as $smiley_set) |
||||
1075 | { |
||||
1076 | $smileyset_option_list .= ' |
||||
1077 | <option data-ext="' . $smiley_set['ext'] . '" value="' . $smiley_set['path'] . '"' . ($modSettings['smiley_sets_default'] === $smiley_set['path'] ? ' selected="selected"' : '') . '>' . $smiley_set['name'] . '</option>'; |
||||
1078 | } |
||||
1079 | |||||
1080 | $smileyset_option_list .= ' |
||||
1081 | </select>'; |
||||
1082 | |||||
1083 | $listOptions = [ |
||||
1084 | 'id' => 'smiley_list', |
||||
1085 | 'title' => $txt['smileys_edit'], |
||||
1086 | 'items_per_page' => 40, |
||||
1087 | 'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editsmileys']), |
||||
1088 | 'default_sort_col' => 'filename', |
||||
1089 | 'get_items' => [ |
||||
1090 | 'function' => 'list_getSmileys', |
||||
1091 | ], |
||||
1092 | 'get_count' => [ |
||||
1093 | 'function' => 'list_getNumSmileys', |
||||
1094 | ], |
||||
1095 | 'no_items_label' => $txt['smileys_no_entries'], |
||||
1096 | 'columns' => [ |
||||
1097 | 'picture' => [ |
||||
1098 | 'data' => [ |
||||
1099 | 'function' => static fn($rowData) => ' |
||||
1100 | <a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'modifysmiley', 'smiley' => '']) . $rowData['id_smiley'] . '"> |
||||
1101 | <img class="smiley" src="' . $context['smiley_path'] . $rowData['filename'] . '.' . $context['smiley_extension'] . '" alt="' . $rowData['description'] . '" id="smiley' . $rowData['id_smiley'] . '" /> |
||||
1102 | <input type="hidden" name="smileys[' . $rowData['id_smiley'] . '][filename]" value="' . $rowData['filename'] . '" /> |
||||
1103 | </a>' |
||||
1104 | ], |
||||
1105 | 'class' => 'imagecolumn', |
||||
1106 | ], |
||||
1107 | 'code' => [ |
||||
1108 | 'header' => [ |
||||
1109 | 'value' => $txt['smileys_code'], |
||||
1110 | ], |
||||
1111 | 'data' => [ |
||||
1112 | 'db_htmlsafe' => 'code', |
||||
1113 | ], |
||||
1114 | 'sort' => [ |
||||
1115 | 'default' => 'code', |
||||
1116 | 'reverse' => 'code DESC', |
||||
1117 | ], |
||||
1118 | ], |
||||
1119 | 'filename' => [ |
||||
1120 | 'header' => [ |
||||
1121 | 'value' => $txt['smileys_filename'], |
||||
1122 | ], |
||||
1123 | 'data' => [ |
||||
1124 | 'db_htmlsafe' => 'filename', |
||||
1125 | ], |
||||
1126 | 'sort' => [ |
||||
1127 | 'default' => 'filename', |
||||
1128 | 'reverse' => 'filename DESC', |
||||
1129 | ], |
||||
1130 | ], |
||||
1131 | 'location' => [ |
||||
1132 | 'header' => [ |
||||
1133 | 'value' => $txt['smileys_location'], |
||||
1134 | ], |
||||
1135 | 'data' => [ |
||||
1136 | 'function' => static fn($rowData) => $smiley_locations[$rowData['hidden']], |
||||
1137 | ], |
||||
1138 | 'sort' => [ |
||||
1139 | 'default' => 'hidden', |
||||
1140 | 'reverse' => 'hidden DESC', |
||||
1141 | ], |
||||
1142 | ], |
||||
1143 | 'description' => [ |
||||
1144 | 'header' => [ |
||||
1145 | 'value' => $txt['smileys_description'], |
||||
1146 | ], |
||||
1147 | 'data' => [ |
||||
1148 | 'function' => static function ($rowData) use ($fileFunc) { |
||||
1149 | global $context, $txt, $modSettings; |
||||
1150 | |||||
1151 | if (empty($modSettings['smileys_dir']) || !$fileFunc->isDir($modSettings['smileys_dir'])) |
||||
1152 | { |
||||
1153 | return htmlspecialchars($rowData['description'], ENT_COMPAT, 'UTF-8'); |
||||
1154 | } |
||||
1155 | |||||
1156 | // Check if there are smileys missing in some sets. |
||||
1157 | $missing_sets = []; |
||||
1158 | $found_replacement = ''; |
||||
1159 | foreach ($context['smiley_sets'] as $smiley_set) |
||||
1160 | { |
||||
1161 | $filename = $rowData['filename'] . '.' . $smiley_set['ext']; |
||||
1162 | if (!$fileFunc->fileExists($modSettings['smileys_dir'] . '/' . $smiley_set['path'] . '/' . $filename)) |
||||
1163 | { |
||||
1164 | $missing_sets[] = $smiley_set['path']; |
||||
1165 | if (possibleSmileEmoji($rowData, $modSettings['smileys_dir'] . '/' . $smiley_set['path'], $smiley_set['ext'])) |
||||
1166 | { |
||||
1167 | $found_replacement = $rowData['emoji'] . '.svg'; |
||||
1168 | } |
||||
1169 | } |
||||
1170 | } |
||||
1171 | |||||
1172 | $description = htmlspecialchars($rowData['description'], ENT_COMPAT, 'UTF-8'); |
||||
1173 | |||||
1174 | if (!empty($missing_sets)) |
||||
1175 | { |
||||
1176 | $description .= ' |
||||
1177 | |||||
1178 | <p class="smalltext"> |
||||
1179 | <strong>' . $txt['smileys_not_found_in_set'] . '</strong> ' . implode(', ', $missing_sets); |
||||
1180 | |||||
1181 | if ($found_replacement !== '' && $found_replacement !== '0') |
||||
1182 | { |
||||
1183 | $description .= '<br />' . sprintf($txt['smileys_emoji_found'], '<img class="smiley emoji" src="' . $context['emoji_path'] . $found_replacement . '" /> '); |
||||
1184 | } |
||||
1185 | |||||
1186 | $description .= ' |
||||
1187 | </p>'; |
||||
1188 | } |
||||
1189 | |||||
1190 | return $description; |
||||
1191 | }, |
||||
1192 | ], |
||||
1193 | 'sort' => [ |
||||
1194 | 'default' => 'description', |
||||
1195 | 'reverse' => 'description DESC', |
||||
1196 | ], |
||||
1197 | ], |
||||
1198 | 'modify' => [ |
||||
1199 | 'header' => [ |
||||
1200 | 'value' => $txt['smileys_modify'], |
||||
1201 | 'class' => 'centertext', |
||||
1202 | ], |
||||
1203 | 'data' => [ |
||||
1204 | 'sprintf' => [ |
||||
1205 | 'format' => '<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'modifysmiley', 'smiley' => '']) . '%1$d">' . $txt['smileys_modify'] . '</a>', |
||||
1206 | 'params' => [ |
||||
1207 | 'id_smiley' => false, |
||||
1208 | ], |
||||
1209 | ], |
||||
1210 | 'class' => 'centertext', |
||||
1211 | ], |
||||
1212 | ], |
||||
1213 | 'check' => [ |
||||
1214 | 'header' => [ |
||||
1215 | 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />', |
||||
1216 | 'class' => 'centertext', |
||||
1217 | ], |
||||
1218 | 'data' => [ |
||||
1219 | 'sprintf' => [ |
||||
1220 | 'format' => '<input type="checkbox" name="checked_smileys[]" value="%1$d" class="input_check" />', |
||||
1221 | 'params' => [ |
||||
1222 | 'id_smiley' => false, |
||||
1223 | ], |
||||
1224 | ], |
||||
1225 | 'class' => 'centertext', |
||||
1226 | ], |
||||
1227 | ], |
||||
1228 | ], |
||||
1229 | 'form' => [ |
||||
1230 | 'href' => getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editsmileys']), |
||||
1231 | 'name' => 'smileyForm', |
||||
1232 | ], |
||||
1233 | 'additional_rows' => [ |
||||
1234 | [ |
||||
1235 | 'position' => 'above_column_headers', |
||||
1236 | 'value' => $txt['smiley_sets'] . $smileyset_option_list, |
||||
1237 | 'class' => 'flow_flex_right', |
||||
1238 | ], |
||||
1239 | [ |
||||
1240 | 'position' => 'below_table_data', |
||||
1241 | 'value' => ' |
||||
1242 | <select name="smiley_action" onchange="makeChanges(this.value);"> |
||||
1243 | <option value="-1">' . $txt['smileys_with_selected'] . ':</option> |
||||
1244 | <option value="-1">--------------</option> |
||||
1245 | <option value="hidden">' . $txt['smileys_make_hidden'] . '</option> |
||||
1246 | <option value="post">' . $txt['smileys_show_on_post'] . '</option> |
||||
1247 | <option value="popup">' . $txt['smileys_show_on_popup'] . '</option> |
||||
1248 | <option value="delete">' . $txt['smileys_remove'] . '</option> |
||||
1249 | </select> |
||||
1250 | <noscript> |
||||
1251 | <input type="submit" name="perform_action" value="' . $txt['go'] . '" class="right_submit" /> |
||||
1252 | </noscript>', |
||||
1253 | 'class' => 'righttext', |
||||
1254 | ], |
||||
1255 | ], |
||||
1256 | ]; |
||||
1257 | |||||
1258 | createList($listOptions); |
||||
1259 | |||||
1260 | // The list is the only thing to show, so make it the main template. |
||||
1261 | $context['default_list'] = 'smiley_list'; |
||||
1262 | $context['sub_template'] = 'show_list'; |
||||
1263 | } |
||||
1264 | // Modifying smileys. |
||||
1265 | elseif ($context['sub_action'] === 'modifysmiley') |
||||
1266 | { |
||||
1267 | // Get a list of all known smiley sets. |
||||
1268 | $context['smileys_dir'] = empty($modSettings['smileys_dir']) ? BOARDDIR . '/smileys' : $modSettings['smileys_dir']; |
||||
1269 | $context['selected_set'] = $modSettings['smiley_sets_default']; |
||||
1270 | |||||
1271 | $this->loadSmileySets(); |
||||
1272 | |||||
1273 | // Get all possible filenames for the smileys. |
||||
1274 | $context['filenames'] = $this->getAllPossibleFilenamesForTheSmileys($context['smiley_sets']); |
||||
1275 | |||||
1276 | $thisSmiley = (int) $this->_req->query->smiley; |
||||
1277 | $context['current_smiley'] = getSmiley($thisSmiley); |
||||
1278 | $context['current_smiley']['code'] = htmlspecialchars($context['current_smiley']['code'], ENT_COMPAT, 'UTF-8'); |
||||
1279 | $context['current_smiley']['filename'] = htmlspecialchars($context['current_smiley']['filename'], ENT_COMPAT, 'UTF-8'); |
||||
1280 | $context['current_smiley']['description'] = htmlspecialchars($context['current_smiley']['description'], ENT_COMPAT, 'UTF-8'); |
||||
1281 | |||||
1282 | if (isset($context['filenames'][strtolower($context['current_smiley']['filename'])])) |
||||
1283 | { |
||||
1284 | $context['filenames'][strtolower($context['current_smiley']['filename'])]['selected'] = true; |
||||
1285 | } |
||||
1286 | } |
||||
1287 | } |
||||
1288 | |||||
1289 | /** |
||||
1290 | * Allows to edit the message icons. |
||||
1291 | * |
||||
1292 | * @event integrate_list_message_icon_list |
||||
1293 | */ |
||||
1294 | public function action_editicon() |
||||
1295 | { |
||||
1296 | global $context, $settings, $txt; |
||||
1297 | |||||
1298 | $fileFunc = FileFunctions::instance(); |
||||
1299 | require_once(SUBSDIR . '/MessageIcons.subs.php'); |
||||
1300 | |||||
1301 | // Get a list of icons. |
||||
1302 | $context['icons'] = fetchMessageIconsDetails(); |
||||
1303 | |||||
1304 | // Submitting a form? |
||||
1305 | if (isset($this->_req->post->icons_save)) |
||||
1306 | { |
||||
1307 | checkSession(); |
||||
1308 | |||||
1309 | // Deleting icons? |
||||
1310 | if (isset($this->_req->post->delete) && !empty($this->_req->post->checked_icons)) |
||||
1311 | { |
||||
1312 | $deleteIcons = []; |
||||
1313 | foreach ($this->_req->post->checked_icons as $icon) |
||||
1314 | { |
||||
1315 | $deleteIcons[] = (int) $icon; |
||||
1316 | } |
||||
1317 | |||||
1318 | // Do the actual delete! |
||||
1319 | deleteMessageIcons($deleteIcons); |
||||
1320 | } |
||||
1321 | // Editing/Adding an icon? |
||||
1322 | elseif ($context['sub_action'] === 'editicon' && isset($this->_req->query->icon)) |
||||
1323 | { |
||||
1324 | $this->_req->query->icon = (int) $this->_req->query->icon; |
||||
1325 | |||||
1326 | // Do some preparation with the data... like check the icon exists *somewhere* |
||||
1327 | if (strpos($this->_req->post->icon_filename, '.png') !== false) |
||||
1328 | { |
||||
1329 | $this->_req->post->icon_filename = substr($this->_req->post->icon_filename, 0, -4); |
||||
1330 | } |
||||
1331 | |||||
1332 | if (!$fileFunc->fileExists($settings['default_theme_dir'] . '/images/post/' . $this->_req->post->icon_filename . '.png')) |
||||
1333 | { |
||||
1334 | throw new Exception('icon_not_found', false); |
||||
1335 | } |
||||
1336 | |||||
1337 | // There is a 16-character limit on message icons... |
||||
1338 | if (strlen($this->_req->post->icon_filename) > 16) |
||||
1339 | { |
||||
1340 | throw new Exception('icon_name_too_long', false); |
||||
1341 | } |
||||
1342 | |||||
1343 | if ($this->_req->post->icon_location === $this->_req->query->icon && $this->_req->query->icon !== 0) |
||||
1344 | { |
||||
1345 | throw new Exception('icon_after_itself', false); |
||||
1346 | } |
||||
1347 | |||||
1348 | // First do the sorting... if this is an edit reduce the order of everything after it by one ;) |
||||
1349 | if ($this->_req->query->icon !== 0) |
||||
1350 | { |
||||
1351 | $oldOrder = $context['icons'][$this->_req->query->icon]['true_order']; |
||||
1352 | foreach ($context['icons'] as $id => $data) |
||||
1353 | { |
||||
1354 | if ($data['true_order'] > $oldOrder) |
||||
1355 | { |
||||
1356 | $context['icons'][$id]['true_order']--; |
||||
1357 | } |
||||
1358 | } |
||||
1359 | } |
||||
1360 | |||||
1361 | // If there are no existing icons and this is a new one, set the id to 1 (mainly for non-mysql) |
||||
1362 | if ($this->_req->query->icon === 0 && empty($context['icons'])) |
||||
1363 | { |
||||
1364 | $this->_req->query->icon = 1; |
||||
1365 | } |
||||
1366 | |||||
1367 | // Get the new order. |
||||
1368 | $newOrder = $this->_req->post->icon_location == 0 ? 0 : $context['icons'][$this->_req->post->icon_location]['true_order'] + 1; |
||||
1369 | |||||
1370 | // Do the same, but with the one that used to be after this icon, done to avoid conflict. |
||||
1371 | foreach ($context['icons'] as $id => $data) |
||||
1372 | { |
||||
1373 | if ($data['true_order'] >= $newOrder) |
||||
1374 | { |
||||
1375 | $context['icons'][$id]['true_order']++; |
||||
1376 | } |
||||
1377 | } |
||||
1378 | |||||
1379 | // Finally set the current icon's position! |
||||
1380 | $context['icons'][$this->_req->query->icon]['true_order'] = $newOrder; |
||||
1381 | |||||
1382 | // Simply replace the existing data for the other bits. |
||||
1383 | $context['icons'][$this->_req->query->icon]['title'] = $this->_req->post->icon_description; |
||||
1384 | $context['icons'][$this->_req->query->icon]['filename'] = $this->_req->post->icon_filename; |
||||
1385 | $context['icons'][$this->_req->query->icon]['board_id'] = (int) $this->_req->post->icon_board; |
||||
1386 | |||||
1387 | // Do a huge replace ;) |
||||
1388 | $iconInsert = []; |
||||
1389 | $iconInsert_new = []; |
||||
1390 | foreach ($context['icons'] as $id => $icon) |
||||
1391 | { |
||||
1392 | if ($id != 0) |
||||
1393 | { |
||||
1394 | $iconInsert[] = [$id, $icon['board_id'], $icon['title'], $icon['filename'], $icon['true_order']]; |
||||
1395 | } |
||||
1396 | else |
||||
1397 | { |
||||
1398 | $iconInsert_new[] = [$icon['board_id'], $icon['title'], $icon['filename'], $icon['true_order']]; |
||||
1399 | } |
||||
1400 | } |
||||
1401 | |||||
1402 | updateMessageIcon($iconInsert); |
||||
1403 | |||||
1404 | if (!empty($iconInsert_new)) |
||||
1405 | { |
||||
1406 | addMessageIcon($iconInsert_new); |
||||
1407 | |||||
1408 | // Flush the cache so the changes are reflected |
||||
1409 | Cache::instance()->remove('posting_icons-' . (int) $this->_req->post->icon_board); |
||||
1410 | } |
||||
1411 | } |
||||
1412 | |||||
1413 | // Unless we're adding a new thing, we'll escape |
||||
1414 | if (!isset($this->_req->post->add)) |
||||
1415 | { |
||||
1416 | Cache::instance()->remove('posting_icons-0'); |
||||
1417 | redirectexit('action=admin;area=smileys;sa=editicons'); |
||||
1418 | } |
||||
1419 | } |
||||
1420 | |||||
1421 | $context[$context['admin_menu_name']]['current_subsection'] = 'editicons'; |
||||
1422 | $token = createToken('admin-sort'); |
||||
1423 | $listOptions = [ |
||||
1424 | 'id' => 'message_icon_list', |
||||
1425 | 'title' => $txt['icons_edit_message_icons'], |
||||
1426 | 'sortable' => true, |
||||
1427 | 'base_href' => getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editicons']), |
||||
1428 | 'get_items' => [ |
||||
1429 | 'function' => fn() => $this->list_fetchMessageIconsDetails(), |
||||
1430 | ], |
||||
1431 | 'no_items_label' => $txt['icons_no_entries'], |
||||
1432 | 'columns' => [ |
||||
1433 | 'icon' => [ |
||||
1434 | 'data' => [ |
||||
1435 | 'sprintf' => [ |
||||
1436 | 'format' => '<img src="%1$s" alt="%2$s" />', |
||||
1437 | 'params' => [ |
||||
1438 | 'image_url' => false, |
||||
1439 | 'filename' => true, |
||||
1440 | ], |
||||
1441 | ], |
||||
1442 | 'class' => 'centertext', |
||||
1443 | ], |
||||
1444 | ], |
||||
1445 | 'filename' => [ |
||||
1446 | 'header' => [ |
||||
1447 | 'value' => $txt['smileys_filename'], |
||||
1448 | ], |
||||
1449 | 'data' => [ |
||||
1450 | 'sprintf' => [ |
||||
1451 | 'format' => '%1$s.png', |
||||
1452 | 'params' => [ |
||||
1453 | 'filename' => true, |
||||
1454 | ], |
||||
1455 | ], |
||||
1456 | ], |
||||
1457 | ], |
||||
1458 | 'tooltip' => [ |
||||
1459 | 'header' => [ |
||||
1460 | 'value' => $txt['smileys_description'], |
||||
1461 | ], |
||||
1462 | 'data' => [ |
||||
1463 | 'db_htmlsafe' => 'title', |
||||
1464 | ], |
||||
1465 | ], |
||||
1466 | 'board' => [ |
||||
1467 | 'header' => [ |
||||
1468 | 'value' => $txt['icons_board'], |
||||
1469 | ], |
||||
1470 | 'data' => [ |
||||
1471 | 'db' => 'board', |
||||
1472 | ], |
||||
1473 | ], |
||||
1474 | 'modify' => [ |
||||
1475 | 'header' => [ |
||||
1476 | 'value' => $txt['smileys_modify'], |
||||
1477 | ], |
||||
1478 | 'data' => [ |
||||
1479 | 'sprintf' => [ |
||||
1480 | 'format' => '<a href="' . getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editicon', 'icon' => '']) . '%1$s">' . $txt['smileys_modify'] . '</a>', |
||||
1481 | 'params' => [ |
||||
1482 | 'id' => false, |
||||
1483 | ], |
||||
1484 | ], |
||||
1485 | ], |
||||
1486 | ], |
||||
1487 | 'check' => [ |
||||
1488 | 'header' => [ |
||||
1489 | 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />', |
||||
1490 | 'class' => 'centertext', |
||||
1491 | ], |
||||
1492 | 'data' => [ |
||||
1493 | 'sprintf' => [ |
||||
1494 | 'format' => '<input type="checkbox" name="checked_icons[]" value="%1$d" class="input_check" />', |
||||
1495 | 'params' => [ |
||||
1496 | 'id' => false, |
||||
1497 | ], |
||||
1498 | ], |
||||
1499 | 'class' => 'centertext', |
||||
1500 | ], |
||||
1501 | ], |
||||
1502 | ], |
||||
1503 | 'form' => [ |
||||
1504 | 'href' => getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editicons']), |
||||
1505 | 'hidden_fields' => [ |
||||
1506 | 'icons_save' => 1, |
||||
1507 | ] |
||||
1508 | ], |
||||
1509 | 'additional_rows' => [ |
||||
1510 | [ |
||||
1511 | 'position' => 'below_table_data', |
||||
1512 | 'class' => 'submitbutton', |
||||
1513 | 'value' => ' |
||||
1514 | <a class="linkbutton" href="' . getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'editicon']) . '">' . $txt['icons_add_new'] . '</a> |
||||
1515 | <input type="submit" name="delete" value="' . $txt['quickmod_delete_selected'] . '" onclick="return confirm(\'' . $txt['icons_confirm'] . '\');" />', |
||||
1516 | ], |
||||
1517 | [ |
||||
1518 | 'position' => 'after_title', |
||||
1519 | 'value' => $txt['icons_reorder_note'], |
||||
1520 | ], |
||||
1521 | ], |
||||
1522 | 'javascript' => ' |
||||
1523 | $().elkSortable({ |
||||
1524 | sa: "messageiconorder", |
||||
1525 | error: "' . $txt['admin_order_error'] . '", |
||||
1526 | title: "' . $txt['admin_order_title'] . '", |
||||
1527 | placeholder: "ui-state-highlight", |
||||
1528 | href: "?action=admin;area=smileys;sa=editicons", |
||||
1529 | token: {token_var: "' . $token['admin-sort_token_var'] . '", token_id: "' . $token['admin-sort_token'] . '"} |
||||
1530 | }); |
||||
1531 | ', |
||||
1532 | ]; |
||||
1533 | |||||
1534 | createList($listOptions); |
||||
1535 | |||||
1536 | // If we're adding/editing an icon we'll need a list of boards |
||||
1537 | if ($context['sub_action'] === 'editicon' || isset($this->_req->post->add)) |
||||
1538 | { |
||||
1539 | // Force the sub_template just in case. |
||||
1540 | $context['sub_template'] = 'editicon'; |
||||
1541 | $context['new_icon'] = !isset($this->_req->query->icon); |
||||
1542 | |||||
1543 | // Get the properties of the current icon from the icon list. |
||||
1544 | if (!$context['new_icon']) |
||||
1545 | { |
||||
1546 | $context['icon'] = $context['icons'][$this->_req->query->icon]; |
||||
1547 | } |
||||
1548 | |||||
1549 | // Get a list of boards needed for assigning this icon to a specific board. |
||||
1550 | $boardListOptions = [ |
||||
1551 | 'selected_board' => $context['icon']['board_id'] ?? 0, |
||||
1552 | ]; |
||||
1553 | require_once(SUBSDIR . '/Boards.subs.php'); |
||||
1554 | $context += getBoardList($boardListOptions); |
||||
1555 | } |
||||
1556 | } |
||||
1557 | |||||
1558 | /** |
||||
1559 | * Callback function for createList(). |
||||
1560 | */ |
||||
1561 | public function list_fetchMessageIconsDetails() |
||||
1562 | { |
||||
1563 | return fetchMessageIconsDetails(); |
||||
1564 | } |
||||
1565 | |||||
1566 | /** |
||||
1567 | * Allows to edit smileys order. |
||||
1568 | */ |
||||
1569 | public function action_setorder() |
||||
1570 | { |
||||
1571 | global $context, $txt; |
||||
1572 | |||||
1573 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||||
1574 | $context['sub_template'] = 'setorder'; |
||||
1575 | |||||
1576 | // Move smileys to another position. |
||||
1577 | if (isset($this->_req->query->reorder)) |
||||
1578 | { |
||||
1579 | $location = empty($this->_req->query->location) || $this->_req->query->location !== 'popup' ? 0 : 2; |
||||
1580 | $source = $this->_req->getQuery('source', 'intval', 0); |
||||
1581 | $after = $this->_req->getQuery('after', 'intval', 0); |
||||
1582 | $row = $this->_req->getQuery('row', 'intval', 0); |
||||
1583 | |||||
1584 | checkSession('get'); |
||||
1585 | |||||
1586 | if (empty($source)) |
||||
1587 | { |
||||
1588 | throw new Exception('smiley_not_found', false); |
||||
1589 | } |
||||
1590 | |||||
1591 | $smiley = []; |
||||
1592 | |||||
1593 | if (!empty($after)) |
||||
1594 | { |
||||
1595 | $smiley = getSmileyPosition($location, $after); |
||||
1596 | if (empty($smiley)) |
||||
1597 | { |
||||
1598 | throw new Exception('smiley_not_found'); |
||||
1599 | } |
||||
1600 | } |
||||
1601 | else |
||||
1602 | { |
||||
1603 | $smiley['row'] = $row; |
||||
1604 | $smiley['order'] = -1; |
||||
1605 | $smiley['location'] = $location; |
||||
1606 | } |
||||
1607 | |||||
1608 | moveSmileyPosition($smiley, $source); |
||||
1609 | } |
||||
1610 | |||||
1611 | $context['smileys'] = getSmileys(); |
||||
1612 | $context['move_smiley'] = $this->_req->getQuery('move', 'intval', 0); |
||||
1613 | |||||
1614 | // Make sure all rows are sequential. |
||||
1615 | foreach (array_keys($context['smileys']) as $location) |
||||
1616 | { |
||||
1617 | $context['smileys'][$location] = [ |
||||
1618 | 'id' => $location, |
||||
1619 | 'title' => $location === 'postform' ? $txt['smileys_location_form'] : $txt['smileys_location_popup'], |
||||
1620 | 'description' => $location === 'postform' ? $txt['smileys_location_form_description'] : $txt['smileys_location_popup_description'], |
||||
1621 | 'last_row' => count($context['smileys'][$location]['rows']), |
||||
1622 | 'rows' => array_values($context['smileys'][$location]['rows']), |
||||
1623 | ]; |
||||
1624 | } |
||||
1625 | |||||
1626 | // Check & fix smileys that are not ordered properly in the database. |
||||
1627 | foreach (array_keys($context['smileys']) as $location) |
||||
1628 | { |
||||
1629 | foreach ($context['smileys'][$location]['rows'] as $id => $smiley_row) |
||||
1630 | { |
||||
1631 | // Fix empty rows if any. |
||||
1632 | if ($id != $smiley_row[0]['row']) |
||||
1633 | { |
||||
1634 | updateSmileyRow($id, $smiley_row[0]['row'], $location); |
||||
1635 | |||||
1636 | // Only change the first row value of the first smiley (we don't need the others :P). |
||||
1637 | $context['smileys'][$location]['rows'][$id][0]['row'] = $id; |
||||
1638 | } |
||||
1639 | |||||
1640 | // Make sure the smiley order is always sequential. |
||||
1641 | foreach ($smiley_row as $order_id => $smiley) |
||||
1642 | { |
||||
1643 | if ($order_id != $smiley['order']) |
||||
1644 | { |
||||
1645 | updateSmileyOrder($smiley['id'], $order_id); |
||||
1646 | } |
||||
1647 | } |
||||
1648 | } |
||||
1649 | } |
||||
1650 | |||||
1651 | $this->clearSmileyCache(); |
||||
1652 | |||||
1653 | createToken('admin-sort'); |
||||
1654 | } |
||||
1655 | |||||
1656 | /** |
||||
1657 | * Install a smiley set. |
||||
1658 | */ |
||||
1659 | public function action_install() |
||||
1660 | { |
||||
1661 | global $modSettings, $context, $txt, $scripturl; |
||||
1662 | |||||
1663 | isAllowedTo('manage_smileys'); |
||||
1664 | checkSession('request'); |
||||
1665 | |||||
1666 | // One of these two may be necessary |
||||
1667 | Txt::load('Errors'); |
||||
1668 | Txt::load('Packages'); |
||||
1669 | |||||
1670 | $fileFunc = FileFunctions::instance(); |
||||
1671 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||||
1672 | require_once(SUBSDIR . '/Package.subs.php'); |
||||
1673 | |||||
1674 | // Installing unless proven otherwise |
||||
1675 | $testing = false; |
||||
1676 | $destination = ''; |
||||
1677 | $name = ''; |
||||
1678 | $base_name = ''; |
||||
1679 | |||||
1680 | if (isset($this->_req->query->set_gz)) |
||||
1681 | { |
||||
1682 | $base_name = strtr(basename($this->_req->query->set_gz), ':/', '-_'); |
||||
1683 | $name = Util::htmlspecialchars(strtok(basename($this->_req->query->set_gz), '.')); |
||||
1684 | $context['filename'] = $base_name; |
||||
1685 | |||||
1686 | $destination = BOARDDIR . '/packages/' . $base_name; |
||||
1687 | |||||
1688 | if ($fileFunc->fileExists($destination)) |
||||
1689 | { |
||||
1690 | throw new Exception('package_upload_error_exists'); |
||||
1691 | } |
||||
1692 | |||||
1693 | // Let's copy it to the packages directory |
||||
1694 | file_put_contents($destination, fetch_web_data($this->_req->query->set_gz)); |
||||
1695 | $testing = true; |
||||
1696 | } |
||||
1697 | elseif (isset($this->_req->query->package)) |
||||
1698 | { |
||||
1699 | $base_name = basename($this->_req->query->package); |
||||
1700 | $name = Util::htmlspecialchars(strtok(basename($this->_req->query->package), '.')); |
||||
1701 | $context['filename'] = $base_name; |
||||
1702 | |||||
1703 | $destination = BOARDDIR . '/packages/' . basename($this->_req->query->package); |
||||
1704 | } |
||||
1705 | |||||
1706 | if (!$fileFunc->fileExists($destination)) |
||||
1707 | { |
||||
1708 | throw new Exception('package_no_file', false); |
||||
1709 | } |
||||
1710 | |||||
1711 | // Make sure temp directory exists and is empty. |
||||
1712 | if ($fileFunc->fileExists(BOARDDIR . '/packages/temp')) |
||||
1713 | { |
||||
1714 | deltree(BOARDDIR . '/packages/temp', false); |
||||
1715 | } |
||||
1716 | |||||
1717 | if (!mktree(BOARDDIR . '/packages/temp')) |
||||
1718 | { |
||||
1719 | deltree(BOARDDIR . '/packages/temp', false); |
||||
1720 | $chmod_control = new PackageChmod(); |
||||
1721 | $chmod_control->createChmodControl( |
||||
1722 | [BOARDDIR . '/packages/temp/delme.tmp'], |
||||
1723 | [ |
||||
1724 | 'destination_url' => $scripturl . '?action=admin;area=smileys;sa=install;set_gz=' . $this->_req->query->set_gz, |
||||
1725 | 'crash_on_error' => true |
||||
1726 | ] |
||||
1727 | ); |
||||
1728 | |||||
1729 | deltree(BOARDDIR . '/packages/temp', false); |
||||
1730 | throw new Exception('package_cant_download', false); |
||||
1731 | } |
||||
1732 | |||||
1733 | $extracted = read_tgz_file($destination, BOARDDIR . '/packages/temp'); |
||||
1734 | |||||
1735 | // @todo needs to change the URL in the next line ;) |
||||
1736 | if (!$extracted) |
||||
1737 | { |
||||
1738 | throw new Exception('packageget_unable', false, ['https://custom.elkarte.net/index.php?action=search;type=12;basic_search=' . $name]); |
||||
1739 | } |
||||
1740 | |||||
1741 | if (!$fileFunc->fileExists(BOARDDIR . '/packages/temp/package-info.xml')) |
||||
1742 | { |
||||
1743 | foreach ($extracted as $file) |
||||
1744 | { |
||||
1745 | if (basename($file['filename']) === 'package-info.xml') |
||||
1746 | { |
||||
1747 | $base_path = dirname($file['filename']) . '/'; |
||||
1748 | break; |
||||
1749 | } |
||||
1750 | } |
||||
1751 | } |
||||
1752 | |||||
1753 | if (!isset($base_path)) |
||||
1754 | { |
||||
1755 | $base_path = ''; |
||||
1756 | } |
||||
1757 | |||||
1758 | if (!$fileFunc->fileExists(BOARDDIR . '/packages/temp/' . $base_path . 'package-info.xml')) |
||||
1759 | { |
||||
1760 | throw new Exception('package_get_error_missing_xml', false); |
||||
1761 | } |
||||
1762 | |||||
1763 | $smileyInfo = getPackageInfo($context['filename']); |
||||
1764 | if (!is_array($smileyInfo)) |
||||
1765 | { |
||||
1766 | throw new Exception($smileyInfo); |
||||
1767 | } |
||||
1768 | |||||
1769 | // See if it is installed? |
||||
1770 | if (isSmileySetInstalled($smileyInfo['id'])) |
||||
1771 | { |
||||
1772 | Errors::instance()->fatal_lang_error('package_installed_warning1'); |
||||
1773 | } |
||||
1774 | |||||
1775 | // Everything is fine, now it's time to do something, first we test |
||||
1776 | $parser = new PackageParser(); |
||||
1777 | $actions = $parser->parsePackageInfo($smileyInfo['xml'], true); |
||||
1778 | |||||
1779 | $context['post_url'] = getUrl('admin', ['action' => 'admin', 'area' => 'smileys', 'sa' => 'install', 'package' => $base_name]); |
||||
1780 | $context['has_failure'] = false; |
||||
1781 | $context['actions'] = []; |
||||
1782 | $context['ftp_needed'] = false; |
||||
1783 | |||||
1784 | $bbc_parser = ParserWrapper::instance(); |
||||
1785 | |||||
1786 | foreach ($actions as $action) |
||||
1787 | { |
||||
1788 | if ($action['type'] === 'readme' || $action['type'] === 'license') |
||||
1789 | { |
||||
1790 | $type = 'package_' . $action['type']; |
||||
1791 | if ($fileFunc->fileExists(BOARDDIR . '/packages/temp/' . $base_path . $action['filename'])) |
||||
1792 | { |
||||
1793 | $context[$type] = htmlspecialchars(trim(file_get_contents(BOARDDIR . '/packages/temp/' . $base_path . $action['filename']), "\n\r"), ENT_COMPAT, 'UTF-8'); |
||||
1794 | } |
||||
1795 | elseif ($fileFunc->fileExists($action['filename'])) |
||||
1796 | { |
||||
1797 | $context[$type] = htmlspecialchars(trim(file_get_contents($action['filename']), "\n\r"), ENT_COMPAT, 'UTF-8'); |
||||
1798 | } |
||||
1799 | |||||
1800 | if (!empty($action['parse_bbc'])) |
||||
1801 | { |
||||
1802 | require_once(SUBSDIR . '/Post.subs.php'); |
||||
1803 | preparsecode($context[$type]); |
||||
1804 | $context[$type] = $bbc_parser->parsePackage($context[$type]); |
||||
1805 | } |
||||
1806 | else |
||||
1807 | { |
||||
1808 | $context[$type] = nl2br($context[$type]); |
||||
1809 | } |
||||
1810 | } |
||||
1811 | elseif ($action['type'] === 'require-dir') |
||||
1812 | { |
||||
1813 | // Do this one... |
||||
1814 | $thisAction = [ |
||||
1815 | 'type' => $txt['package_extract'] . ' ' . $txt['package_tree'], |
||||
1816 | 'action' => Util::htmlspecialchars(strtr($action['destination'], [BOARDDIR => '.'])) |
||||
1817 | ]; |
||||
1818 | |||||
1819 | $file = BOARDDIR . '/packages/temp/' . $base_path . $action['filename']; |
||||
1820 | if (isset($action['filename']) && (!$fileFunc->fileExists($file) || !$fileFunc->isWritable(dirname($action['destination'])))) |
||||
1821 | { |
||||
1822 | $context['has_failure'] = true; |
||||
1823 | |||||
1824 | $thisAction += [ |
||||
1825 | 'description' => $txt['package_action_error'], |
||||
1826 | 'failed' => true, |
||||
1827 | ]; |
||||
1828 | } |
||||
1829 | |||||
1830 | // Show a description for the action if one is provided |
||||
1831 | if (empty($thisAction['description'])) |
||||
1832 | { |
||||
1833 | $thisAction['description'] = $action['description'] ?? ''; |
||||
1834 | } |
||||
1835 | |||||
1836 | $context['actions'][] = $thisAction; |
||||
1837 | } |
||||
1838 | elseif ($action['type'] === 'credits') |
||||
1839 | { |
||||
1840 | // Time to build the billboard |
||||
1841 | $credits_tag = [ |
||||
1842 | 'url' => $action['url'], |
||||
1843 | 'license' => $action['license'], |
||||
1844 | 'copyright' => $action['copyright'], |
||||
1845 | 'title' => $action['title'], |
||||
1846 | ]; |
||||
1847 | } |
||||
1848 | } |
||||
1849 | |||||
1850 | if ($testing) |
||||
1851 | { |
||||
1852 | $context['sub_template'] = 'view_package'; |
||||
1853 | $context['uninstalling'] = false; |
||||
1854 | $context['is_installed'] = false; |
||||
1855 | $context['package_name'] = $smileyInfo['name']; |
||||
1856 | theme()->getTemplates()->load('Packages'); |
||||
1857 | } |
||||
1858 | // Do the actual install |
||||
1859 | else |
||||
1860 | { |
||||
1861 | foreach ($context['actions'] as $action) |
||||
1862 | { |
||||
1863 | updateSettings([ |
||||
1864 | 'smiley_sets_known' => $modSettings['smiley_sets_known'] . ',' . basename($action['action']), |
||||
1865 | 'smiley_sets_names' => $modSettings['smiley_sets_names'] . "\n" . $smileyInfo['name'] . (count($context['actions']) > 1 ? ' ' . (empty($action['description']) ? basename($action['action']) : Util::htmlspecialchars($action['description'])) : ''), |
||||
1866 | ]); |
||||
1867 | } |
||||
1868 | |||||
1869 | package_flush_cache(); |
||||
1870 | |||||
1871 | // Time to tell pacman we have a new package installed! |
||||
1872 | package_put_contents(BOARDDIR . '/packages/installed.list', time()); |
||||
1873 | |||||
1874 | // Credits tag? |
||||
1875 | $credits_tag = (empty($credits_tag)) ? '' : serialize($credits_tag); |
||||
1876 | $installed = [ |
||||
1877 | 'filename' => $smileyInfo['filename'], |
||||
1878 | 'name' => $smileyInfo['name'], |
||||
1879 | 'package_id' => $smileyInfo['id'], |
||||
1880 | 'version' => $smileyInfo['filename'], |
||||
1881 | 'id_member' => $this->user->id, |
||||
1882 | 'member_name' => $this->user->name, |
||||
1883 | 'credits_tag' => $credits_tag, |
||||
1884 | ]; |
||||
1885 | logPackageInstall($installed); |
||||
1886 | |||||
1887 | logAction('install_package', ['package' => Util::htmlspecialchars($smileyInfo['name']), 'version' => Util::htmlspecialchars($smileyInfo['version'])], 'admin'); |
||||
1888 | |||||
1889 | $this->clearSmileyCache(); |
||||
1890 | } |
||||
1891 | |||||
1892 | if ($fileFunc->fileExists(BOARDDIR . '/packages/temp')) |
||||
1893 | { |
||||
1894 | deltree(BOARDDIR . '/packages/temp'); |
||||
1895 | } |
||||
1896 | |||||
1897 | if (!$testing) |
||||
1898 | { |
||||
1899 | redirectexit('action=admin;area=smileys'); |
||||
1900 | } |
||||
1901 | } |
||||
1902 | |||||
1903 | /** |
||||
1904 | * Load known smiley set information into context |
||||
1905 | * |
||||
1906 | * @return void |
||||
1907 | */ |
||||
1908 | public function loadSmileySets() |
||||
1909 | { |
||||
1910 | global $context, $modSettings; |
||||
1911 | |||||
1912 | $set_paths = explode(',', $modSettings['smiley_sets_known']); |
||||
1913 | $set_names = explode("\n", $modSettings['smiley_sets_names']); |
||||
1914 | $set_exts = isset($modSettings['smiley_sets_extensions']) |
||||
1915 | ? explode(',', $modSettings['smiley_sets_extensions']) |
||||
1916 | : setSmileyExtensionArray(); |
||||
1917 | |||||
1918 | foreach ($set_paths as $i => $set) |
||||
1919 | { |
||||
1920 | $context['smiley_sets'][$i] = [ |
||||
1921 | 'id' => $i, |
||||
1922 | 'path' => htmlspecialchars($set, ENT_COMPAT), |
||||
1923 | 'name' => htmlspecialchars(stripslashes($set_names[$i]), ENT_COMPAT), |
||||
1924 | 'selected' => $set === $modSettings['smiley_sets_default'], |
||||
1925 | 'ext' => Util::htmlspecialchars($set_exts[$i]), |
||||
1926 | ]; |
||||
1927 | } |
||||
1928 | } |
||||
1929 | |||||
1930 | /** |
||||
1931 | * Perhaps a longer name for the function would better describe what this does. So it will |
||||
1932 | * search group of directories and return just the unique filenames, dis-regarding the extension. |
||||
1933 | * this allows us to match by name across sets that have different extensions |
||||
1934 | * |
||||
1935 | * @param array $smiley_sets array of smiley sets (end directory names) to search |
||||
1936 | * @return array of unique smiley names across one or many "sets" |
||||
1937 | */ |
||||
1938 | public function getAllPossibleFilenamesForTheSmileys($smiley_sets) |
||||
1939 | { |
||||
1940 | global $context, $modSettings; |
||||
1941 | |||||
1942 | $filenames = []; |
||||
1943 | $fileFunc = FileFunctions::instance(); |
||||
1944 | |||||
1945 | $smileys_dir = empty($modSettings['smileys_dir']) ? BOARDDIR . '/smileys' : $modSettings['smileys_dir']; |
||||
1946 | if (!$fileFunc->isDir($smileys_dir)) |
||||
1947 | { |
||||
1948 | return []; |
||||
1949 | } |
||||
1950 | |||||
1951 | foreach ($smiley_sets as $smiley_set) |
||||
1952 | { |
||||
1953 | $smiles = $fileFunc->listTree($context['smileys_dir'] . '/' . un_htmlspecialchars($smiley_set['path'])); |
||||
1954 | foreach ($smiles as $smile) |
||||
1955 | { |
||||
1956 | if (in_array($smile['filename'], ['.', '..', '.htaccess', 'index.php'], true)) |
||||
1957 | { |
||||
1958 | continue; |
||||
1959 | } |
||||
1960 | |||||
1961 | $key = strtolower(pathinfo($smile['filename'], PATHINFO_FILENAME)); |
||||
0 ignored issues
–
show
It seems like
pathinfo($smile['filenam...ller\PATHINFO_FILENAME) can also be of type array ; however, parameter $string of strtolower() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1962 | if (in_array($key, $filenames, true)) |
||||
1963 | { |
||||
1964 | continue; |
||||
1965 | } |
||||
1966 | |||||
1967 | if (!in_array(strtolower(pathinfo($smile['filename'], PATHINFO_EXTENSION)), $this->_smiley_types, true)) |
||||
1968 | { |
||||
1969 | continue; |
||||
1970 | } |
||||
1971 | |||||
1972 | $filenames[strtolower($key)] = [ |
||||
1973 | 'id' => Util::htmlspecialchars($key, ENT_COMPAT, 'UTF-8'), |
||||
1974 | 'selected' => false, |
||||
1975 | ]; |
||||
1976 | } |
||||
1977 | } |
||||
1978 | |||||
1979 | ksort($filenames); |
||||
1980 | |||||
1981 | return $filenames; |
||||
1982 | } |
||||
1983 | } |
||||
1984 |
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