Passed
Push — master ( fcbd1b...c90017 )
by Spuds
01:07 queued 21s
created

Unread   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 423
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 159
dl 0
loc 423
ccs 0
cts 261
cp 0
rs 4.5599
c 3
b 0
f 0
wmc 58

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getUnreads() 0 6 2
A setEarliestMsg() 0 3 1
A setSorting() 0 4 1
A setAction() 0 4 2
A bodyPreview() 0 6 2
A setBoards() 0 6 2
A isSortAsc() 0 3 1
A __construct() 0 8 1
A numUnreads() 0 8 2
B _countRecentTopics() 0 22 7
A _countUnreadReplies() 0 20 3
D _getUnreadReplies() 0 105 19
C _getUnreadTopics() 0 78 15

How to fix   Complexity   

Complex Class

Complex classes like Unread 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.

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

1
<?php
2
3
/**
4
 * Find and retrieve information about recently posted topics, messages, and the like.
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.7
15
 *
16
 */
17
18
/**
19
 * Unread posts and replies Controller
20
 */
21
class Unread
22
{
23
	const UNREAD = 0;
24
	const UNREADREPLIES = 1;
25
26
	/** @var bool */
27
	private $_ascending = false;
28
	/** @var string */
29
	private $_sort_query = '';
30
	/** @var int */
31
	private $_num_topics = 0;
32
	/** @var int */
33
	private $_min_message = 0;
34
	/** @var int */
35
	private $_action = self::UNREAD;
36
	/** @var int */
37
	private $_earliest_msg = 0;
38
	/** @var bool */
39
	private $_showing_all_topics = false;
40
	/** @var int */
41
	private $_user_id = 0;
42
	/** @var bool */
43
	private $_post_mod = false;
44
	/** @var bool */
45
	private $_unwatch = false;
46
	/** @var Database|null */
47
	private $_db = null;
48
	/** @var int|string */
49
	private $_preview_bodies = 0;
50
51
	/**
52
	 * Parameters for the main query.
53
	 */
54
	private $_query_parameters = array();
55
56
	/**
57
	 * Constructor
58
	 *
59
	 * @param int $user - ID of the user
60
	 * @param bool|int $post_mod - if post moderation is active or not
61
	 * @param bool|int $unwatch - if unwatch topics is active or not
62
	 * @param bool|int $showing_all_topics - Is the user looking at all the unread
63
	 *             replies, or the recent topics?
64
	 */
65
	public function __construct($user, $post_mod, $unwatch, $showing_all_topics = false)
66
	{
67
		$this->_user_id = (int) $user;
68
		$this->_post_mod = (bool) $post_mod;
69
		$this->_unwatch = (bool) $unwatch;
70
		$this->_showing_all_topics = (bool) $showing_all_topics;
71
72
		$this->_db = database();
73
	}
74
75
	/**
76
	 * Sets the boards the member is looking at
77
	 *
78
	 * @param int|int[] $boards - the id of the boards
79
	 */
80
	public function setBoards($boards)
81
	{
82
		if (is_array($boards))
83
			$this->_query_parameters['boards'] = $boards;
84
		else
85
			$this->_query_parameters['boards'] = array($boards);
86
	}
87
88
	/**
89
	 * The action the user is performing
90
	 *
91
	 * @param int $action - Unread::UNREAD, Unread::UNREADREPLIES
92
	 */
93
	public function setAction($action)
94
	{
95
		if (in_array($action, array(self::UNREAD, self::UNREADREPLIES)))
96
			$this->_action = $action;
97
	}
98
99
	/**
100
	 * Sets the lower message id to be taken in consideration
101
	 *
102
	 * @param int $msg_id - id of the earliest message to consider
103
	 */
104
	public function setEarliestMsg($msg_id)
105
	{
106
		$this->_earliest_msg = (int) $msg_id;
107
	}
108
109
	/**
110
	 * Sets the sorting query and the direction
111
	 *
112
	 * @param string $query - The query to be used in the ORDER clause
113
	 * @param bool|int $asc - If the sorting is ascending or not
114
	 */
115
	public function setSorting($query, $asc)
116
	{
117
		$this->_sort_query = $query;
118
		$this->_ascending = $asc;
0 ignored issues
show
Documentation Bug introduced by
It seems like $asc can also be of type integer. However, the property $_ascending is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
119
	}
120
121
	/**
122
	 * Return the sorting direction
123
	 *
124
	 * @return boolean
125
	 */
126
	public function isSortAsc()
127
	{
128
		return $this->_ascending;
129
	}
130
131
	/**
132
	 * Sets if the data returned by the class will include a shorted version
133
	 * of the body of the last message.
134
	 *
135
	 * @param bool|int $chars - The number of chars to retrieve.
136
	 *                 If true it will return the entire body,
137
	 *                 if 0 no preview will be generated.
138
	 */
139
	public function bodyPreview($chars)
140
	{
141
		if ($chars === true)
142
			$this->_preview_bodies = 'all';
143
		else
144
			$this->_preview_bodies = (int) $chars;
145
	}
146
147
	/**
148
	 * Counts the number of unread topics or messages
149
	 *
150
	 * @param bool $first_login - If this is the first login of the user
151
	 * @param int $id_msg_last_visit - highest id_msg found during the last visit
152
	 */
153
	public function numUnreads($first_login = false, $id_msg_last_visit = 0)
154
	{
155
		if ($this->_action === self::UNREAD)
156
			$this->_countRecentTopics($first_login, $id_msg_last_visit);
157
		else
158
			$this->_countUnreadReplies();
159
160
		return $this->_num_topics;
161
	}
162
163
	/**
164
	 * Retrieves unread topics or messages
165
	 *
166
	 * @param string $join - kind of "JOIN" to execute. If 'topic' JOINs boards on
167
	 *                       the topics table, otherwise ('message') the JOIN is on
168
	 *                       the messages table
169
	 * @param int $start - position to start the query
170
	 * @param int $limit - number of entries to grab
171
	 * @param bool $include_avatars - if avatars should be retrieved as well
172
	 * @return mixed[] - see Topic_Util::prepareContext
173
	 */
174
	public function getUnreads($join, $start, $limit, $include_avatars)
175
	{
176
		if ($this->_action === self::UNREAD)
177
			return $this->_getUnreadTopics($join, $start, $limit, $include_avatars);
178
		else
179
			return $this->_getUnreadReplies($start, $limit, $include_avatars);
180
	}
181
182
	/**
183
	 * Retrieves unread topics, used in *all* unread replies with temp table and
184
	 * new posts since last visit
185
	 *
186
	 * @param string $join - kind of "JOIN" to execute. If 'topic' JOINs boards on
187
	 *                       the topics table, otherwise ('message') the JOIN is on
188
	 *                       the messages table
189
	 * @param int $start - position to start the query
190
	 * @param int $limit - number of entries to grab
191
	 * @param bool|int $include_avatars - if avatars should be retrieved as well
192
	 * @return mixed[] - see Topic_Util::prepareContext
193
	 */
194
	private function _getUnreadTopics($join, $start, $limit, $include_avatars = false)
195
	{
196
		if ($this->_preview_bodies == 'all')
197
			$body_query = 'ml.body AS last_body, ms.body AS first_body,';
198
		else
199
		{
200
			// If empty, no preview at all
201
			if (empty($this->_preview_bodies))
202
				$body_query = '';
203
			// Default: a SUBSTRING
204
			else
205
				$body_query = 'SUBSTRING(ml.body, 1, ' . ($this->_preview_bodies + 256) . ') AS last_body, SUBSTRING(ms.body, 1, ' . ($this->_preview_bodies + 256) . ') AS first_body,';
206
		}
207
208
		if (!empty($include_avatars))
209
		{
210
			// Double equal comparison for 1 because it is backward compatible with 1.0 where the value was true/false
211
			if ($include_avatars == 1 || $include_avatars === 3)
212
			{
213
				$custom_selects = array('meml.avatar', 'COALESCE(a.id_attach, 0) AS id_attach', 'a.filename', 'a.attachment_type', 'meml.email_address');
214
				$custom_joins = array('LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = ml.id_member AND a.id_member != 0)');
215
			}
216
			else
217
			{
218
				$custom_selects = array();
219
				$custom_joins = array();
220
			}
221
222
			if ($include_avatars === 2 || $include_avatars === 3)
223
			{
224
				$custom_selects = array_merge($custom_selects, array('memf.avatar AS avatar_first', 'COALESCE(af.id_attach, 0) AS id_attach_first', 'af.filename AS filename_first', 'af.attachment_type AS attachment_type_first', 'memf.email_address AS email_address_first'));
225
				$custom_joins = array_merge($custom_joins, array('LEFT JOIN {db_prefix}attachments AS af ON (af.id_member = mf.id_member AND af.id_member != 0)'));
226
			}
227
		}
228
229
		$request = $this->_db->query('substring', '
0 ignored issues
show
Bug introduced by
The method query() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

229
		/** @scrutinizer ignore-call */ 
230
  $request = $this->_db->query('substring', '

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
230
			SELECT
231
				ms.subject AS first_subject, ms.poster_time AS first_poster_time, ms.poster_name AS first_member_name,
232
				ms.id_topic, t.id_board, b.name AS bname, t.num_replies, t.num_views, t.num_likes, t.approved,
233
				ms.id_member AS first_id_member, ml.id_member AS last_id_member, ml.poster_name AS last_member_name,
234
				ml.poster_time AS last_poster_time, COALESCE(mems.real_name, ms.poster_name) AS first_display_name,
235
				COALESCE(meml.real_name, ml.poster_name) AS last_display_name, ml.subject AS last_subject,
236
				ml.icon AS last_icon, ms.icon AS first_icon, t.id_poll, t.is_sticky, t.locked, ml.modified_time AS last_modified_time,
237
				COALESCE(lt.id_msg, lmr.id_msg, -1) + 1 AS new_from,
238
				' . $body_query . '
239
				' . (!empty($custom_selects) ? implode(',', $custom_selects) . ', ' : '') . '
240
				ml.smileys_enabled AS last_smileys, ms.smileys_enabled AS first_smileys, t.id_first_msg, t.id_last_msg
241
			FROM {db_prefix}messages AS ms
242
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ms.id_topic AND t.id_first_msg = ms.id_msg)
243
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)' . ($join == 'topics' ? '
244
				LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)' : '
245
				LEFT JOIN {db_prefix}boards AS b ON (b.id_board = ms.id_board)') . '
246
				LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
247
				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
248
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})' . (!empty($custom_joins) ? implode("\n\t\t\t\t", $custom_joins) : '') . '
249
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
250
			WHERE t.id_board IN ({array_int:boards})
