Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

Printpage.php ➔ PrintTopic()   F

Complexity

Conditions 61
Paths > 20000

Size

Total Lines 308
Code Lines 161

Duplication

Lines 57
Ratio 18.51 %

Importance

Changes 0
Metric Value
cc 61
eloc 161
nc 429496.7295
nop 0
dl 57
loc 308
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file contains just one function that formats a topic to be printer
5
 * friendly.
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2017 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 4
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * Format a topic to be printer friendly.
22
 * Must be called with a topic specified.
23
 * Accessed via ?action=printpage.
24
 *
25
 * @uses Printpage template, main sub-template.
26
 * @uses print_above/print_below later without the main layer.
27
 */
28
29
function PrintTopic()
30
{
31
	global $topic, $txt, $scripturl, $context, $user_info;
32
	global $board_info, $smcFunc, $modSettings;
33
34
	// Redirect to the boardindex if no valid topic id is provided.
35
	if (empty($topic))
36
		redirectexit();
37
38
	if (!empty($modSettings['disable_print_topic']))
39
	{
40
		unset($_REQUEST['action']);
41
		$context['theme_loaded'] = false;
42
		fatal_lang_error('feature_disabled', false);
43
	}
44
45
	// Whatever happens don't index this.
46
	$context['robot_no_index'] = true;
47
48
	// Get the topic starter information.
49
	$request = $smcFunc['db_query']('', '
50
		SELECT mem.id_member, m.poster_time, COALESCE(mem.real_name, m.poster_name) AS poster_name, t.id_poll
51
		FROM {db_prefix}messages AS m
52
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
53
			LEFT JOIN {db_prefix}topics as t ON (t.id_first_msg = m.id_msg)
54
		WHERE m.id_topic = {int:current_topic}
55
		ORDER BY m.id_msg
56
		LIMIT 1',
57
		array(
58
			'current_topic' => $topic,
59
		)
60
	);
61
	// Redirect to the boardindex if no valid topic id is provided.
62
	if ($smcFunc['db_num_rows']($request) == 0)
63
		redirectexit();
64
	$row = $smcFunc['db_fetch_assoc']($request);
65
	$smcFunc['db_free_result']($request);
66
67
	if (!empty($row['id_poll']))
68
	{
69
		loadLanguage('Post');
70
		// Get the question and if it's locked.
71
		$request = $smcFunc['db_query']('', '
72
			SELECT
73
				p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
74
				p.guest_vote, p.id_member, COALESCE(mem.real_name, p.poster_name) AS poster_name, p.num_guest_voters, p.reset_poll
75
			FROM {db_prefix}polls AS p
76
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member)
77
			WHERE p.id_poll = {int:id_poll}
78
			LIMIT 1',
79
			array(
80
				'id_poll' => $row['id_poll'],
81
			)
82
		);
83
		$pollinfo = $smcFunc['db_fetch_assoc']($request);
84
		$smcFunc['db_free_result']($request);
85
86
		$request = $smcFunc['db_query']('', '
87
			SELECT COUNT(DISTINCT id_member) AS total
88
			FROM {db_prefix}log_polls
89
			WHERE id_poll = {int:id_poll}
90
				AND id_member != {int:not_guest}',
91
			array(
92
				'id_poll' => $row['id_poll'],
93
				'not_guest' => 0,
94
			)
95
		);
96
		list ($pollinfo['total']) = $smcFunc['db_fetch_row']($request);
97
		$smcFunc['db_free_result']($request);
98
99
		// Total voters needs to include guest voters
100
		$pollinfo['total'] += $pollinfo['num_guest_voters'];
101
102
		// Get all the options, and calculate the total votes.
103
		$request = $smcFunc['db_query']('', '
104
			SELECT pc.id_choice, pc.label, pc.votes, COALESCE(lp.id_choice, -1) AS voted_this
105
			FROM {db_prefix}poll_choices AS pc
106
				LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_choice = pc.id_choice AND lp.id_poll = {int:id_poll} AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
107
			WHERE pc.id_poll = {int:id_poll}',
108
			array(
109
				'current_member' => $user_info['id'],
110
				'id_poll' => $row['id_poll'],
111
				'not_guest' => 0,
112
			)
113
		);
114
		$pollOptions = array();
115
		$realtotal = 0;
116
		$pollinfo['has_voted'] = false;
117 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
118
		{
119
			censorText($row['label']);
120
			$pollOptions[$row['id_choice']] = $row;
121
			$realtotal += $row['votes'];
122
			$pollinfo['has_voted'] |= $row['voted_this'] != -1;
123
		}
124
		$smcFunc['db_free_result']($request);
125
126
		// If this is a guest we need to do our best to work out if they have voted, and what they voted for.
127 View Code Duplication
		if ($user_info['is_guest'] && $pollinfo['guest_vote'] && allowedTo('poll_vote'))
128
		{
129
			if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $row['id_poll'] . ',') !== false)
