elkarte /
Elkarte
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * Handles xml requests |
||
| 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 | * @version 2.0 dev |
||
| 11 | * |
||
| 12 | */ |
||
| 13 | |||
| 14 | namespace ElkArte\Controller; |
||
| 15 | |||
| 16 | use ElkArte\AbstractController; |
||
| 17 | use ElkArte\Action; |
||
| 18 | use ElkArte\AdminController\CoreFeatures; |
||
| 19 | use ElkArte\BoardsTree; |
||
| 20 | use ElkArte\Cache\Cache; |
||
| 21 | use ElkArte\EventManager; |
||
| 22 | use ElkArte\Exceptions\Exception; |
||
| 23 | use ElkArte\Languages\Txt; |
||
| 24 | use ElkArte\User; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Receives XMLhttp requests of various types such as |
||
| 28 | * jump to, message and group icons, core features, drag and drop ordering |
||
| 29 | */ |
||
| 30 | class Xml extends AbstractController |
||
| 31 | { |
||
| 32 | /** |
||
| 33 | * {@inheritDoc} |
||
| 34 | */ |
||
| 35 | public function trackStats($action = '') |
||
| 36 | { |
||
| 37 | return false; |
||
| 38 | } |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Main dispatcher for action=xmlhttp. |
||
| 42 | * |
||
| 43 | * @see \ElkArte\AbstractController::action_index() |
||
| 44 | */ |
||
| 45 | public function action_index() |
||
| 46 | { |
||
| 47 | theme()->getTemplates()->load('Xml'); |
||
| 48 | theme()->getLayers()->removeAll(); |
||
| 49 | |||
| 50 | $subActions = [ |
||
| 51 | 'jumpto' => ['controller' => $this, 'function' => 'action_jumpto'], |
||
| 52 | 'messageicons' => ['controller' => $this, 'function' => 'action_messageicons'], |
||
| 53 | 'groupicons' => ['controller' => $this, 'function' => 'action_groupicons'], |
||
| 54 | 'corefeatures' => ['controller' => $this, 'function' => 'action_corefeatures', 'permission' => 'admin_forum'], |
||
| 55 | 'profileorder' => ['controller' => $this, 'function' => 'action_profileorder', 'permission' => 'admin_forum'], |
||
| 56 | 'messageiconorder' => ['controller' => $this, 'function' => 'action_messageiconorder', 'permission' => 'admin_forum'], |
||
| 57 | 'smileyorder' => ['controller' => $this, 'function' => 'action_smileyorder', 'permission' => 'admin_forum'], |
||
| 58 | 'boardorder' => ['controller' => $this, 'function' => 'action_boardorder', 'permission' => 'manage_boards'], |
||
| 59 | 'parserorder' => ['controller' => $this, 'function' => 'action_parserorder', 'permission' => 'admin_forum'], |
||
| 60 | 'videoembed' => ['controller' => $this, 'function' => 'action_videoembed'], |
||
| 61 | ]; |
||
| 62 | |||
| 63 | // Easy adding of xml sub actions with integrate_sa_xmlhttp |
||
| 64 | $action = new Action('xmlhttp'); |
||
| 65 | $subAction = $action->initialize($subActions); |
||
| 66 | |||
| 67 | // Act a bit special for XML, probably never see it anyway :P |
||
| 68 | if (empty($subAction)) |
||
| 69 | { |
||
| 70 | throw new Exception('no_access', false); |
||
| 71 | } |
||
| 72 | |||
| 73 | // Off we go then, (it will check permissions) |
||
| 74 | $action->dispatch($subAction); |
||
| 75 | } |
||
| 76 | |||
| 77 | /** |
||
| 78 | * Get a list of boards and categories used for the jumpto dropdown. |
||
| 79 | */ |
||
| 80 | public function action_jumpto(): void |
||
| 81 | { |
||
| 82 | global $context; |
||
| 83 | |||
| 84 | // Find the boards/categories they can see. |
||
| 85 | require_once(SUBSDIR . '/Boards.subs.php'); |
||
| 86 | $boardListOptions = [ |
||
| 87 | 'selected_board' => $context['current_board'] ?? 0, |
||
| 88 | ]; |
||
| 89 | $context += getBoardList($boardListOptions); |
||
| 90 | |||
| 91 | // Make the board safe for display. |
||
| 92 | foreach ($context['categories'] as $id_cat => $cat) |
||
| 93 | { |
||
| 94 | $context['categories'][$id_cat]['name'] = un_htmlspecialchars(strip_tags($cat['name'])); |
||
| 95 | foreach ($cat['boards'] as $id_board => $board) |
||
| 96 | { |
||
| 97 | $context['categories'][$id_cat]['boards'][$id_board]['name'] = un_htmlspecialchars(strip_tags($board['name'])); |
||
| 98 | } |
||
| 99 | } |
||
| 100 | |||
| 101 | $context['sub_template'] = 'jump_to'; |
||
| 102 | } |
||
| 103 | |||
| 104 | /** |
||
| 105 | * Get the message icons available for a given board |
||
| 106 | */ |
||
| 107 | public function action_messageicons(): void |
||
| 108 | { |
||
| 109 | global $context, $board; |
||
| 110 | |||
| 111 | require_once(SUBSDIR . '/MessageIcons.subs.php'); |
||
| 112 | |||
| 113 | $context['icons'] = array_values(getMessageIcons($board)); |
||
| 114 | $context['sub_template'] = 'message_icons'; |
||
| 115 | } |
||
| 116 | |||
| 117 | /** |
||
| 118 | * Get the member group icons |
||
| 119 | */ |
||
| 120 | public function action_groupicons(): void |
||
| 121 | { |
||
| 122 | global $context, $settings; |
||
| 123 | |||
| 124 | // Only load images |
||
| 125 | $allowedTypes = ['jpeg', 'jpg', 'gif', 'png', 'bmp']; |
||
| 126 | $context['membergroup_icons'] = []; |
||
| 127 | $directory = $settings['theme_dir'] . '/images/group_icons'; |
||
| 128 | $icons = []; |
||
| 129 | |||
| 130 | // Get all the available member group icons |
||
| 131 | $files = new \FilesystemIterator($directory, \FilesystemIterator::SKIP_DOTS); |
||
| 132 | foreach ($files as $file) |
||
| 133 | { |
||
| 134 | if ($file->getFilename() === 'blank.png') |
||
| 135 | { |
||
| 136 | continue; |
||
| 137 | } |
||
| 138 | |||
| 139 | if (in_array(strtolower($file->getExtension()), $allowedTypes)) |
||
| 140 | { |
||
| 141 | $icons[] = [ |
||
| 142 | 'value' => $file->getFilename(), |
||
| 143 | 'name' => '', |
||
| 144 | 'url' => $settings['images_url'] . '/group_icons/' . $file->getFilename(), |
||
| 145 | 'is_last' => false, |
||
| 146 | ]; |
||
| 147 | } |
||
| 148 | } |
||
| 149 | |||
| 150 | $context['icons'] = array_values($icons); |
||
| 151 | $context['sub_template'] = 'message_icons'; |
||
| 152 | } |
||
| 153 | |||
| 154 | /** |
||
| 155 | * Turns on or off a core forum feature via ajax |
||
| 156 | */ |
||
| 157 | public function action_corefeatures(): void |
||
| 158 | { |
||
| 159 | global $context, $txt; |
||
| 160 | |||
| 161 | $context['xml_data'] = []; |
||
| 162 | |||
| 163 | // Just in case, maybe we don't need it |
||
| 164 | Txt::load('Errors'); |
||
| 165 | Txt::load('Admin'); |
||
| 166 | |||
| 167 | // We need (at least) this to ensure that mod files are included |
||
| 168 | call_integration_include_hook('integrate_admin_include'); |
||
| 169 | |||
| 170 | $errors = []; |
||
| 171 | $returns = []; |
||
| 172 | $tokens = []; |
||
| 173 | $feature_title = ''; |
||
| 174 | |||
| 175 | // You have to be allowed to do this of course |
||
| 176 | $validation = validateSession(); |
||
| 177 | if ($validation === true) |
||
| 178 | { |
||
| 179 | $controller = new CoreFeatures(new EventManager()); |
||
| 180 | $controller->setUser(User::$info); |
||
| 181 | $controller->pre_dispatch(); |
||
| 182 | $result = $controller->action_index(); |
||
| 183 | |||
| 184 | // Load up the core features of the system |
||
| 185 | if ($result === true) |
||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
| 186 | { |
||
| 187 | $id = $this->_req->getPost('feature_id', 'trim', ''); |
||
| 188 | |||
| 189 | // The feature being enabled does exist, no messing about |
||
| 190 | if (!empty($id) && isset($context['features'][$id])) |
||
| 191 | { |
||
| 192 | $feature = $context['features'][$id]; |
||
| 193 | $feature_id = 'feature_' . $id; |
||
| 194 | $feature_title = (!empty($this->_req->post->{$feature_id}) && $feature['url'] ? '<a href="' . $feature['url'] . '">' . $feature['title'] . '</a>' : $feature['title']); |
||
| 195 | $returns[] = [ |
||
| 196 | 'value' => $feature_title, |
||
| 197 | ]; |
||
| 198 | |||
| 199 | createToken('admin-core', 'post'); |
||
| 200 | $tokens = [ |
||
| 201 | [ |
||
| 202 | 'value' => $context['admin-core_token'], |
||
| 203 | 'attributes' => ['type' => 'token_var'], |
||
| 204 | ], |
||
| 205 | [ |
||
| 206 | 'value' => $context['admin-core_token_var'], |
||
| 207 | 'attributes' => ['type' => 'token'], |
||
| 208 | ], |
||
| 209 | ]; |
||
| 210 | } |
||
| 211 | else |
||
| 212 | { |
||
| 213 | $errors[] = ['value' => $txt['feature_no_exists']]; |
||
| 214 | } |
||
| 215 | } |
||
| 216 | // Some problem loading in the core feature set |
||
| 217 | else |
||
| 218 | { |
||
| 219 | $errors[] = ['value' => $txt[$result]]; |
||
| 220 | } |
||
| 221 | } |
||
| 222 | // Failed session validation I'm afraid |
||
| 223 | else |
||
| 224 | { |
||
| 225 | $errors[] = ['value' => $txt[$validation] ?? $txt['error_occurred']]; |
||
| 226 | } |
||
| 227 | |||
| 228 | // Return the response to the calling program |
||
| 229 | $context['sub_template'] = 'generic_xml'; |
||
| 230 | theme()->addJavascriptVar(['core_settings_generic_error' => $txt['core_settings_generic_error']], true); |
||
| 231 | |||
| 232 | $message = str_replace('{core_feature}', $feature_title, !empty($feature_id) && !empty($this->_req->post->{$feature_id}) ? $txt['core_settings_activation_message'] : $txt['core_settings_deactivation_message']); |
||
| 233 | $context['xml_data'] = [ |
||
| 234 | 'corefeatures' => [ |
||
| 235 | 'identifier' => 'corefeature', |
||
| 236 | 'children' => $returns, |
||
| 237 | ], |
||
| 238 | 'messages' => [ |
||
| 239 | 'identifier' => 'message', |
||
| 240 | 'children' => [[ |
||
| 241 | 'value' => $message |
||
| 242 | ]], |
||
| 243 | ], |
||
| 244 | 'tokens' => [ |
||
| 245 | 'identifier' => 'token', |
||
| 246 | 'children' => $tokens, |
||
| 247 | ], |
||
| 248 | 'errors' => [ |
||
| 249 | 'identifier' => 'error', |
||
| 250 | 'children' => $errors, |
||
| 251 | ], |
||
| 252 | ]; |
||
| 253 | } |
||
| 254 | |||
| 255 | /** |
||
| 256 | * Reorders the custom profile fields from a drag/drop event |
||
| 257 | */ |
||
| 258 | public function action_profileorder(): void |
||
| 259 | { |
||
| 260 | global $context, $txt; |
||
| 261 | |||
| 262 | // Start off with nothing |
||
| 263 | $context['xml_data'] = []; |
||
| 264 | $errors = []; |
||
| 265 | $order = []; |
||
| 266 | |||
| 267 | // Chances are |
||
| 268 | Txt::load('Errors'); |
||
| 269 | Txt::load('ManageSettings'); |
||
| 270 | require_once(SUBSDIR . '/ManageFeatures.subs.php'); |
||
| 271 | |||
| 272 | // You have to be allowed to do this |
||
| 273 | $validation_token = validateToken('admin-sort', 'post', false, false); |
||
| 274 | $validation_session = validateSession(); |
||
| 275 | |||
| 276 | if ($validation_session === true && $validation_token === true) |
||
| 277 | { |
||
| 278 | // No questions that we are reordering |
||
| 279 | if ($this->_req->getPost('order', 'trim', '') === 'reorder') |
||
| 280 | { |
||
| 281 | $view_order = 1; |
||
| 282 | $replace = ''; |
||
| 283 | |||
| 284 | // The field ids arrive in 1-n view order ... |
||
| 285 | foreach ($this->_req->post->list_custom_profile_fields as $id) |
||
| 286 | { |
||
| 287 | $id = (int) $id; |
||
| 288 | $replace .= ' |
||
| 289 | WHEN id_field = ' . $id . ' THEN ' . $view_order++; |
||
| 290 | } |
||
| 291 | |||
| 292 | // With the replace set |
||
| 293 | if (!empty($replace)) |
||
| 294 | { |
||
| 295 | updateProfileFieldOrder($replace); |
||
| 296 | } |
||
| 297 | else |
||
| 298 | { |
||
| 299 | $errors[] = ['value' => $txt['no_sortable_items']]; |
||
| 300 | } |
||
| 301 | } |
||
| 302 | |||
| 303 | $order[] = [ |
||
| 304 | 'value' => $txt['custom_profile_reordered'], |
||
| 305 | ]; |
||
| 306 | } |
||
| 307 | // Failed validation, tough to be you |
||
| 308 | else |
||
| 309 | { |
||
| 310 | if ($validation_session !== true) |
||
| 311 | { |
||
| 312 | $errors[] = ['value' => $txt['session_verify_fail']]; |
||
| 313 | } |
||
| 314 | |||
| 315 | if ($validation_token === false) |
||
| 316 | { |
||
| 317 | $errors[] = ['value' => $txt['token_verify_fail']]; |
||
| 318 | } |
||
| 319 | } |
||
| 320 | |||
| 321 | // New generic token for use |
||
| 322 | createToken('admin-sort', 'post'); |
||
| 323 | $tokens = [ |
||
| 324 | [ |
||
| 325 | 'value' => $context['admin-sort_token'], |
||
| 326 | 'attributes' => ['type' => 'token'], |
||
| 327 | ], |
||
| 328 | [ |
||
| 329 | 'value' => $context['admin-sort_token_var'], |
||
| 330 | 'attributes' => ['type' => 'token_var'], |
||
| 331 | ], |
||
| 332 | ]; |
||
| 333 | |||
| 334 | // Return the response |
||
| 335 | $context['sub_template'] = 'generic_xml'; |
||
| 336 | $context['xml_data'] = [ |
||
| 337 | 'orders' => [ |
||
| 338 | 'identifier' => 'order', |
||
| 339 | 'children' => $order, |
||
| 340 | ], |
||
| 341 | 'tokens' => [ |
||
| 342 | 'identifier' => 'token', |
||
| 343 | 'children' => $tokens, |
||
| 344 | ], |
||
| 345 | 'errors' => [ |
||
| 346 | 'identifier' => 'error', |
||
| 347 | 'children' => $errors, |
||
| 348 | ], |
||
| 349 | ]; |
||
| 350 | } |
||
| 351 | |||
| 352 | /** |
||
| 353 | * Reorders the boards in response to an ajax sortable request |
||
| 354 | */ |
||
| 355 | public function action_boardorder(): void |
||
| 356 | { |
||
| 357 | global $context, $txt; |
||
| 358 | |||
| 359 | // Start off clean |
||
| 360 | $context['xml_data'] = []; |
||
| 361 | $errors = []; |
||
| 362 | $order = []; |
||
| 363 | $board_tree = []; |
||
| 364 | |||
| 365 | // Chances are we will need these |
||
| 366 | Txt::load('Errors'); |
||
| 367 | Txt::load('ManageBoards'); |
||
| 368 | require_once(SUBSDIR . '/ManageFeatures.subs.php'); |
||
| 369 | require_once(SUBSDIR . '/Boards.subs.php'); |
||
| 370 | |||
| 371 | // Validating that you can do this is always a good idea |
||
| 372 | $validation_token = validateToken('admin-sort', 'post', false, false); |
||
| 373 | $validation_session = validateSession(); |
||
| 374 | |||
| 375 | if ($validation_session === true && $validation_token === true) |
||
| 376 | { |
||
| 377 | // No question that we are doing some board reordering |
||
| 378 | if ($this->_req->getPost('order', 'trim', '') === 'reorder' |
||
| 379 | && isset($this->_req->post->moved)) |
||
| 380 | { |
||
| 381 | $list_order = 0; |
||
| 382 | $moved_key = 0; |
||
| 383 | |||
| 384 | // What board was drag and dropped? |
||
| 385 | [, $board_moved,] = explode(',', $this->_req->post->moved); |
||
| 386 | $board_moved = (int) $board_moved; |
||
| 387 | |||
| 388 | // The board ids arrive in 1-n view order ... |
||
| 389 | foreach ($this->_req->post->cbp as $id) |
||
| 390 | { |
||
| 391 | [$category, $board, $childof] = explode(',', $id); |
||
| 392 | |||
| 393 | if ($board == -1) |
||
| 394 | { |
||
| 395 | continue; |
||
| 396 | } |
||
| 397 | |||
| 398 | $board_tree[] = [ |
||
| 399 | 'category' => $category, |
||
| 400 | 'parent' => $childof, |
||
| 401 | 'order' => $list_order, |
||
| 402 | 'id' => $board, |
||
| 403 | ]; |
||
| 404 | |||
| 405 | // Keep track of where the moved board is in the sort stack |
||
| 406 | if ($board == $board_moved) |
||
| 407 | { |
||
| 408 | $moved_key = $list_order; |
||
| 409 | } |
||
| 410 | |||
| 411 | $list_order++; |
||
| 412 | } |
||
| 413 | |||
| 414 | // Look behind for the previous board and previous sibling |
||
| 415 | $board_previous = (isset($board_tree[$moved_key - 1]) && $board_tree[$moved_key]['category'] === $board_tree[$moved_key - 1]['category']) ? $board_tree[$moved_key - 1] : null; |
||
| 416 | $board_previous_sibling = null; |
||
| 417 | for ($i = $moved_key - 1; $i >= 0; $i--) |
||
| 418 | { |
||
| 419 | // Sibling must have the same category and same parent tree |
||
| 420 | if ($board_tree[$moved_key]['category'] === $board_tree[$i]['category']) |
||
| 421 | { |
||
| 422 | if ($board_tree[$moved_key]['parent'] === $board_tree[$i]['parent']) |
||
| 423 | { |
||
| 424 | $board_previous_sibling = $board_tree[$i]; |
||
| 425 | break; |
||
| 426 | } |
||
| 427 | // Don't go to another parent tree |
||
| 428 | elseif ($board_tree[$i]['parent'] == 0) |
||
| 429 | { |
||
| 430 | break; |
||
| 431 | } |
||
| 432 | } |
||
| 433 | // Don't go to another category |
||
| 434 | else |
||
| 435 | { |
||
| 436 | break; |
||
| 437 | } |
||
| 438 | } |
||
| 439 | |||
| 440 | // Retrieve the current saved state |
||
| 441 | $boardTree = new BoardsTree(database()); |
||
| 442 | $board_current = $boardTree->getBoardById($board_moved); |
||
| 443 | $board_new = $board_tree[$moved_key]; |
||
| 444 | |||
| 445 | // Dropped on a sibling node, move after that |
||
| 446 | if (isset($board_previous_sibling)) |
||
| 447 | { |
||
| 448 | $boardOptions = [ |
||
| 449 | 'move_to' => 'after', |
||
| 450 | 'target_board' => $board_previous_sibling['id'], |
||
| 451 | ]; |
||
| 452 | $order[] = ['value' => $board_current['name'] . ' ' . $txt['mboards_order_after'] . ' ' . $boardTree->getBoardById($board_previous_sibling['id'])['name']]; |
||
| 453 | } |
||
| 454 | // No sibling, maybe a new child |
||
| 455 | elseif (isset($board_previous)) |
||
| 456 | { |
||
| 457 | $boardOptions = [ |
||
| 458 | 'move_to' => 'child', |
||
| 459 | 'target_board' => $board_previous['id'], |
||
| 460 | 'move_first_child' => true, |
||
| 461 | ]; |
||
| 462 | $order[] = ['value' => $board_current['name'] . ' ' . $txt['mboards_order_child_of'] . ' ' . $boardTree->getBoardById($board_previous['id'])['name']]; |
||
| 463 | } |
||
| 464 | // Nothing before this board at all, move to the top of the cat |
||
| 465 | else |
||
| 466 | { |
||
| 467 | $boardOptions = [ |
||
| 468 | 'move_to' => 'top', |
||
| 469 | 'target_category' => $board_new['category'], |
||
| 470 | 'move_first_child' => true, |
||
| 471 | ]; |
||
| 472 | $order[] = ['value' => $board_current['name'] . ' ' . $txt['mboards_order_in_category'] . ' ' . $boardTree->getCategoryNodeById($board_new['category'])['node']['name']]; |
||
| 473 | } |
||
| 474 | |||
| 475 | // If we have figured out what to do |
||
| 476 | if (!empty($boardOptions)) |
||
| 477 | { |
||
| 478 | modifyBoard($board_moved, $boardOptions); |
||
| 479 | } |
||
| 480 | else |
||
| 481 | { |
||
| 482 | $errors[] = ['value' => $txt['mboards_board_error']]; |
||
| 483 | } |
||
| 484 | } |
||
| 485 | } |
||
| 486 | // Failed validation, extra work for you I'm afraid |
||
| 487 | else |
||
| 488 | { |
||
| 489 | if ($validation_session !== true) |
||
| 490 | { |
||
| 491 | $errors[] = ['value' => $txt['session_verify_fail']]; |
||
| 492 | } |
||
| 493 | |||
| 494 | if ($validation_token === false) |
||
| 495 | { |
||
| 496 | $errors[] = ['value' => $txt['token_verify_fail']]; |
||
| 497 | } |
||
| 498 | } |
||
| 499 | |||
| 500 | // New generic token for use |
||
| 501 | createToken('admin-sort', 'post'); |
||
| 502 | $tokens = [ |
||
| 503 | [ |
||
| 504 | 'value' => $context['admin-sort_token'], |
||
| 505 | 'attributes' => ['type' => 'token'], |
||
| 506 | ], |
||
| 507 | [ |
||
| 508 | 'value' => $context['admin-sort_token_var'], |
||
| 509 | 'attributes' => ['type' => 'token_var'], |
||
| 510 | ], |
||
| 511 | ]; |
||
| 512 | |||
| 513 | // Return the response |
||
| 514 | $context['sub_template'] = 'generic_xml'; |
||
| 515 | $context['xml_data'] = [ |
||
| 516 | 'orders' => [ |
||
| 517 | 'identifier' => 'order', |
||
| 518 | 'children' => $order, |
||
| 519 | ], |
||
| 520 | 'tokens' => [ |
||
| 521 | 'identifier' => 'token', |
||
| 522 | 'children' => $tokens, |
||
| 523 | ], |
||
| 524 | 'errors' => [ |
||
| 525 | 'identifier' => 'error', |
||
| 526 | 'children' => $errors, |
||
| 527 | ], |
||
| 528 | ]; |
||
| 529 | } |
||
| 530 | |||
| 531 | /** |
||
| 532 | * Reorders the smileys from a drag/drop event |
||
| 533 | * |
||
| 534 | * What it does: |
||
| 535 | * |
||
| 536 | * - Will move them from post to popup location and visa-versa |
||
| 537 | * - Will move them to new rows |
||
| 538 | */ |
||
| 539 | public function action_smileyorder(): void |
||
| 540 | { |
||
| 541 | global $context, $txt; |
||
| 542 | |||
| 543 | // Start off with an empty response |
||
| 544 | $context['xml_data'] = []; |
||
| 545 | $errors = []; |
||
| 546 | $order = []; |
||
| 547 | |||
| 548 | Txt::load('Errors'); |
||
| 549 | Txt::load('ManageSmileys'); |
||
| 550 | require_once(SUBSDIR . '/Smileys.subs.php'); |
||
| 551 | |||
| 552 | // You have to be allowed to do this |
||
| 553 | $validation_token = validateToken('admin-sort', 'post', false, false); |
||
| 554 | $validation_session = validateSession(); |
||
| 555 | |||
| 556 | if ($validation_session === true && $validation_token === true) |
||
| 557 | { |
||
| 558 | // Valid posting |
||
| 559 | if ($this->_req->getPost('order', 'trim', '') === 'reorder') |
||
| 560 | { |
||
| 561 | // Get the details on the moved smile |
||
| 562 | [, $smile_moved] = explode('_', $this->_req->post->moved); |
||
| 563 | $smile_moved = (int) $smile_moved; |
||
| 564 | $smile_moved_details = getSmiley($smile_moved); |
||
|
0 ignored issues
–
show
|
|||
| 565 | |||
| 566 | // Check if we moved rows or locations |
||
| 567 | $smile_received_location = null; |
||
| 568 | $smile_received_row = null; |
||
| 569 | if (!empty($this->_req->post->received)) |
||
| 570 | { |
||
| 571 | $displayTypes = [ |
||
| 572 | 'postform' => 0, |
||
| 573 | 'popup' => 2 |
||
| 574 | ]; |
||
| 575 | [$smile_received_location, $smile_received_row] = explode('|', $this->_req->post->received); |
||
| 576 | $smile_received_location = $displayTypes[substr($smile_received_location, 7)]; |
||
| 577 | } |
||
| 578 | |||
| 579 | // If these are not set, we are kind of lost :P |
||
| 580 | if (isset($smile_received_location, $smile_received_row)) |
||
| 581 | { |
||
| 582 | // Read the new ordering, remember where the moved smiley is in the stack |
||
| 583 | $list_order = 0; |
||
| 584 | $moved_key = 0; |
||
| 585 | $smiley_tree = []; |
||
| 586 | |||
| 587 | foreach ($this->_req->post->smile as $smile_id) |
||
| 588 | { |
||
| 589 | $smiley_tree[] = $smile_id; |
||
| 590 | |||
| 591 | // Keep track of where the moved smiley is in the sort stack |
||
| 592 | if ($smile_id == $smile_moved) |
||
| 593 | { |
||
| 594 | $moved_key = $list_order; |
||
| 595 | } |
||
| 596 | |||
| 597 | $list_order++; |
||
| 598 | } |
||
| 599 | |||
| 600 | // Now get the updated row, location, order |
||
| 601 | $smiley = []; |
||
| 602 | $smiley['row'] = $smile_received_row; |
||
| 603 | $smiley['location'] = $smile_received_location; |
||
| 604 | $smiley['order'] = -1; |
||
| 605 | |||
| 606 | // If the node after the drop zone is in the same row/container, we use its position |
||
| 607 | if (isset($smiley_tree[$moved_key + 1], $smiley_tree[$moved_key - 1])) |
||
| 608 | { |
||
| 609 | $possible_after = getSmiley($smiley_tree[$moved_key - 1]); |
||
| 610 | if ($possible_after['row'] == $smiley['row'] && $possible_after['location'] == $smiley['location']) |
||
| 611 | { |
||
| 612 | $smiley = getSmileyPosition($smiley['location'], $smiley_tree[$moved_key - 1]); |
||
| 613 | } |
||
| 614 | } |
||
| 615 | |||
| 616 | // Empty means getSmileyPosition failed and so do we |
||
| 617 | if (!empty($smiley)) |
||
| 618 | { |
||
| 619 | moveSmileyPosition($smiley, $smile_moved); |
||
| 620 | |||
| 621 | // Done with the move, now we clean up across the containers/rows |
||
| 622 | $smileys = getSmileys(); |
||
| 623 | foreach (array_keys($smileys) as $location) |
||
| 624 | { |
||
| 625 | foreach ($smileys[$location]['rows'] as $id => $smiley_row) |
||
| 626 | { |
||
| 627 | // Fix empty rows if any. |
||
| 628 | if ($id != $smiley_row[0]['row']) |
||
| 629 | { |
||
| 630 | updateSmileyRow($id, $smiley_row[0]['row'], $location); |
||
| 631 | |||
| 632 | // Only change the first row value of the first smiley. |
||
| 633 | $smileys[$location]['rows'][$id][0]['row'] = $id; |
||
| 634 | } |
||
| 635 | |||
| 636 | // Make sure the smiley order is always sequential. |
||
| 637 | foreach ($smiley_row as $order_id => $smiley) |
||
| 638 | { |
||
| 639 | if ($order_id != $smiley['order']) |
||
| 640 | { |
||
| 641 | updateSmileyOrder($smiley['id'], $order_id); |
||
| 642 | } |
||
| 643 | } |
||
| 644 | } |
||
| 645 | } |
||
| 646 | |||
| 647 | // Clear the cache, its stale now |
||
| 648 | Cache::instance()->remove('parsing_smileys'); |
||
| 649 | Cache::instance()->remove('posting_smileys'); |
||
| 650 | $order[] = ['value' => $txt['smileys_moved_done']]; |
||
| 651 | } |
||
| 652 | } |
||
| 653 | } |
||
| 654 | else |
||
| 655 | { |
||
| 656 | $errors[] = ['value' => $txt['smileys_moved_fail']]; |
||
| 657 | } |
||
| 658 | } |
||
| 659 | // Failed validation :'( |
||
| 660 | else |
||
| 661 | { |
||
| 662 | if ($validation_session !== true) |
||
| 663 | { |
||
| 664 | $errors[] = ['value' => $txt['session_verify_fail']]; |
||
| 665 | } |
||
| 666 | |||
| 667 | if ($validation_token === false) |
||
| 668 | { |
||
| 669 | $errors[] = ['value' => $txt['token_verify_fail']]; |
||
| 670 | } |
||
| 671 | } |
||
| 672 | |||
| 673 | // New generic token for use |
||
| 674 | createToken('admin-sort', 'post'); |
||
| 675 | $tokens = [ |
||
| 676 | [ |
||
| 677 | 'value' => $context['admin-sort_token'], |
||
| 678 | 'attributes' => ['type' => 'token'], |
||
| 679 | ], |
||
| 680 | [ |
||
| 681 | 'value' => $context['admin-sort_token_var'], |
||
| 682 | 'attributes' => ['type' => 'token_var'], |
||
| 683 | ], |
||
| 684 | ]; |
||
| 685 | |||
| 686 | // Return the response, whatever it is |
||
| 687 | $context['sub_template'] = 'generic_xml'; |
||
| 688 | $context['xml_data'] = [ |
||
| 689 | 'orders' => [ |
||
| 690 | 'identifier' => 'order', |
||
| 691 | 'children' => $order, |
||
| 692 | ], |
||
| 693 | 'tokens' => [ |
||
| 694 | 'identifier' => 'token', |
||
| 695 | 'children' => $tokens, |
||
| 696 | ], |
||
| 697 | 'errors' => [ |
||
| 698 | 'identifier' => 'error', |
||
| 699 | 'children' => $errors, |
||
| 700 | ], |
||
| 701 | ]; |
||
| 702 | } |
||
| 703 | |||
| 704 | /** |
||
| 705 | * Reorders the PBE parsers or filters from a drag/drop event |
||
| 706 | */ |
||
| 707 | public function action_parserorder(): void |
||
| 708 | { |
||
| 709 | global $context, $txt; |
||
| 710 | |||
| 711 | // Start off with nothing |
||
| 712 | $context['xml_data'] = []; |
||
| 713 | $errors = []; |
||
| 714 | $order = []; |
||
| 715 | |||
| 716 | // Chances are |
||
| 717 | Txt::load('Errors'); |
||
| 718 | Txt::load('Maillist'); |
||
| 719 | require_once(SUBSDIR . '/Maillist.subs.php'); |
||
| 720 | |||
| 721 | // You have to be allowed to do this |
||
| 722 | $validation_token = validateToken('admin-sort', 'post', false, false); |
||
| 723 | $validation_session = validateSession(); |
||
| 724 | |||
| 725 | if ($validation_session === true && $validation_token === true) |
||
| 726 | { |
||
| 727 | // No questions that we are reordering |
||
| 728 | if (isset($this->_req->post->order, $this->_req->post->list_sort_email_fp) && $this->_req->post->order === 'reorder') |
||
| 729 | { |
||
| 730 | $filters = []; |
||
| 731 | $filter_order = 1; |
||
| 732 | $replace = ''; |
||
| 733 | |||
| 734 | // The field ids arrive in 1-n view order ... |
||
| 735 | foreach ($this->_req->post->list_sort_email_fp as $id) |
||
| 736 | { |
||
| 737 | $filters[] = (int) $id; |
||
| 738 | $replace .= ' |
||
| 739 | WHEN id_filter = ' . $id . ' THEN ' . $filter_order++; |
||
| 740 | } |
||
| 741 | |||
| 742 | // With the replace set |
||
| 743 | if (!empty($replace)) |
||
| 744 | { |
||
| 745 | updateParserFilterOrder($replace, $filters); |
||
| 746 | } |
||
| 747 | else |
||
| 748 | { |
||
| 749 | $errors[] = ['value' => $txt['no_sortable_items']]; |
||
| 750 | } |
||
| 751 | } |
||
| 752 | |||
| 753 | $order[] = [ |
||
| 754 | 'value' => $txt['parser_reordered'], |
||
| 755 | ]; |
||
| 756 | } |
||
| 757 | // Failed validation, tough to be you |
||
| 758 | else |
||
| 759 | { |
||
| 760 | if ($validation_session !== true) |
||
| 761 | { |
||
| 762 | $errors[] = ['value' => $txt['session_verify_fail']]; |
||
| 763 | } |
||
| 764 | |||
| 765 | if ($validation_token === false) |
||
| 766 | { |
||
| 767 | $errors[] = ['value' => $txt['token_verify_fail']]; |
||
| 768 | } |
||
| 769 | } |
||
| 770 | |||
| 771 | // New generic token for use |
||
| 772 | createToken('admin-sort', 'post'); |
||
| 773 | $tokens = [ |
||
| 774 | [ |
||
| 775 | 'value' => $context['admin-sort_token'], |
||
| 776 | 'attributes' => ['type' => 'token'], |
||
| 777 | ], |
||
| 778 | [ |
||
| 779 | 'value' => $context['admin-sort_token_var'], |
||
| 780 | 'attributes' => ['type' => 'token_var'], |
||
| 781 | ], |
||
| 782 | ]; |
||
| 783 | |||
| 784 | // Return the response |
||
| 785 | $context['sub_template'] = 'generic_xml'; |
||
| 786 | $context['xml_data'] = [ |
||
| 787 | 'orders' => [ |
||
| 788 | 'identifier' => 'order', |
||
| 789 | 'children' => $order, |
||
| 790 | ], |
||
| 791 | 'tokens' => [ |
||
| 792 | 'identifier' => 'token', |
||
| 793 | 'children' => $tokens, |
||
| 794 | ], |
||
| 795 | 'errors' => [ |
||
| 796 | 'identifier' => 'error', |
||
| 797 | 'children' => $errors, |
||
| 798 | ], |
||
| 799 | ]; |
||
| 800 | } |
||
| 801 | |||
| 802 | /** |
||
| 803 | * Reorders the message icons from a drag/drop event |
||
| 804 | */ |
||
| 805 | public function action_messageiconorder(): void |
||
| 806 | { |
||
| 807 | global $context, $txt; |
||
| 808 | |||
| 809 | // Initialize |
||
| 810 | $context['xml_data'] = []; |
||
| 811 | $errors = []; |
||
| 812 | $order = []; |
||
| 813 | |||
| 814 | // Seems these will be needed |
||
| 815 | Txt::load('Errors'); |
||
| 816 | Txt::load('ManageSmileys'); |
||
| 817 | require_once(SUBSDIR . '/MessageIcons.subs.php'); |
||
| 818 | |||
| 819 | // You have to be allowed to do this |
||
| 820 | $validation_token = validateToken('admin-sort', 'post', false, false); |
||
| 821 | $validation_session = validateSession(); |
||
| 822 | |||
| 823 | if ($validation_session === true && $validation_token === true) |
||
| 824 | { |
||
| 825 | // No questions that we are reordering |
||
| 826 | if ($this->_req->getPost('order', 'trim', '') === 'reorder') |
||
| 827 | { |
||
| 828 | // Get the current list of icons. |
||
| 829 | $message_icons = fetchMessageIconsDetails(); |
||
| 830 | |||
| 831 | $view_order = 0; |
||
| 832 | $iconInsert = []; |
||
| 833 | |||
| 834 | // The field ids arrive in 1-n view order, so we simply build an update array |
||
| 835 | foreach ($this->_req->post->list_message_icon_list as $id) |
||
| 836 | { |
||
| 837 | $iconInsert[] = [$id, $message_icons[$id]['board_id'], $message_icons[$id]['title'], $message_icons[$id]['filename'], $view_order]; |
||
| 838 | $view_order++; |
||
| 839 | } |
||
| 840 | |||
| 841 | // With the replace set |
||
| 842 | if (!empty($iconInsert)) |
||
| 843 | { |
||
| 844 | updateMessageIcon($iconInsert); |
||
| 845 | } |
||
| 846 | else |
||
| 847 | { |
||
| 848 | $errors[] = ['value' => $txt['no_sortable_items']]; |
||
| 849 | } |
||
| 850 | } |
||
| 851 | |||
| 852 | $order[] = [ |
||
| 853 | 'value' => $txt['icons_reordered'], |
||
| 854 | ]; |
||
| 855 | } |
||
| 856 | // Failed validation, tough to be you |
||
| 857 | else |
||
| 858 | { |
||
| 859 | if ($validation_session !== true) |
||
| 860 | { |
||
| 861 | $errors[] = ['value' => $txt['session_verify_fail']]; |
||
| 862 | } |
||
| 863 | |||
| 864 | if ($validation_token === false) |
||
| 865 | { |
||
| 866 | $errors[] = ['value' => $txt['token_verify_fail']]; |
||
| 867 | } |
||
| 868 | } |
||
| 869 | |||
| 870 | // New generic token for use |
||
| 871 | createToken('admin-sort', 'post'); |
||
| 872 | $tokens = [ |
||
| 873 | [ |
||
| 874 | 'value' => $context['admin-sort_token'], |
||
| 875 | 'attributes' => ['type' => 'token'], |
||
| 876 | ], |
||
| 877 | [ |
||
| 878 | 'value' => $context['admin-sort_token_var'], |
||
| 879 | 'attributes' => ['type' => 'token_var'], |
||
| 880 | ], |
||
| 881 | ]; |
||
| 882 | |||
| 883 | // Return the response |
||
| 884 | $context['sub_template'] = 'generic_xml'; |
||
| 885 | $context['xml_data'] = [ |
||
| 886 | 'orders' => [ |
||
| 887 | 'identifier' => 'order', |
||
| 888 | 'children' => $order, |
||
| 889 | ], |
||
| 890 | 'tokens' => [ |
||
| 891 | 'identifier' => 'token', |
||
| 892 | 'children' => $tokens, |
||
| 893 | ], |
||
| 894 | 'errors' => [ |
||
| 895 | 'identifier' => 'error', |
||
| 896 | 'children' => $errors, |
||
| 897 | ], |
||
| 898 | ]; |
||
| 899 | } |
||
| 900 | |||
| 901 | /** |
||
| 902 | * An experimental function to fetch a videos embed code when JS will throw CORS errors |
||
| 903 | */ |
||
| 904 | public function action_videoembed(): void |
||
| 905 | { |
||
| 906 | global $context; |
||
| 907 | |||
| 908 | theme()->getLayers()->removeAll(); |
||
| 909 | theme()->getTemplates()->load('Json'); |
||
| 910 | |||
| 911 | $context['sub_template'] = 'send_json_raw'; |
||
| 912 | $context['json_data'] = json_encode([]); |
||
| 913 | |||
| 914 | $videoID = $this->_req->getQuery('videoid', 'trim'); |
||
| 915 | $site = $this->_req->getQuery('site', 'trim'); |
||
| 916 | |||
| 917 | if (checkSession('get', '', false)) |
||
| 918 | { |
||
| 919 | $context['json_data'] = json_encode(['session' => 'failed']); |
||
| 920 | $videoID = 0; |
||
| 921 | } |
||
| 922 | |||
| 923 | // Right now only one site, but a fetch based on site is the idea |
||
| 924 | if (!empty($videoID) && !empty($site)) |
||
| 925 | { |
||
| 926 | require_once(SUBSDIR . '/Package.subs.php'); |
||
| 927 | $data = fetch_web_data('https://api.x.com/1.1/statuses/oembed.json?id=' . $videoID); |
||
| 928 | if ($data !== false) |
||
| 929 | { |
||
| 930 | $context['json_data'] = trim($data); |
||
| 931 | } |
||
| 932 | } |
||
| 933 | } |
||
| 934 | } |
||
| 935 |