251
				AND t.id_last_msg >= {int:min_message}
252
				AND COALESCE(lt.id_msg, lmr.id_msg, 0) < ml.id_msg' .
253
				($this->_post_mod ? ' AND ms.approved = {int:is_approved}' : '') .
254
				($this->_unwatch ? ' AND COALESCE(lt.unwatched, 0) != 1' : '') . '
255
			ORDER BY {raw:order}
256
			LIMIT {int:offset}, {int:limit}',
257
			array_merge($this->_query_parameters, array(
258
				'current_member' => $this->_user_id,
259
				'min_message' => $this->_min_message,
260
				'is_approved' => 1,
261
				'order' => $this->_sort_query . ($this->_ascending ? '' : ' DESC'),
262
				'offset' => $start,
263
				'limit' => $limit,
264
			))
265
		);
266
		$topics = array();
267
		while ($row = $this->_db->fetch_assoc($request))
268
			$topics[] = $row;
269
		$this->_db->free_result($request);
270
271
		return Topic_Util::prepareContext($topics, true, ((int) $this->_preview_bodies) + 128);
272
	}
273
274
	/**
275
	 * Counts unread replies
276
	 */
277
	private function _countUnreadReplies()
278
	{
279
		$request = $this->_db->query('unread_fetch_topic_count', '
280
			SELECT COUNT(DISTINCT t.id_topic), MIN(t.id_last_msg)
281
			FROM {db_prefix}topics AS t
282
				INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
283
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
284
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
285
			WHERE t.id_board IN ({array_int:boards})
286
				AND m.id_member = {int:current_member}
287
				AND COALESCE(lt.id_msg, lmr.id_msg, 0) < t.id_last_msg' . ($this->_post_mod ? '
288
				AND t.approved = {int:is_approved}' : '') . ($this->_unwatch ? '
289
				AND COALESCE(lt.unwatched, 0) != 1' : ''),
290
			array_merge($this->_query_parameters, array(
291
				'current_member' => $this->_user_id,
292
				'is_approved' => 1,
293
			))
294
		);
295
		list ($this->_num_topics, $this->_min_message) = $this->_db->fetch_row($request);
296
		$this->_db->free_result($request);
297
	}
