Completed
Push — patch_1-1-4 ( 3f780f...826343 )
by Emanuele
25:17 queued 11:40
created

MoveTopic_Controller   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 423
Duplicated Lines 6.38 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 27
loc 423
rs 6.96
c 0
b 0
f 0
ccs 0
cts 181
cp 0
wmc 53
lcom 1
cbo 4

10 Methods

Rating   Name   Duplication   Size   Complexity  
A pre_dispatch() 0 7 1
A action_index() 0 5 1
B action_movetopic() 11 33 8
B action_movetopic2() 0 49 5
B _prep_template() 7 39 5
B _check_access() 0 22 8
B _check_access_2() 0 45 9
B _rename_topic() 0 33 6
B _post_redirect() 0 52 6
A _count_update() 9 21 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MoveTopic_Controller often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MoveTopic_Controller, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Handles the moving of topics from board to board
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1
15
 *
16
 */
17
18
/**
19
 * Move Topic Controller
20
 */
21
class MoveTopic_Controller extends Action_Controller
22
{
23
	/**
24
	 * The id of the topic being manipulated
25
	 * @var int
26
	 */
27
	private $_topic;
28
29
	/**
30
	 * Information about the topic being moved
31
	 * @var array
32
	 */
33
	private $_topic_info;
34
35
	/**
36
	 * Information about the board where the topic resides
37
	 * @var array
38
	 */
39
	private $_board_info;
40
41
	/**
42
	 * Board that will receive the topic
43
	 * @var int
44
	 */
45
	private $_toboard;
46
47
	/**
48
	 * Pre Dispatch, called before other methods.
49
	 */
50
	public function pre_dispatch()
51
	{
52
		global $topic;
53
54
		// Set the topic from the global, yes yes
55
		$this->_topic = $topic;
56
	}
57
58
	/**
59
	 * Forwards to the action method to handle the action.
60
	 *
61
	 * @see Action_Controller::action_index()
62
	 */
63
	public function action_index()
64
	{
65
		// move a topic, what else?!
66
		// $this->action_movetopic();
67
	}
68
69
	/**
70
	 * This function allows to move a topic
71
	 *
72
	 * What it does:
73
	 *
74
	 * - It must be called with a topic specified. (that is, global $topic must
75
	 * be set... @todo fix this thing.)
76
	 * - Validates access
77
	 * - Accessed via ?action=movetopic.
78
	 *
79
	 * @uses template_move_topic() sub-template in MoveTopic.template.php
80
	 */
81
	public function action_movetopic()
82
	{
83
		global $context;
84
85
		// Lets make sure they can access the topic being moved and have permissions to move it
86
		$this->_check_access();
87
88
		// Get a list of boards this moderator can move to.
89
		require_once(SUBSDIR . '/Boards.subs.php');
90
		$context += getBoardList(array('not_redirection' => true));
91
92
		// No boards?
93
		if (empty($context['categories']) || $context['num_boards'] == 1)
94
			throw new Elk_Exception('moveto_noboards', false);
95
96
		// Already used the function, let's set the selected board back to the last
97
		$last_moved_to = isset($_SESSION['move_to_topic']['move_to']) && $_SESSION['move_to_topic']['move_to'] != $context['current_board'] ? (int) $_SESSION['move_to_topic']['move_to'] : 0;
98 View Code Duplication
		if (!empty($last_moved_to))
99
		{
100
			foreach ($context['categories'] as $id => $values)
101
			{
102
				if (isset($values['boards'][$last_moved_to]))
103
				{
104
					$context['categories'][$id]['boards'][$last_moved_to]['selected'] = true;
105
					break;
106
				}
107
			}
108
		}
109
110
		// Set up for the template
111
		loadTemplate('MoveTopic');
112
		$this->_prep_template();
113
	}
114
115
	/**
116
	 * Executes the actual move of a topic.
117
	 *
118
	 * What it does:
119
	 *
120
	 * - It is called on the submit of action_movetopic.
121
	 * - This function logs that topics have been moved in the moderation log.
122
	 * - Upon successful completion redirects to message index.
123
	 * - Accessed via ?action=movetopic2.
124
	 *
125
	 * @uses subs/Post.subs.php.
126
	 */
127
	public function action_movetopic2()
128
	{
129
		global $board, $user_info;
130
131
		$this->_check_access_2();
132
133
		checkSession();
134
		require_once(SUBSDIR . '/Post.subs.php');
135
		require_once(SUBSDIR . '/Boards.subs.php');
136
137
		// The destination board must be numeric.
138
		$this->_toboard = (int) $this->_req->post->toboard;
139
140
		// Make sure they can see the board they are trying to move to (and get whether posts count in the target board).
141
		$this->_board_info = boardInfo($this->_toboard, $this->_topic);
142
		if (empty($this->_board_info))
143
			throw new Elk_Exception('no_board');
144
145
		// Remember this for later.
146
		$_SESSION['move_to_topic'] = array(
147
			'move_to' => $this->_toboard
148
		);
149
150
		// Rename the topic if needed
151
		$this->_rename_topic();
152
153
		// Create a link to this in the old board.
154
		$this->_post_redirect();
155
156
		// Account for boards that count posts and those that don't
157
		$this->_count_update();
158
159
		// Do the move (includes statistics update needed for the redirect topic).
160
		moveTopics($this->_topic, $this->_toboard);
161
162
		// Log that they moved this topic.
163
		if (!allowedTo('move_own') || $this->_topic_info['id_member_started'] != $user_info['id'])
164
			logAction('move', array('topic' => $this->_topic, 'board_from' => $board, 'board_to' => $this->_toboard));
165
166
		// Notify people that this topic has been moved?
167
		require_once(SUBSDIR . '/Notification.subs.php');
168
		sendNotifications($this->_topic, 'move');
169
170
		// Why not go back to the original board in case they want to keep moving?
171
		if (!isset($this->_req->post->goback))
172
			redirectexit('board=' . $board . '.0');
173
		else
174
			redirectexit('topic=' . $this->_topic . '.0');
175
	}
176
177
	/**
178
	 * Prepares the content for use in the move topic template
179
	 */
180
	private function _prep_template()
181
	{
182
		global $context, $txt, $scripturl, $user_info, $language;
183
184
		$context['is_approved'] = $this->_topic_info['approved'];
185
		$context['subject'] = $this->_topic_info['subject'];
186
		$context['redirect_topic'] = isset($_SESSION['move_to_topic']['redirect_topic']) ? (int) $_SESSION['move_to_topic']['redirect_topic'] : 0;
187
		$context['redirect_expires'] = isset($_SESSION['move_to_topic']['redirect_expires']) ? (int) $_SESSION['move_to_topic']['redirect_expires'] : 0;
188
		$context['page_title'] = $txt['move_topic'];
189
		$context['sub_template'] = 'move_topic';
190
191
		// Breadcrumbs
192
		$context['linktree'][] = array(
193
			'url' => $scripturl . '?topic=' . $this->_topic . '.0',
194
			'name' => $context['subject'],
195
		);
196
		$context['linktree'][] = array(
197
			'url' => '#',
198
			'name' => $txt['move_topic'],
199
		);
200
201
		$context['back_to_topic'] = isset($this->_req->post->goback);
202
203
		// Ugly !
204 View Code Duplication
		if ($user_info['language'] != $language)
205
		{
206
			loadLanguage('index', $language);
207
			$temp = $txt['movetopic_default'];
208
			loadLanguage('index');
209
			$txt['movetopic_default'] = $temp;
210
		}
211
212
		// We will need this
213
		if (isset($this->_req->query->current_board))
214
			moveTopicConcurrence((int) $this->_req->query->current_board);
215
216
		// Register this form and get a sequence number in $context.
217
		checkSubmitOnce('register');
218
	}
219
220
	/**
221
	 * Validates that the member can access the topic
222
	 *
223
	 * What it does:
224
	 *
225
	 * - Checks that a topic is supplied
226
	 * - Validates the topic information can be loaded
227
	 * - If the topic is not approved yet, must have approve permissions to move it
228
	 * - If the member is the topic starter requires the move_own permission, otherwise the move_any permission.
229
	 */
230
	private function _check_access()
231
	{
232
		global $modSettings, $user_info;
233
234
		if (empty($this->_topic))
235
			throw new Elk_Exception('no_access', false);
236
237
		// Retrieve the basic topic information for whats being moved
238
		require_once(SUBSDIR . '/Topic.subs.php');
239
		$this->_topic_info = getTopicInfo($this->_topic, 'message');
240
241
		if (empty($this->_topic_info))
242
			throw new Elk_Exception('topic_gone', false);
243
244
		// Can they see it - if not approved?
245
		if ($modSettings['postmod_active'] && !$this->_topic_info['approved'])
246
			isAllowedTo('approve_posts');
247
248
		// Are they allowed to actually move any topics or even their own?
249
		if (!allowedTo('move_any') && ($this->_topic_info['id_member_started'] == $user_info['id'] && !allowedTo('move_own')))
250
			throw new Elk_Exception('cannot_move_any', false);
251
	}
252
253
	/**
254
	 * Checks access and input validation before committing the move
255
	 *
256
	 * What it does:
257
	 *
258
	 * - Checks that a topic is supplied
259
	 * - Validates the move location
260
	 * - Checks redirection details if its a redirection is to be posted
261
	 * - If the member is the topic starter requires the move_own permission, otherwise the move_any permission.
262
	 *
263
	 * @return bool
264
	 * @throws Elk_Exception no_access
265
	 */
266
	private function _check_access_2()
267
	{
268
		global $user_info;
269
270
		if (empty($this->_topic))
271
			throw new Elk_Exception('no_access', false);
272
273
		// You can't choose to have a redirection topic and not provide a reason.
274
		if (isset($this->_req->post->postRedirect) && $this->_req->getPost('reason', 'trim', '') === '')
275
			throw new Elk_Exception('movetopic_no_reason', false);
276
277
		// You have to tell us were you are moving to
278
		if (!isset($this->_req->post->toboard))
279
			throw new Elk_Exception('movetopic_no_board', false);
280
281
		// We will need this
282
		require_once(SUBSDIR . '/Topic.subs.php');
283
		if (isset($this->_req->query->current_board))
284
			moveTopicConcurrence((int) $this->_req->query->current_board);
285
286
		// Make sure this form hasn't been submitted before.
287
		checkSubmitOnce('check');
288
289
		// Get the basic details on this topic (again)
290
		$this->_topic_info = getTopicInfo($this->_topic);
291
292
		// Not approved then you need approval permissions to move it as well
293
		if (!$this->_topic_info['approved'])
294
			isAllowedTo('approve_posts');
295
296
		// Can they move topics on this board?
297
		if (!allowedTo('move_any'))
298
		{
299
			if ($this->_topic_info['id_member_started'] == $user_info['id'])
300
			{
301
				isAllowedTo('move_own');
302
			}
303
			else
304
			{
305
				isAllowedTo('move_any');
306
			}
307
		}
308
309
		return true;
310
	}
311
312
	/**
313
	 * Renames the topic during the move if requested
314
	 *
315
	 * What it does:
316
	 *
317
	 * - Renames the moved topic with a new topic subject
318
	 * - If enforce_subject is set, renames all posts withing the moved topic posts with a new subject
319
	 */
320
	private function _rename_topic()
321
	{
322
		global $context;
323
324
		// Rename the topic...
325
		if (isset($this->_req->post->reset_subject, $this->_req->post->custom_subject) && $this->_req->post->custom_subject != '')
326
		{
327
			$custom_subject = strtr(Util::htmltrim(Util::htmlspecialchars($this->_req->post->custom_subject)), array("\r" => '', "\n" => '', "\t" => ''));
328
329
			// Keep checking the length.
330
			if (Util::strlen($custom_subject) > 100)
331
				$custom_subject = Util::substr($custom_subject, 0, 100);
332
333
			// If it's still valid move onwards and upwards.
334
			if ($custom_subject != '')
335
			{
336
				$all_messages = isset($this->_req->post->enforce_subject);
337
				if ($all_messages)
338
				{
339
					// Get a response prefix, but in the forum's default language.
340
					$context['response_prefix'] = response_prefix();
341
342
					topicSubject($this->_topic_info, $custom_subject, $context['response_prefix'], $all_messages);
343
				}
344
				else
345
					topicSubject($this->_topic_info, $custom_subject);
346
347
				// Fix the subject cache.
348
				require_once(SUBSDIR . '/Messages.subs.php');
349
				updateSubjectStats($this->_topic, $custom_subject);
350
			}
351
		}
352
	}
353
354
	/**
355
	 * Posts a redirection topic in the original location of the moved topic
356
	 *
357
	 * What it does:
358
	 *
359
	 * - If leaving a moved "where did it go" topic, validates the needed inputs
360
	 * - Posts a new topic in the originating board of the topic to be moved.
361
	 */
362
	private function _post_redirect()
363
	{
364
		global $txt, $board, $scripturl, $language, $user_info;
365
366
		// @todo Does this make sense if the topic was unapproved before? I'd just about say so.
367
		if (isset($this->_req->post->postRedirect))
368
		{
369
			// Should be in the boardwide language.
370
			if ($user_info['language'] != $language)
371
				loadLanguage('index', $language);
372
373
			$reason = Util::htmlspecialchars($this->_req->post->reason, ENT_QUOTES);
374
			preparsecode($reason);
375
376
			// Add a URL onto the message.
377
			$reason = strtr($reason, array(
378
				$txt['movetopic_auto_board'] => '[url=' . $scripturl . '?board=' . $this->_toboard . '.0]' . $this->_board_info['name'] . '[/url]',
379
				$txt['movetopic_auto_topic'] => '[iurl]' . $scripturl . '?topic=' . $this->_topic . '.0[/iurl]'
380
			));
381
382
			// Auto remove this MOVED redirection topic in the future?
383
			$redirect_expires = !empty($this->_req->post->redirect_expires) ? (int) $this->_req->post->redirect_expires : 0;
384
385
			// Redirect to the MOVED topic from topic list?
386
			$redirect_topic = isset($this->_req->post->redirect_topic) ? $this->_topic : 0;
387
388
			// And remember the last expiry period too.
389
			$_SESSION['move_to_topic']['redirect_topic'] = $redirect_topic;
390
			$_SESSION['move_to_topic']['redirect_expires'] = $redirect_expires;
391
392
			$msgOptions = array(
393
				'subject' => $txt['moved'] . ': ' . $this->_board_info['subject'],
394
				'body' => $reason,
395
				'icon' => 'moved',
396
				'smileys_enabled' => 1,
397
			);
398
399
			$topicOptions = array(
400
				'board' => $board,
401
				'lock_mode' => 1,
402
				'mark_as_read' => true,
403
				'redirect_expires' => empty($redirect_expires) ? 0 : ($redirect_expires * 60) + time(),
404
				'redirect_topic' => $redirect_topic,
405
			);
406
407
			$posterOptions = array(
408
				'id' => $user_info['id'],
409
				'update_post_count' => empty($this->_board_info['count_posts']),
410
			);
411
			createPost($msgOptions, $topicOptions, $posterOptions);
412
		}
413
	}
414
415
	/**
416
	 * Accounts for board / user post counts when a topic is moved.
417
	 *
418
	 * What it does:
419
	 *
420
	 * - Checks if a topic is being moved to/from a board that does/does'nt count posts.
421
	 */
422
	private function _count_update()
423
	{
424
		global $board;
425
426
		$board_from = boardInfo($board);
427
		if ($board_from['count_posts'] != $this->_board_info['count_posts'])
428
		{
429
			require_once(SUBSDIR . '/Members.subs.php');
430
			$posters = postersCount($this->_topic);
431
432 View Code Duplication
			foreach ($posters as $id_member => $posts)
433
			{
434
				// The board we're moving from counted posts, but not to.
435
				if (empty($board_from['count_posts']))
436
					updateMemberData($id_member, array('posts' => 'posts - ' . $posts));
437
				// The reverse: from didn't, to did.
438
				else
439
					updateMemberData($id_member, array('posts' => 'posts + ' . $posts));
440
			}
441
		}
442
	}
443
}
444