130
			{
131
				// ;id,timestamp,[vote,vote...]; etc
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
132
				$guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
133
				// Find the poll we're after.
134
				foreach ($guestinfo as $i => $guestvoted)
135
				{
136
					$guestvoted = explode(',', $guestvoted);
137
					if ($guestvoted[0] == $row['id_poll'])
138
						break;
139
				}
140
				// Has the poll been reset since guest voted?
141
				if ($pollinfo['reset_poll'] > $guestvoted[1])
142
				{
143
					// Remove the poll info from the cookie to allow guest to vote again
144
					unset($guestinfo[$i]);
145
					if (!empty($guestinfo))
146
						$_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
147
					else
148
						unset($_COOKIE['guest_poll_vote']);
149
				}
150
				else
151
				{
152
					// What did they vote for?
153
					unset($guestvoted[0], $guestvoted[1]);
154
					foreach ($pollOptions as $choice => $details)
155
					{
156
						$pollOptions[$choice]['voted_this'] = in_array($choice, $guestvoted) ? 1 : -1;
0 ignored issues
show
Bug introduced by
The variable $guestvoted 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...
157
						$pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1;
158
					}
159
					unset($choice, $details, $guestvoted);
160
				}
161
				unset($guestinfo, $guestvoted, $i);
162
			}
163
		}
164
165
		$context['user']['started'] = $user_info['id'] == $row['id_member'] && !$user_info['is_guest'];
166
		// Set up the basic poll information.
167
		$context['poll'] = array(
168
			'id' => $row['id_poll'],
169
			'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'),
170
			'question' => parse_bbc($pollinfo['question']),
171
			'total_votes' => $pollinfo['total'],
172
			'change_vote' => !empty($pollinfo['change_vote']),
173
			'is_locked' => !empty($pollinfo['voting_locked']),
174
			'options' => array(),
175
			'lock' => allowedTo('poll_lock_any') || ($context['user']['started'] && allowedTo('poll_lock_own')),
176
			'edit' => allowedTo('poll_edit_any') || ($context['user']['started'] && allowedTo('poll_edit_own')),