298
299
	/**
300
	 * Counts unread topics, used in *all* unread replies with temp table and
301
	 * new posts since last visit
302
	 *
303
	 * @param bool $is_first_login - if the member has already logged in at least
304
	 *             once, then there is an $id_msg_last_visit
305
	 * @param int $id_msg_last_visit - highest id_msg found during the last visit
306
	 */
307
	private function _countRecentTopics($is_first_login, $id_msg_last_visit = 0)
308
	{
309
		$request = $this->_db->query('', '
310
			SELECT COUNT(*), MIN(t.id_last_msg)
311
			FROM {db_prefix}topics AS t
312
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
313
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
314
			WHERE t.id_board IN ({array_int:boards})' . ($this->_showing_all_topics && !empty($this->_earliest_msg) ? '
315
				AND t.id_last_msg > {int:earliest_msg}' : (!$this->_showing_all_topics && $is_first_login ? '
316
				AND t.id_last_msg > {int:id_msg_last_visit}' : '')) . '
317
				AND COALESCE(lt.id_msg, lmr.id_msg, 0) < t.id_last_msg' .
318
				($this->_post_mod ? ' AND t.approved = {int:is_approved}' : '') .
319
				($this->_unwatch ? ' AND COALESCE(lt.unwatched, 0) != 1' : ''),
320
			array_merge($this->_query_parameters, array(
321
				'current_member' => $this->_user_id,
322
				'earliest_msg' => $this->_earliest_msg,
323
				'id_msg_last_visit' => $id_msg_last_visit,
324
				'is_approved' => 1,
325
			))
326
		);
327
		list ($this->_num_topics, $this->_min_message) = $this->_db->fetch_row($request);
328
		$this->_db->free_result($request);
329
	}
