Issues (1686)

sources/ElkArte/Controller/Xml.php (2 issues)

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 = array(
51
			'jumpto' => array('controller' => $this, 'function' => 'action_jumpto'),
52
			'messageicons' => array('controller' => $this, 'function' => 'action_messageicons'),
53
			'groupicons' => array('controller' => $this, 'function' => 'action_groupicons'),
54
			'corefeatures' => array('controller' => $this, 'function' => 'action_corefeatures', 'permission' => 'admin_forum'),
55
			'profileorder' => array('controller' => $this, 'function' => 'action_profileorder', 'permission' => 'admin_forum'),
56
			'messageiconorder' => array('controller' => $this, 'function' => 'action_messageiconorder', 'permission' => 'admin_forum'),
57
			'smileyorder' => array('controller' => $this, 'function' => 'action_smileyorder', 'permission' => 'admin_forum'),
58
			'boardorder' => array('controller' => $this, 'function' => 'action_boardorder', 'permission' => 'manage_boards'),
59
			'parserorder' => array('controller' => $this, 'function' => 'action_parserorder', 'permission' => 'admin_forum'),
60
			'videoembed' => array('controller' => $this, 'function' => 'action_videoembed'),
61
		);
62
63
		// Easy adding of xml sub actions with integrate_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()