177
			'allowed_warning' => $pollinfo['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '',
178
			'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(),
179
			'expire_time' => !empty($pollinfo['expire_time']) ? timeformat($pollinfo['expire_time']) : 0,
180
			'has_voted' => !empty($pollinfo['has_voted']),
181
			'starter' => array(
182
				'id' => $pollinfo['id_member'],
183
				'name' => $row['poster_name'],
184
				'href' => $pollinfo['id_member'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['id_member'],
185
				'link' => $pollinfo['id_member'] == 0 ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $pollinfo['id_member'] . '">' . $row['poster_name'] . '</a>'
186
			)
187
		);
188
189
		// Make the lock and edit permissions defined above more directly accessible.
190
		$context['allow_lock_poll'] = $context['poll']['lock'];
191
		$context['allow_edit_poll'] = $context['poll']['edit'];
192
193
		// You're allowed to view the results if:
194
		// 1. you're just a super-nice-guy, or
195
		// 2. anyone can see them (hide_results == 0), or
196
		// 3. you can see them after you voted (hide_results == 1), or
197
		// 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.)
198
		$context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || ($pollinfo['hide_results'] == 1 && $context['poll']['has_voted']) || $context['poll']['is_expired'];
199
200
		// Calculate the percentages and bar lengths...
201
		$divisor = $realtotal == 0 ? 1 : $realtotal;
202
203
		// Determine if a decimal point is needed in order for the options to add to 100%.
204
		$precision = $realtotal == 100 ? 0 : 1;
205
206
		// Now look through each option, and...
207
		foreach ($pollOptions as $i => $option)
208
		{
209
			// First calculate the percentage, and then the width of the bar...
210
			$bar = round(($option['votes'] * 100) / $divisor, $precision);
211
			$barWide = $bar == 0 ? 1 : floor(($bar * 8) / 3);
212
213
			// Now add it to the poll's contextual theme data.
214
			$context['poll']['options'][$i] = array(
215
				'id' => 'options-' . $i,
216
				'percent' => $bar,
217
				'votes' => $option['votes'],
218
				'voted_this' => $option['voted_this'] != -1,
219
				// Note: IE < 8 requires us to set a width on the container, too.
220
				'bar_ndt' => $bar > 0 ? '<div class="bar" style="width: ' . ($bar * 3.5 + 4) . 'px;"><div style="width: ' . $bar * 3.5 . 'px;"></div></div>' : '',
221
				'bar_width' => $barWide,
222
				'option' => parse_bbc($option['label']),
223
				'vote_button' => '<input type="' . ($pollinfo['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '" class="input_' . ($pollinfo['max_votes'] > 1 ? 'check' : 'radio') . '">'
224
			);
225
		}
226
	}
227
228
	// Lets "output" all that info.
229
	loadTemplate('Printpage');
230
	$context['template_layers'] = array('print');
231
	$context['board_name'] = $board_info['name'];
232
	$context['category_name'] = $board_info['cat']['name'];
233
	$context['poster_name'] = $row['poster_name'];
234
	$context['post_time'] = timeformat($row['poster_time'], false);
235
	$context['parent_boards'] = array();
236
	foreach ($board_info['parent_boards'] as $parent)
237
		$context['parent_boards'][] = $parent['name'];
238
239
	// Split the topics up so we can print them.
240
	$request = $smcFunc['db_query']('', '
241
		SELECT subject, poster_time, body, COALESCE(mem.real_name, poster_name) AS poster_name, id_msg
242
		FROM {db_prefix}messages AS m
243
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
244
		WHERE m.id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && !allowedTo('approve_posts') ? '
245
			AND (m.approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR m.id_member = {int:current_member}') . ')' : '') . '
246
		ORDER BY m.id_msg',
247
		array(
248
			'current_topic' => $topic,
249
			'is_approved' => 1,
250
			'current_member' => $user_info['id'],
251
		)
252
	);
253
	$context['posts'] = array();
254
	while ($row = $smcFunc['db_fetch_assoc']($request))
255
	{
256
		// Censor the subject and message.
257
		censorText($row['subject']);
258
		censorText($row['body']);
259
260
		$context['posts'][] = array(
261
			'subject' => $row['subject'],
262
			'member' => $row['poster_name'],
263
			'time' => timeformat($row['poster_time'], false),
264
			'timestamp' => forum_time(true, $row['poster_time']),
265
			'body' => parse_bbc($row['body'], 'print'),
0 ignored issues
show
Documentation introduced by
'print' 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...
266
			'id_msg' => $row['id_msg'],
267
		);
268
269
		if (!isset($context['topic_subject']))
270
			$context['topic_subject'] = $row['subject'];
271
	}
272
	$smcFunc['db_free_result']($request);
273
274
	// Fetch attachments so we can print them if asked, enabled and allowed
275
	if (isset($_REQUEST['images']) && !empty($modSettings['attachmentEnable']) && allowedTo('view_attachments'))
276
	{
277
		$messages = array();
278
		foreach ($context['posts'] as $temp)
279
			$messages[] = $temp['id_msg'];
280
281
		// build the request
282
		$request = $smcFunc['db_query']('', '
283
			SELECT
284
				a.id_attach, a.id_msg, a.approved, a.width, a.height, a.file_hash, a.filename, a.id_folder, a.mime_type
285
			FROM {db_prefix}attachments AS a
286
			WHERE a.id_msg IN ({array_int:message_list})
287
				AND a.attachment_type = {int:attachment_type}',
288
			array(
289
				'message_list' => $messages,
290
				'attachment_type' => 0,
291
				'is_approved' => 1,
292
			)
293
		);
294
		$temp = array();
295
		while ($row = $smcFunc['db_fetch_assoc']($request))
296
		{
297
			$temp[$row['id_attach']] = $row;
298
			if (!isset($context['printattach'][$row['id_msg']]))
299
				$context['printattach'][$row['id_msg']] = array();
300
		}
301
		$smcFunc['db_free_result']($request);
302
		ksort($temp);
303
304
		// load them into $context so the template can use them
305
		foreach ($temp as $row)
306
		{
307
			if (!empty($row['width']) && !empty($row['height']))
308
			{
309
				if (!empty($modSettings['max_image_width']) && (empty($modSettings['max_image_height']) || $row['height'] * ($modSettings['max_image_width'] / $row['width']) <= $modSettings['max_image_height']))
310
				{
311 View Code Duplication
					if ($row['width'] > $modSettings['max_image_width'])
312
					{
313
						$row['height'] = floor($row['height'] * ($modSettings['max_image_width'] / $row['width']));
314
						$row['width'] = $modSettings['max_image_width'];
315
					}
316
				}
317 View Code Duplication
				elseif (!empty($modSettings['max_image_width']))
318
				{
319
					if ($row['height'] > $modSettings['max_image_height'])
320
					{
321
						$row['width'] = floor($row['width'] * $modSettings['max_image_height'] / $row['height']);
322
						$row['height'] = $modSettings['max_image_height'];
323
					}
324
				}
325
326
				$row['filename'] = getAttachmentFilename($row['filename'], $row['id_attach'], $row['id_folder'], false, $row['file_hash']);
327
328
				// save for the template
329
				$context['printattach'][$row['id_msg']][] = $row;
330
			}
331
		}
332
	}
333
334
	// Set a canonical URL for this page.
335
	$context['canonical_url'] = $scripturl . '?topic=' . $topic . '.0';
336
}
337
338
?>