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

TopicsMerge   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 505
Duplicated Lines 3.56 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 18
loc 505
rs 6.4799
c 0
b 0
f 0
ccs 0
cts 296
cp 0
wmc 54
lcom 1
cbo 2

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A hasErrors() 0 4 1
A firstError() 0 13 2
A getPolls() 0 36 3
F doMerge() 9 171 24
A _updateStats() 0 33 4
F _loadTopicDetails() 9 109 19

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 TopicsMerge 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 TopicsMerge, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * This file has functions in it to handle merging of two or more topics
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
 * This class has functions to handle merging of two or more topics
20
 * in to a single new or existing topic.
21
 *
22
 * Class TopicsMerge
23
 */
24
class TopicsMerge
25
{
26
	/**
27
	 * For each topic a set of information (id, board, subject, poll, etc.)
28
	 *
29
	 * @var mixed[]
30
	 */
31
	public $topic_data = array();
32
33
	/**
34
	 * All the boards the topics are in
35
	 *
36
	 * @var int[]
37
	 */
38
	public $boards = array();
39
40
	/**
41
	 * The id_topic with the lowest id_first_msg
42
	 *
43
	 * @var int
44
	 */
45
	public $firstTopic = 0;
46
47
	/**
48
	 * The id_board of the topic TopicsMerge::$firstTopic
49
	 *
50
	 * @var int
51
	 */
52
	public $firstBoard = 0;
53
54
	/**
55
	 * Just the array of topics to merge.
56
	 *
57
	 * @var int[]
58
	 */
59
	private $_topics = array();
60
61
	/**
62
	 * Sum of the number of views of each topic.
63
	 *
64
	 * @var int
65
	 */
66
	private $_num_views = 0;
67
68
	/**
69
	 * If at least one of the topics is sticky
70
	 *
71
	 * @var int
72
	 */
73
	private $_is_sticky = 0;
74
75
	/**
76
	 * An array of "totals" (number of topics/messages, unapproved, etc.) for
77
	 * each board involved
78
	 *
79
	 * @var mixed[]
80
	 */
81
	private $_boardTotals = array();
82
83
	/**
84
	 * If any topic has a poll, the array of poll id
85
	 *
86
	 * @var int[]
87
	 */
88
	private $_polls = array();
89
90
	/**
91
	 * List of errors occurred
92
	 *
93
	 * @var string[]
94
	 */
95
	private $_errors = array();
96
97
	/**
98
	 * The database object
99
	 *
100
	 * @var object
101
	 */
102
	private $_db = null;
103
104
	/**
105
	 * Initialize the class with a list of topics to merge
106
	 *
107
	 * @param int[] $topics array of topics to merge into one
108
	 */
109
	public function __construct($topics)
110
	{
111
		// Prepare the vars
112
		$this->_db = database();
113
114
		// Ensure all the id's are integers
115
		$topics = array_map('intval', $topics);
116
		$this->_topics = array_filter($topics);
117
118
		// Find out some preliminary information
119
		$this->_loadTopicDetails();
120
	}
121
122
	/**
123
	 * If errors occurred while working
124
	 *
125
	 * @return bool
126
	 */
127
	public function hasErrors()
128
	{
129
		return !empty($this->_errors);
130
	}
131
132
	/**
133
	 * The first error occurred
134
	 *
135
	 * @return array|string
136
	 */
137
	public function firstError()
138
	{
139
		if (!empty($this->_errors))
140
		{
141
			$errors = array_values($this->_errors);
142
143
			return array_shift($errors);
144
		}
145
		else
146
		{
147
			return '';
148
		}
149
	}
150
151
	/**
152
	 * Returns the polls information if any of the topics has a poll.
153
	 *
154
	 * @return mixed[]
155
	 */
156
	public function getPolls()
157
	{
158
		$polls = array();
159
160
		if (count($this->_polls) > 1)
161
		{
162
			$request = $this->_db->query('', '
163
				SELECT
164
					t.id_topic, t.id_poll, m.subject, p.question
165
				FROM {db_prefix}polls AS p
166
					INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
167
					INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
168
				WHERE p.id_poll IN ({array_int:polls})
169
				LIMIT {int:limit}',
170
				array(
171
					'polls' => $this->_polls,
172
					'limit' => count($this->_polls),
173
				)
174
			);
175
			while ($row = $this->_db->fetch_assoc($request))
176
			{
177
				$polls[] = array(
178
					'id' => $row['id_poll'],
179
					'topic' => array(
180
						'id' => $row['id_topic'],
181
						'subject' => $row['subject']
182
					),
183
					'question' => $row['question'],
184
					'selected' => $row['id_topic'] == $this->firstTopic
185
				);
186
			}
187
			$this->_db->free_result($request);
188
		}
189
190
		return $polls;
191
	}
192
193
	/**
194
	 * Performs the merge operations
195
	 *
196
	 * @param mixed[] $details
197
	 * @return bool|int[]
198
	 * @throws Elk_Exception
199
	 */
200
	public function doMerge($details = array())
201
	{
202
		// Just to be sure, here we should not have any error around
203
		$this->_errors = array();
204
205
		// Determine target board.
206
		$target_board = count($this->boards) > 1 ? (int) $details['board'] : $this->boards[0];
207
		if (!in_array($target_board, $details['accessible_boards']))
208
		{
209
			$this->_errors[] = array('no_board', true);
210
			return false;
211
		}
212
213
		// Determine which poll will survive and which polls won't.
214
		$target_poll = count($this->_polls) > 1 ? (int) $details['poll'] : (count($this->_polls) == 1 ? $this->_polls[0] : 0);
215
		if ($target_poll > 0 && !in_array($target_poll, $this->_polls))
216
		{
217
			$this->_errors[] = array('no_access', false);
218
			return false;
219
		}
220
221
		$deleted_polls = empty($target_poll) ? $this->_polls : array_diff($this->_polls, array($target_poll));
222
223
		// Determine the subject of the newly merged topic - was a custom subject specified?
224
		if (empty($details['subject']) && $details['custom_subject'] != '')
225
		{
226
			$target_subject = strtr(Util::htmltrim(Util::htmlspecialchars($details['custom_subject'])), array("\r" => '', "\n" => '', "\t" => ''));
227
228
			// Keep checking the length.
229
			if (Util::strlen($target_subject) > 100)
230
				$target_subject = Util::substr($target_subject, 0, 100);
231
232
			// Nothing left - odd but pick the first topics subject.
233
			if ($target_subject == '')
234
				$target_subject = $this->topic_data[$this->firstTopic]['subject'];
235
		}
236
		// A subject was selected from the list.
237
		elseif (!empty($this->topic_data[(int) $details['subject']]['subject']))
238
			$target_subject = $this->topic_data[(int) $details['subject']]['subject'];
239
		// Nothing worked? Just take the subject of the first message.
240
		else
241
			$target_subject = $this->topic_data[$this->firstTopic]['subject'];
242
243
		// Get the first and last message and the number of messages....
244
		$request = $this->_db->query('', '
245
			SELECT
246
				approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count
247
			FROM {db_prefix}messages
248
			WHERE id_topic IN ({array_int:topics})
249
			GROUP BY approved
250
			ORDER BY approved DESC',
251
			array(
252
				'topics' => $this->_topics,
253
			)
254
		);
255
		$topic_approved = 1;
256
		$first_msg = 0;
257
		$num_replies = 0;
258
		while ($row = $this->_db->fetch_assoc($request))
259
		{
260
			// If this is approved, or is fully unapproved.
261
			if ($row['approved'] || !isset($first_msg))
262
			{
263
				$first_msg = $row['first_msg'];
264
				$last_msg = $row['last_msg'];
265
				if ($row['approved'])
266
				{
267
					$num_replies = $row['message_count'] - 1;
268
					$num_unapproved = 0;
269
				}
270
				else
271
				{
272
					$topic_approved = 0;
273
					$num_replies = 0;
274
					$num_unapproved = $row['message_count'];
275
				}
276
			}
277
			else
278
			{
279
				// If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong!
280
				if ($first_msg > $row['first_msg'])
281
				{
282
					$first_msg = $row['first_msg'];
283
					$num_replies++;
284
					$topic_approved = 0;
285
				}
286
				$num_unapproved = $row['message_count'];
287
			}
288
		}
289
		$this->_db->free_result($request);
290
291
		// Ensure we have a board stat for the target board.
292 View Code Duplication
		if (!isset($this->_boardTotals[$target_board]))
293
		{
294
			$this->_boardTotals[$target_board] = array(
295
				'num_posts' => 0,
296
				'num_topics' => 0,
297
				'unapproved_posts' => 0,
298
				'unapproved_topics' => 0
299
			);
300
		}
301
302
		// Fix the topic count stuff depending on what the new one counts as.
303
		if ($topic_approved)
304
			$this->_boardTotals[$target_board]['num_topics']--;
305
		else
306
			$this->_boardTotals[$target_board]['unapproved_topics']--;
307
308
		$this->_boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved;
0 ignored issues
show
Bug introduced by
The variable $num_unapproved does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
309
		$this->_boardTotals[$target_board]['num_posts'] -= $topic_approved ? $num_replies + 1 : $num_replies;
310
311
		// Get the member ID of the first and last message.
312
		$request = $this->_db->query('', '
313
			SELECT
314
				id_member
315
			FROM {db_prefix}messages
316
			WHERE id_msg IN ({int:first_msg}, {int:last_msg})
317
			ORDER BY id_msg
318
			LIMIT 2',
319
			array(
320
				'first_msg' => $first_msg,
321
				'last_msg' => $last_msg,
0 ignored issues
show
Bug introduced by
The variable $last_msg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
322
			)
323
		);
324
		list ($member_started) = $this->_db->fetch_row($request);
325
		list ($member_updated) = $this->_db->fetch_row($request);
326
327
		// First and last message are the same, so only row was returned.
328
		if ($member_updated === null)
329
			$member_updated = $member_started;
330
331
		$this->_db->free_result($request);
332
333
		// Obtain all the message ids we are going to affect.
334
		$affected_msgs = messagesInTopics($this->_topics);
335
336
		// Assign the first topic ID to be the merged topic.
337
		$id_topic = min($this->_topics);
338
339
		$enforce_subject = Util::htmlspecialchars(trim($details['enforce_subject']));
340
341
		// Merge topic notifications.
342
		$notifications = is_array($details['notifications']) ? array_intersect($this->_topics, $details['notifications']) : array();
343
		fixMergedTopics($first_msg, $this->_topics, $id_topic, $target_board, $target_subject, $enforce_subject, $notifications);
344
345
		// Assign the properties of the newly merged topic.
346
		setTopicAttribute($id_topic, array(
347
			'id_board' => $target_board,
348
			'is_sticky' => $this->_is_sticky,
349
			'approved' => $topic_approved,
350
			'id_member_started' => $member_started,
351
			'id_member_updated' => $member_updated,
352
			'id_first_msg' => $first_msg,
353
			'id_last_msg' => $last_msg,
354
			'id_poll' => $target_poll,
355
			'num_replies' => $num_replies,
356
			'unapproved_posts' => $num_unapproved,
357
			'num_views' => $this->_num_views,
358
		));
359
360
		// Get rid of the redundant polls.
361
		if (!empty($deleted_polls))
362
		{
363
			require_once(SUBSDIR . '/Poll.subs.php');
364
			removePoll($deleted_polls);
365
		}
366
367
		$this->_updateStats($affected_msgs, $id_topic, $target_subject, $enforce_subject);
0 ignored issues
show
Documentation introduced by
$enforce_subject is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
368
369
		return array($id_topic, $target_board);
370
	}
371
372
	/**
373
	 * Takes care of updating all the relevant statistics
374
	 *
375
	 * @param int[] $affected_msgs
376
	 * @param int $id_topic
377
	 * @param string $target_subject
378
	 * @param bool $enforce_subject
379
	 * @throws Elk_Exception
380
	 */
381
	protected function _updateStats($affected_msgs, $id_topic, $target_subject, $enforce_subject)
382
	{
383
		// Cycle through each board...
384
		foreach ($this->_boardTotals as $id_board => $stats)
385
			decrementBoard($id_board, $stats);
386
387
		// Determine the board the final topic resides in
388
		$topic_info = getTopicInfo($id_topic);
389
		$id_board = $topic_info['id_board'];
390
391
		// Update all the statistics.
392
		require_once(SUBSDIR . '/Topic.subs.php');
393
		updateTopicStats();
394
395
		require_once(SUBSDIR . '/Messages.subs.php');
396
		updateSubjectStats($id_topic, $target_subject);
397
		updateLastMessages($this->boards);
398
399
		logAction('merge', array('topic' => $id_topic, 'board' => $id_board));
400
401
		// Notify people that these topics have been merged?
402
		require_once(SUBSDIR . '/Notification.subs.php');
403
		sendNotifications($id_topic, 'merge');
404
405
		// Grab the response prefix (like 'Re: ') in the default forum language.
406
		$response_prefix = response_prefix();
407
408
		// If there's a search index that needs updating, update it...
409
		$search = new \ElkArte\Search\Search;
410
		$searchAPI = $search->findSearchAPI();
411
		if (is_callable(array($searchAPI, 'topicMerge')))
412
			$searchAPI->topicMerge($id_topic, $this->_topics, $affected_msgs, empty($enforce_subject) ? null : array($response_prefix, $target_subject));
413
	}
414
415
	/**
416
	 * Grabs all the details of the topics involved in the merge process and loads
417
	 * then in $this->topic_data
418
	 */
419
	protected function _loadTopicDetails()
420
	{
421
		global $scripturl, $modSettings, $user_info;
422
423
		// Joy of all joys, make sure they're not pi**ing about with unapproved topics they can't see :P
424
		if ($modSettings['postmod_active'])
425
			$can_approve_boards = !empty($user_info['mod_cache']['ap']) ? $user_info['mod_cache']['ap'] : boardsAllowedTo('approve_posts');
426
427
		// Get info about the topics and polls that will be merged.
428
		$request = $this->_db->query('', '
429
			SELECT
430
				t.id_topic, t.id_board, b.id_cat, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts,
431
				m1.subject, m1.poster_time AS time_started, COALESCE(mem1.id_member, 0) AS id_member_started, COALESCE(mem1.real_name, m1.poster_name) AS name_started,
432
				m2.poster_time AS time_updated, COALESCE(mem2.id_member, 0) AS id_member_updated, COALESCE(mem2.real_name, m2.poster_name) AS name_updated
433
			FROM {db_prefix}topics AS t
434
				INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg)
435
				INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg)
436
				LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member)