81
	{
82
		global $context;
83
84
		// Find the boards/categories they can see.
85
		require_once(SUBSDIR . '/Boards.subs.php');
86
		$boardListOptions = array(
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()
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()
121
	{
122
		global $context, $settings;
123
124
		// Only load images
125
		$allowedTypes = array('jpeg', 'jpg', 'gif', 'png', 'bmp');
126
		$context['membergroup_icons'] = array();
127
		$directory = $settings['theme_dir'] . '/images/group_icons';
128
		$icons = array();
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[] = array(
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()
158
	{
159
		global $context, $txt;
160
161
		$context['xml_data'] = array();
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 = array();
171
		$returns = array();
172
		$tokens = array();
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
The condition $result === true is always true.
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[] = array(
196
						'value' => $feature_title,
197
					);
198
199
					createToken('admin-core', 'post');
200
					$tokens = array(
201
						array(
202
							'value' => $context['admin-core_token'],
203
							'attributes' => array('type' => 'token_var'),
204
						),
205
						array(
206
							'value' => $context['admin-core_token_var'],
207
							'attributes' => array('type' => 'token'),
208
						),
209
					);
210
				}
211
				else
212
				{
213
					$errors[] = array('value' => $txt['feature_no_exists']);
214
				}
215
			}
216
			// Some problem loading in the core feature set
217
			else
218
			{
219
				$errors[] = array('value' => $txt[$result]);
220
			}
221
		}
222
		// Failed session validation I'm afraid
223
		else
224
		{
225
			$errors[] = array('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(array('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'] = array(
234
			'corefeatures' => array(
235
				'identifier' => 'corefeature',
236
				'children' => $returns,
237
			),
238
			'messages' => array(
239
				'identifier' => 'message',
240
				'children' => array(array(
241
					'value' => $message
242
				)),
243
			),
244
			'tokens' => array(
245
				'identifier' => 'token',
246
				'children' => $tokens,
247
			),
248
			'errors' => array(
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()
259
	{
260
		global $context, $txt;
261
262
		// Start off with nothing
263
		$context['xml_data'] = array();
264
		$errors = array();
265
		$order = array();
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[] = array('value' => $txt['no_sortable_items']);
300
				}
301
			}
302
303
			$order[] = array(
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[] = array('value' => $txt['session_verify_fail']);
313
			}
314
315
			if ($validation_token === false)
316
			{
317
				$errors[] = array('value' => $txt['token_verify_fail']);
318
			}
319
		}
320
321
		// New generic token for use
322
		createToken('admin-sort', 'post');
323
		$tokens = array(
324
			array(
325
				'value' => $context['admin-sort_token'],
326
				'attributes' => array('type' => 'token'),
327
			),
328
			array(
329
				'value' => $context['admin-sort_token_var'],
330
				'attributes' => array('type' => 'token_var'),
331
			),
332
		);
333
334
		// Return the response
335
		$context['sub_template'] = 'generic_xml';
336
		$context['xml_data'] = array(
337
			'orders' => array(
338
				'identifier' => 'order',
339
				'children' => $order,
340
			),
341
			'tokens' => array(
342
				'identifier' => 'token',
343
				'children' => $tokens,
344
			),
345
			'errors' => array(
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()
356
	{
357
		global $context, $txt;
358
359
		// Start off clean
360
		$context['xml_data'] = array();
361
		$errors = array();
362
		$order = array();
363
		$board_tree = array();
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[] = array(
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 = array(
449
						'move_to' => 'after',
450
						'target_board' => $board_previous_sibling['id'],
451
					);
452
					$order[] = array('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 = array(
458
						'move_to' => 'child',
459
						'target_board' => $board_previous['id'],
460
						'move_first_child' => true,
461
					);
462
					$order[] = array('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 = array(
468
						'move_to' => 'top',
469
						'target_category' => $board_new['category'],
470
						'move_first_child' => true,
471
					);
472
					$order[] = array('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[] = array('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[] = array('value' => $txt['session_verify_fail']);
492
			}
493
494
			if ($validation_token === false)
495
			{
496
				$errors[] = array('value' => $txt['token_verify_fail']);
497
			}
498
		}
499
500
		// New generic token for use
501
		createToken('admin-sort', 'post');
502
		$tokens = array(
503
			array(
504
				'value' => $context['admin-sort_token'],
505
				'attributes' => array('type' => 'token'),
506
			),
507
			array(
508
				'value' => $context['admin-sort_token_var'],
509
				'attributes' => array('type' => 'token_var'),
510
			),
511
		);
512
513
		// Return the response
514
		$context['sub_template'] = 'generic_xml';
515
		$context['xml_data'] = array(
516
			'orders' => array(
517
				'identifier' => 'order',
518
				'children' => $order,
519
			),
520
			'tokens' => array(
521
				'identifier' => 'token',
522
				'children' => $tokens,
523
			),
524
			'errors' => array(
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()
540
	{
541
		global $context, $txt;
542
543
		// Start off with an empty response
544
		$context['xml_data'] = array();
545
		$errors = array();
546
		$order = array();
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
The assignment to $smile_moved_details is dead and can be removed.
Loading history...
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 = array(
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 = array();
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 = array();
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[] = array('value' => $txt['smileys_moved_done']);
651
					}
652
				}
653
			}
654
			else
655
			{
656
				$errors[] = array('value' => $txt['smileys_moved_fail']);
657
			}
658
		}
659
		// Failed validation :'(
660
		else
661
		{
662
			if ($validation_session !== true)
663
			{
664
				$errors[] = array('value' => $txt['session_verify_fail']);
665
			}
666
667
			if ($validation_token === false)
668
			{
669
				$errors[] = array('value' => $txt['token_verify_fail']);
670
			}
671
		}
672
673
		// New generic token for use
674
		createToken('admin-sort', 'post');
675
		$tokens = array(
676
			array(
677
				'value' => $context['admin-sort_token'],
678
				'attributes' => array('type' => 'token'),
679
			),
680
			array(
681
				'value' => $context['admin-sort_token_var'],
682
				'attributes' => array('type' => 'token_var'),
683
			),
684
		);
685
686
		// Return the response, whatever it is
687
		$context['sub_template'] = 'generic_xml';
688
		$context['xml_data'] = array(
689
			'orders' => array(
690
				'identifier' => 'order',
691
				'children' => $order,
692
			),
693
			'tokens' => array(
694
				'identifier' => 'token',
695
				'children' => $tokens,
696
			),
697
			'errors' => array(
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()
708
	{
709
		global $context, $txt;
710
711
		// Start off with nothing
712
		$context['xml_data'] = array();
713
		$errors = array();
714
		$order = array();
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 = array();
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[] = array('value' => $txt['no_sortable_items']);
750
				}
751
			}
752
753
			$order[] = array(
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[] = array('value' => $txt['session_verify_fail']);
763
			}
764
765
			if ($validation_token === false)
766
			{
767
				$errors[] = array('value' => $txt['token_verify_fail']);
768
			}
769
		}
770
771
		// New generic token for use
772
		createToken('admin-sort', 'post');
773
		$tokens = array(
774
			array(
775
				'value' => $context['admin-sort_token'],
776
				'attributes' => array('type' => 'token'),
777
			),
778
			array(
779
				'value' => $context['admin-sort_token_var'],
780
				'attributes' => array('type' => 'token_var'),
781
			),
782
		);
783
784
		// Return the response
785
		$context['sub_template'] = 'generic_xml';
786
		$context['xml_data'] = array(
787
			'orders' => array(
788
				'identifier' => 'order',
789
				'children' => $order,
790
			),
791
			'tokens' => array(
792
				'identifier' => 'token',
793
				'children' => $tokens,
794
			),
795
			'errors' => array(
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()
806
	{
807
		global $context, $txt;
808
809
		// Initialize
810
		$context['xml_data'] = array();
811
		$errors = array();
812
		$order = array();
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 = array();
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[] = array($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[] = array('value' => $txt['no_sortable_items']);
849
				}
850
			}
851
852
			$order[] = array(
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[] = array('value' => $txt['session_verify_fail']);
862
			}
863
864
			if ($validation_token === false)
865
			{
866
				$errors[] = array('value' => $txt['token_verify_fail']);
867
			}
868
		}
869
870
		// New generic token for use
871
		createToken('admin-sort', 'post');
872
		$tokens = array(
873
			array(
874
				'value' => $context['admin-sort_token'],
875
				'attributes' => array('type' => 'token'),
876
			),
877
			array(
878
				'value' => $context['admin-sort_token_var'],
879
				'attributes' => array('type' => 'token_var'),
880
			),
881
		);
882
883
		// Return the response
884
		$context['sub_template'] = 'generic_xml';
885
		$context['xml_data'] = array(
886
			'orders' => array(
887
				'identifier' => 'order',
888
				'children' => $order,
889
			),
890
			'tokens' => array(
891
				'identifier' => 'token',
892
				'children' => $tokens,
893
			),
894
			'errors' => array(
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()
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.twitter.com/1.1/statuses/oembed.json?id=' . $videoID);
928
			if ($data !== false)
929
			{
930
				$context['json_data'] = trim($data);
931
			}
932
		}
933
	}
934
}
935