MergeTopics::action_mergeDone()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 0
dl 0
loc 10
ccs 0
cts 4
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Handle merging of topics
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 Beta 1
14
 *
15
 * Original module by Mach8 - We'll never forget you.
16
 * ETA: Sorry, we did.
17
 */
18
19
namespace ElkArte\Controller;
20
21
use ElkArte\AbstractController;
22
use ElkArte\Action;
23
use ElkArte\Exceptions\Exception;
24
use ElkArte\TopicsMerge;
25
26
/**
27
 * Merges two or more topics into a single topic.
28
 */
29
class MergeTopics extends AbstractController
30
{
31
	/**
32
	 * Merges two or more topics into one topic.
33
	 *
34
	 * What it does:
35
	 *
36
	 * - delegates to the other functions (based on the URL parameter sa).
37
	 * - loads the MergeTopics template.
38
	 * - requires the merge_any permission.
39
	 * - is accessed with ?action=mergetopics.
40
	 *
41
	 * @see AbstractController::action_index
42
	 */
43
	public function action_index()
44
	{
45
		global $context;
46
47
		// Load the template....
48
		theme()->getTemplates()->load('MergeTopics');
49
50
		$subActions = [
51
			'done' => [$this, 'action_mergeDone'],
52
			'execute' => [$this, 'action_mergeExecute'],
53
			'index' => [$this, 'action_mergeIndex'],
54
			'options' => [$this, 'action_mergeExecute']
55
		];
56
57
		// ?action=mergetopics;sa=LETSBREAKIT won't work, sorry.
58
		$action = new Action('merge_topics');
59
		$subAction = $action->initialize($subActions, 'index');
60
		$context['sub_action'] = $subAction;
61
		$action->dispatch($subAction);
62
	}
63
64
	/**
65
	 * Allows to pick a topic to merge the current topic with.
66
	 *
67
	 * What it does:
68
	 *
69
	 * - is accessed with ?action=mergetopics;sa=index
70
	 * - default sub action for ?action=mergetopics.
71
	 * - uses 'merge' sub template of the MergeTopics template.
72
	 * - allows to set a different target board.
73
	 */
74
	public function action_mergeIndex(): void
75
	{
76
		global $txt, $board, $context, $modSettings;
77
78
		// If we don't know where you are from we know where you go
79
		$from = $this->_req->getQuery('from', 'intval');
80
		if (!isset($from))
81
		{
82
			throw new Exception('no_access', false);
83
		}
84
85
		$target_board = $this->_req->getPost('targetboard', 'intval', $board);
86
		$context['target_board'] = $target_board;
87
88
		// Prepare a handy query bit for approval...
89
		if ($modSettings['postmod_active'])
90
		{
91
			$can_approve_boards = empty($this->user->mod_cache['ap']) ? boardsAllowedTo('approve_posts') : $this->user->mod_cache['ap'];
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
92
			$onlyApproved = $can_approve_boards !== [0] && !in_array($target_board, $can_approve_boards);
93
		}
94
		else
95
		{
96
			$onlyApproved = false;
97
		}
98
99
		// How many topics are on this board?  (used for paging.)
100
		require_once(SUBSDIR . '/Topic.subs.php');
101
		$topiccount = countTopicsByBoard($target_board, $onlyApproved);
102
103
		// Make the page list.
104
		$start = $this->_req->getQuery('start', 'intval', 0);
105
		$context['page_index'] = constructPageIndex('{scripturl}?action=mergetopics;from=' . $from . ';targetboard=' . $target_board . ';board=' . $board . '.%1$d', $start, $topiccount, $modSettings['defaultMaxTopics'], true);
106
107
		// Get the topic's subject.
108
		$topic_info = getTopicInfo($from, 'message');
109
110
		// @todo review: double check the logic
111
		if (empty($topic_info) || ($topic_info['id_board'] != $board) || ($onlyApproved && empty($topic_info['approved'])))
112
		{
113
			throw new Exception('no_board');
114
		}
115
116
		// Tell the template a few things..
117
		$context['origin_topic'] = $from;
118
		$context['origin_subject'] = $topic_info['subject'];
119
		$context['origin_js_subject'] = addcslashes(addslashes($topic_info['subject']), '/');
120
		$context['page_title'] = $txt['merge'];
121
122
		// Check which boards you have merge permissions on.
123
		$merge_boards = boardsAllowedTo('merge_any');
124
125
		if (empty($merge_boards))
126
		{
127
			throw new Exception('cannot_merge_any', 'user');
128
		}
129
130
		// Get a list of boards they can navigate to to merge.
131
		require_once(SUBSDIR . '/Boards.subs.php');
132
		$boardListOptions = [
133
			'not_redirection' => true
134
		];
135
136
		if (!in_array(0, $merge_boards))
137
		{
138
			$boardListOptions['included_boards'] = $merge_boards;
139
		}
140
141
		$boards_list = getBoardList($boardListOptions, true);
142
		$context['boards'] = [];
143
144
		foreach ($boards_list as $board)
145
		{
146
			$context['boards'][] = [
147
				'id' => $board['id_board'],
148
				'name' => $board['board_name'],
149
				'category' => $board['cat_name']
150
			];
151
		}
152
153
		// Get some topics to merge it with.
154
		$context['topics'] = mergeableTopics($target_board, $from, $onlyApproved, $start);
155
156
		if (empty($context['topics']) && count($context['boards']) <= 1)
157
		{
158
			throw new Exception('merge_need_more_topics');
159
		}
160
161
		$context['sub_template'] = 'merge';
162
	}
163
164
	/**
165
	 * Set merge options and do the actual merge of two or more topics.
166
	 *
167
	 * The merge options screen:
168
	 * - shows topics to be merged and allows to set some merge options.
169
	 * - is accessed by ?action=mergetopics;sa=options and can also internally be called by action_quickmod().
170
	 * - uses 'merge_extra_options' sub template of the MergeTopics template.
171
	 *
172
	 * The actual merge:
173
	 * - is accessed with ?action=mergetopics;sa=execute.
174
	 * - updates the statistics to reflect the merge.
175
	 * - logs the action in the moderation log.
176
	 * - sends a notification is sent to all users monitoring this topic.
177
	 * - redirects to ?action=mergetopics;sa=done.
178
	 *
179
	 * @param int[] $topics = array() of topic ids
180
	 *
181
	 * @return bool
182
	 * @throws Exception merge_need_more_topics
183
	 */
184
	public function action_mergeExecute($topics = []): bool
185
	{
186
		global $txt, $context;
187
188
		// Check the session.
189
		checkSession('request');
190
191
		require_once(SUBSDIR . '/Topic.subs.php');
192
		require_once(SUBSDIR . '/Post.subs.php');
193
194
		// Handle URLs from action_mergeIndex.
195
		if ($this->_req->hasQuery('from') && $this->_req->hasQuery('to'))
196
		{
197
			$from = $this->_req->getQuery('from', 'intval', 0);
198
			$to = $this->_req->getQuery('to', 'intval', 0);
199
			if (!empty($from) && !empty($to))
200
			{
201
				$topics = [$from, $to];
202
			}
203
		}
204
205
		// If we came from a form, the topic IDs came by post.
206
		$posted_topics = $this->_req->getPost('topics', null, []);
207
		if (!empty($posted_topics) && is_array($posted_topics))
208
		{
209
			$topics = $posted_topics;
210
		}
211
212
		// There's nothing to merge with just one topic...
213
		if (empty($topics) || !is_array($topics) || count($topics) === 1)
214
		{
215
			throw new Exception('merge_need_more_topics');
216
		}
217
218
		// Send the topics to the TopicsMerge class
219
		$merger = new TopicsMerge($topics);
220
221
		// If we didn't get any topics then they've been messing with unapproved stuff.
222
		if ($merger->hasErrors())
223
		{
224
			$error = $merger->firstError();
225
			throw new Exception($error[0], $error[1]);
226
		}
227
228
		// The parameters of action_mergeExecute were set, so this must've been an internal call.
229
		isAllowedTo('merge_any', $merger->boards);
230
		theme()->getTemplates()->load('MergeTopics');
231
232
		// Get the boards a user is allowed to merge in.
233
		$allowedto_merge_boards = boardsAllowedTo('merge_any');
234
235
		// No permissions to merge, your effort ends here
236
		if (empty($allowedto_merge_boards))
237
		{
238
			throw new Exception('cannot_merge_any', 'user');
239
		}
240
241
		// Make sure they can see all boards....
242
		$query_boards = ['boards' => $merger->boards];
243
244
		if (!in_array(0, $allowedto_merge_boards))
245
		{
246
			$query_boards['boards'] = array_merge($query_boards['boards'], $allowedto_merge_boards);
247
		}
248
249
		// Saved in a variable to (potentially) save a query later
250
		require_once(SUBSDIR . '/Boards.subs.php');
251
		$boards_info = fetchBoardsInfo($query_boards);
252
253
		$boardListOptions = [
254
			'not_redirection' => true,
255
			'selected_board' => $merger->firstBoard,
256
		];
257
258
		if (!in_array(0, $allowedto_merge_boards))
259
		{
260
			$boardListOptions['included_boards'] = $allowedto_merge_boards;
261
		}
262
263
		$context += getBoardList($boardListOptions);
264
265
		// This is removed to avoid the board not being selectable.
266
		$context['current_board'] = null;
267
268
		// This happens when a member is moderator of a board he cannot see
269
		foreach ($merger->boards as $board)
270
		{
271
			if (!isset($boards_info[$board]))
272
			{
273
				throw new Exception('no_board');
274
			}
275
		}
276
277
		$sa = $this->_req->getQuery('sa', 'trim|strval', '');
278
		if ($sa === '' || $sa === 'options')
279
		{
280
			$context['polls'] = $merger->getPolls();
281
			$context['topics'] = $merger->topic_data;
282
			$context['target_board'] = $this->_req->getQuery('board', 'intval', 0);
283
284
			foreach ($merger->topic_data as $id => $topic)
285
			{
286
				$context['topics'][$id]['selected'] = $topic['id'] == $merger->firstTopic;
287
			}
288
289
			$context['page_title'] = $txt['merge'];
290
			$context['sub_template'] = 'merge_extra_options';
291
292
			return true;
293
		}
294
295
		$result = $merger->doMerge([
296
			'board' => $merger->boards[0],
297
			'poll' => $this->_req->getPost('poll', 'intval', 0),
298
			'subject' => $this->_req->getPost('subject', 'trim', ''),
299
			'custom_subject' => $this->_req->getPost('custom_subject', 'trim', ''),
300
			'enforce_subject' => $this->_req->getPost('enforce_subject', 'trim', ''),
301
			'notifications' => $this->_req->getPost('notifications', 'trim', ''),
302
			'accessible_boards' => array_keys($boards_info),
303
		]);
304
305
		if ($merger->hasErrors())
306
		{
307
			$error = $merger->firstError();
308
			throw new Exception($error[0], $error[1]);
309
		}
310
311
		// Send them to the all done page.
312
		redirectexit('action=mergetopics;sa=done;to=' . $result[0] . ';targetboard=' . $result[1]);
313
314
		return true;
315
	}
316
317
	/**
318
	 * Shows a 'merge completed' screen.
319
	 *
320
	 * - is accessed with ?action=mergetopics;sa=done.
321
	 * - uses 'merge_done' sub template of the MergeTopics template.
322
	 */
323
	public function action_mergeDone(): void
324
	{
325
		global $txt, $context;
326
327
		// Make sure the template knows everything...
328
		$context['target_board'] = $this->_req->getQuery('targetboard', 'intval', 0);
329
		$context['target_topic'] = $this->_req->getQuery('to', 'intval', 0);
330
331
		$context['page_title'] = $txt['merge'];
332
		$context['sub_template'] = 'merge_done';
333
	}
334
}
335