437
				LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member)
438
				LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
439
			WHERE t.id_topic IN ({array_int:topic_list})
440
			ORDER BY t.id_first_msg
441
			LIMIT {int:limit}',
442
			array(
443
				'topic_list' => $this->_topics,
444
				'limit' => count($this->_topics),
445
			)
446
		);
447
		if ($this->_db->num_rows($request) < 2)
448
		{
449
			$this->_db->free_result($request);
450
451
			$this->_errors[] = array('no_topic_id', true);
452
453
			return false;
454
		}
455
		while ($row = $this->_db->fetch_assoc($request))
456
		{
457
			// Make a note for the board counts...
458 View Code Duplication
			if (!isset($this->_boardTotals[$row['id_board']]))
459
			{
460
				$this->_boardTotals[$row['id_board']] = array(
461
					'num_posts' => 0,
462
					'num_topics' => 0,
463
					'unapproved_posts' => 0,
464
					'unapproved_topics' => 0
465
				);
466
			}
467
468
			// We can't see unapproved topics here?
469
			if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards))
0 ignored issues
show
Bug introduced by
The variable $can_approve_boards does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
470
				continue;
471
			elseif (!$row['approved'])
472
				$this->_boardTotals[$row['id_board']]['unapproved_topics']++;
473
			else