330
331
	/**
332
	 * Retrieves unread replies since last visit
333
	 *
334
	 * @param int $start - position to start the query
335
	 * @param int $limit - number of entries to grab
336
	 * @param bool|int $include_avatars - if avatars should be retrieved as well
337
	 * @return mixed[] - see Topic_Util::prepareContext
338
	 */
339
	private function _getUnreadReplies($start, $limit, $include_avatars = false)
340
	{
341
        $request = $this->_db->query('unread_replies', '
342
				SELECT DISTINCT t.id_topic
343
				FROM {db_prefix}topics AS t
344
					INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic AND m.id_member = {int:current_member})' . (strpos($this->_sort_query, 'ms.') === false ? '' : '
345
					INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)') . (strpos($this->_sort_query, 'mems.') === false ? '' : '
346
					LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)') . '
347
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
348
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
349
				WHERE t.id_board IN ({array_int:boards})
350
					AND t.id_last_msg >= {int:min_message}
351
					AND COALESCE(lt.id_msg, lmr.id_msg, 0) < t.id_last_msg' .
352
            ($this->_post_mod ? ' AND t.approved = {int:is_approved}' : '') .
353
            ($this->_unwatch ? ' AND COALESCE(lt.unwatched, 0) != 1' : '') . '
354
				ORDER BY {raw:order}
355
				LIMIT {int:offset}, {int:limit}',
356
            array_merge($this->_query_parameters, array(
357
                'current_member' => $this->_user_id,
358
                'min_message' => $this->_min_message,
359
                'is_approved' => 1,
360
                'order' => $this->_sort_query . ($this->_ascending ? '' : ' DESC'),
361
                'offset' => $start,
362
                'limit' => $limit,
363
            ))
364
        );
365
366
		$topics = array();
367
		while ($row = $this->_db->fetch_assoc($request))
368
			$topics[] = $row['id_topic'];
369
		$this->_db->free_result($request);