474
				$this->_boardTotals[$row['id_board']]['num_topics']++;
475
476
			$this->_boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
477
			$this->_boardTotals[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0);
478
479
			$this->topic_data[$row['id_topic']] = array(
480
				'id' => $row['id_topic'],
481
				'board' => $row['id_board'],
482
				'poll' => $row['id_poll'],
483
				'num_views' => $row['num_views'],
484
				'subject' => $row['subject'],
485
				'started' => array(
486
					'time' => standardTime($row['time_started']),
487
					'html_time' => htmlTime($row['time_started']),
488
					'timestamp' => forum_time(true, $row['time_started']),
489
					'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'],
490
					'link' => empty($row['id_member_started']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_started'] . '">' . $row['name_started'] . '</a>'
491
				),
492
				'updated' => array(
493
					'time' => standardTime($row['time_updated']),
494
					'html_time' => htmlTime($row['time_updated']),
495
					'timestamp' => forum_time(true, $row['time_updated']),
496
					'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'],
497
					'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['name_updated'] . '</a>'
498
				)
499
			);
500
			$this->_num_views += $row['num_views'];
501
			$this->boards[] = $row['id_board'];
502
503
			// If there's no poll, id_poll == 0...
504
			if ($row['id_poll'] > 0)
505
				$this->_polls[] = $row['id_poll'];
506
507
			// Store the id_topic with the lowest id_first_msg.
508
			if (empty($this->firstTopic))
509
			{
510
				$this->firstTopic = $row['id_topic'];
511
				$this->firstBoard = $row['id_board'];
512
			}
513
514
			$this->_is_sticky = max($this->_is_sticky, $row['is_sticky']);
515
		}
516
		$this->_db->free_result($request);
517
518
		$this->boards = array_map('intval', array_values(array_unique($this->boards)));
519
520
		// If we didn't get any topics then they've been messing with unapproved stuff.
521
		if (empty($this->topic_data))
522
		{
523
			$this->_errors[] = array('no_topic_id', true);
524
		}
525
526
		return true;
527
	}
528
}
529