370
371
		// Sanity... where have you gone?
372
		if (empty($topics))
373
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array<mixed,mixed>.
Loading history...
374
375
		if ($this->_preview_bodies == 'all')
376
			$body_query = 'ml.body AS last_body, ms.body AS first_body,';
377
		else
378
		{
379
			// If empty, no preview at all
380
			if (empty($this->_preview_bodies))
381
				$body_query = '';
382
			// Default: a SUBSTRING
383
			else
384
				$body_query = 'SUBSTRING(ml.body, 1, ' . ($this->_preview_bodies + 256) . ') AS last_body, SUBSTRING(ms.body, 1, ' . ($this->_preview_bodies + 256) . ') AS first_body,';
385
		}
386
387
		if (!empty($include_avatars))
388
		{
389
			// Double equal comparison for 1 because it is backward compatible with 1.0 where the value was true/false
390
			if ($include_avatars == 1 || $include_avatars === 3)
391
			{
392
				$custom_selects = array('meml.avatar', 'COALESCE(a.id_attach, 0) AS id_attach', 'a.filename', 'a.attachment_type', 'meml.email_address');
393
				$custom_joins = array('LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = ml.id_member AND a.id_member != 0)');
394
			}
395
			else
396
			{
397
				$custom_selects = array();
398
				$custom_joins = array();
399
			}
400
401
			if ($include_avatars === 2 || $include_avatars === 3)
402
			{
403
				$custom_selects = array_merge($custom_selects, array('memf.avatar AS avatar_first', 'COALESCE(af.id_attach, 0) AS id_attach_first', 'af.filename AS filename_first', 'af.attachment_type AS attachment_type_first', 'memf.email_address AS email_address_first'));
404
				$custom_joins = array_merge($custom_joins, array('LEFT JOIN {db_prefix}attachments AS af ON (af.id_member = ms.id_member AND af.id_member != 0)'));
405
			}
406
		}
407
408
		$request = $this->_db->query('substring', '
409
			SELECT
410
				ms.subject AS first_subject, ms.poster_time AS first_poster_time, ms.id_topic, t.id_board, b.name AS bname,
411
				ms.poster_name AS first_member_name, ml.poster_name AS last_member_name, t.approved,
412
				t.num_replies, t.num_views, t.num_likes, ms.id_member AS first_id_member, ml.id_member AS last_id_member,
413
				ml.poster_time AS last_poster_time, COALESCE(mems.real_name, ms.poster_name) AS first_display_name,
414
				COALESCE(meml.real_name, ml.poster_name) AS last_display_name, ml.subject AS last_subject,
415
				ml.icon AS last_icon, ms.icon AS first_icon, t.id_poll, t.is_sticky, t.locked, ml.modified_time AS last_modified_time,
416
				COALESCE(lt.id_msg, lmr.id_msg, -1) + 1 AS new_from,
417
				' . $body_query . '
418
				' . (!empty($custom_selects) ? implode(',', $custom_selects) . ', ' : '') . '
419
				ml.smileys_enabled AS last_smileys, ms.smileys_enabled AS first_smileys, t.id_first_msg, t.id_last_msg
420
			FROM {db_prefix}topics AS t
421
				INNER JOIN {db_prefix}messages AS ms ON (ms.id_topic = t.id_topic AND ms.id_msg = t.id_first_msg)
422
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
423
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
424
				LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
425
				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
426
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
427
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})' . (!empty($custom_joins) ? implode("\n\t\t\t\t", $custom_joins) : '') . '
428
			WHERE t.id_topic IN ({array_int:topic_list})
429
			ORDER BY {raw:order}
430
			LIMIT {int:limit}',
431
			array(
432
				'current_member' => $this->_user_id,
433
				'order' => $this->_sort_query . ($this->_ascending ? '' : ' DESC'),
434
				'topic_list' => $topics,
435
				'limit' => count($topics),
436
			)
437
		);
438
		$return = array();
439
		while ($row = $this->_db->fetch_assoc($request))
440
			$return[] = $row;
441
		$this->_db->free_result($request);
442
443
		return Topic_Util::prepareContext($return, true, ((int) $this->_preview_bodies) + 128);
444
	}
445
}
446