Completed
Pull Request — development (#3620)
by Emanuele
07:38 queued 07:38
created

AttachmentsDisplay   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 52
eloc 98
dl 0
loc 250
rs 7.44
c 1
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 20 5
D loadAttachmentContext() 0 132 36
B getAttachments() 0 47 11

How to fix   Complexity   

Complex Class

Complex classes like AttachmentsDisplay 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 AttachmentsDisplay, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Handles the job of attachment directory management.
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
 * @version 2.0 dev
11
 *
12
 */
13
14
namespace ElkArte;
15
16
class AttachmentsDisplay
17
{
18
	/** @var The good old attachments array */
19
	protected $messages = [];
20
21
	/** @var mixed[] The good old attachments array */
22
	protected $attachments = [];
23
24
	/** @var bool If unapproved posts/attachments should be shown */
25
	protected $includeUnapproved = false;
26
27
	/**
28
	 * @param int[] $messages
29
	 * @param int[] $posters
30
	 * @param bool $includeUnapproved
31
	 */
32
	public function __construct($messages, $posters,  $includeUnapproved)
33
	{
34
		$this->messages = $messages;
0 ignored issues
show
Documentation Bug introduced by
It seems like $messages of type integer[] is incompatible with the declared type ElkArte\The of property $messages.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
35
		$this->includeUnapproved = $includeUnapproved;
36
37
		// Fetch attachments.
38
		if (allowedTo('view_attachments'))
39
		{
40
			// Reminder: this should not be necessary, it removes the current user from the list of posters if not present among the actual list of posters
41
			if (isset($posters[-1]))
42
			{
43
				unset($posters[-1]);
44
			}
45
46
			// The filter returns false when:
47
			//  - the attachment is unapproved, and
48
			//  - the viewer is not the poster of the message where the attachment is
49
			$this->getAttachments($this->messages, $this->includeUnapproved, static function ($attachment_info, $all_posters) {
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type null|string expected by parameter $filter of ElkArte\AttachmentsDisplay::getAttachments(). ( Ignorable by Annotation )

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

49
			$this->getAttachments($this->messages, $this->includeUnapproved, /** @scrutinizer ignore-type */ static function ($attachment_info, $all_posters) {
Loading history...
50
				return !(!$attachment_info['approved'] && (!isset($all_posters[$attachment_info['id_msg']]) || $all_posters[$attachment_info['id_msg']] != User::$info->id));
51
			}, $posters);
52
		}
53
	}
54
55
	/**
56
	 * Get all attachments associated with a set of posts.
57
	 *
58
	 * What it does:
59
	 *  - This does not check permissions.
60
	 *
61
	 * @param int[] $messages array of messages ids
62
	 * @param bool $includeUnapproved = false
63
	 * @param string|null $filter name of a callback function
64
	 * @param mixed[] $all_posters
65
	 *
66
	 * @return array
67
	 * @package Attachments
68
	 */
69
	protected function getAttachments($messages, $includeUnapproved = false, $filter = null, $all_posters = array())
70
	{
71
		global $modSettings;
72
73
		$db = database();
74
75
		$attachments = array();
76
		$temp = array();
77
		$db->fetchQuery('
78
			SELECT
79
				a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, COALESCE(a.size, 0) AS filesize, a.downloads, a.approved,
80
				a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ',
81
				COALESCE(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
82
				FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
83
				LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . '
84
			WHERE a.id_msg IN ({array_int:message_list})
85
				AND a.attachment_type = {int:attachment_type}',
86
			array(
87
				'message_list' => $messages,
88
				'attachment_type' => 0,
89
			)
90
		)->fetch_callback(
91
			function ($row) use ($includeUnapproved, $filter, $all_posters, &$attachments, &$temp) {
92
				if (!$row['approved'] && !$includeUnapproved
93
					&& (empty($filter) || !call_user_func($filter, $row, $all_posters)))
94
				{
95
					return;
96
				}
97
98
				$temp[$row['id_attach']] = $row;
99
100
				if (!isset($attachments[$row['id_msg']]))
101
				{
102
					$attachments[$row['id_msg']] = array();
103
				}
104
			}
105
		);
106
107
		// This is better than sorting it with the query...
108
		ksort($temp);
109
110
		foreach ($temp as $row)
111
		{
112
			$attachments[$row['id_msg']][] = $row;
113
		}
114
115
		$this->attachments = $attachments;
116
	}
117
118
	/**
119
	 * This loads an attachment's contextual data including, most importantly, its size if it is an image.
120
	 *
121
	 * What it does:
122
	 *
123
	 * - Pre-condition: $attachments array to have been filled with the proper attachment data, as Display() does.
124
	 * - It requires the view_attachments permission to calculate image size.
125
	 * - It attempts to keep the "aspect ratio" of the posted image in line, even if it has to be resized by
126
	 * the max_image_width and max_image_height settings.
127
	 *
128
	 * @param int $id_msg message number to load attachments for
129
	 * @return array of attachments
130
	 * @todo change this pre-condition, too fragile and error-prone.
131
	 *
132
	 * @package Attachments
133
	 */
134
	public function loadAttachmentContext($id_msg)
135
	{
136
		global $context, $modSettings, $scripturl, $topic;
137
138
		// Set up the attachment info - based on code by Meriadoc.
139
		$attachmentData = array();
140
		$have_unapproved = false;
141
		if (isset($this->attachments[$id_msg]) && !empty($modSettings['attachmentEnable']))
142
		{
143
			foreach ($this->attachments[$id_msg] as $i => $attachment)
144
			{
145
				if (!empty($context['ila_dont_show_attach_below']) && in_array($attachment['id_attach'], $context['ila_dont_show_attach_below']))
146
				{
147
					continue;
148
				}
149
				$attachmentData[$i] = array(
150
					'id' => $attachment['id_attach'],
151
					'name' => preg_replace('~&amp;#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', '&#\\1;', htmlspecialchars($attachment['filename'], ENT_COMPAT, 'UTF-8')),
152
					'downloads' => $attachment['downloads'],
153
					'size' => byte_format($attachment['filesize']),
154
					'byte_size' => $attachment['filesize'],
155
					'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_attach'],
156
					'link' => '<a href="' . $scripturl . '?action=dlattach;topic=' . $topic . '.0;attach=' . $attachment['id_attach'] . '">' . htmlspecialchars($attachment['filename'], ENT_COMPAT, 'UTF-8') . '</a>',
157
					'is_image' => !empty($attachment['width']) && !empty($attachment['height']) && !empty($modSettings['attachmentShowImages']),
158
					'is_approved' => $attachment['approved'],
159
					'file_hash' => $attachment['file_hash'],
160
				);
161
162
				// If something is unapproved we'll note it so we can sort them.
163
				if (!$attachment['approved'])
164
				{
165
					$have_unapproved = true;
166
				}
167
168
				if (!$attachmentData[$i]['is_image'])
169
				{
170
					continue;
171
				}
172
173
				$attachmentData[$i]['real_width'] = $attachment['width'];
174
				$attachmentData[$i]['width'] = $attachment['width'];
175
				$attachmentData[$i]['real_height'] = $attachment['height'];
176
				$attachmentData[$i]['height'] = $attachment['height'];
177
178
				// Let's see, do we want thumbs?
179
				if (!empty($modSettings['attachmentThumbnails'])
180
					&& !empty($modSettings['attachmentThumbWidth'])
181
					&& !empty($modSettings['attachmentThumbHeight'])
182
					&& ($attachment['width'] > $modSettings['attachmentThumbWidth'] || $attachment['height'] > $modSettings['attachmentThumbHeight']) && strlen($attachment['filename']) < 249)
183
				{
184
					// A proper thumb doesn't exist yet? Create one! Or, it needs update.
185
					if (empty($attachment['id_thumb'])
186
						|| $attachment['thumb_width'] > $modSettings['attachmentThumbWidth']
187
						|| $attachment['thumb_height'] > $modSettings['attachmentThumbHeight'])
188
						//|| ($attachment['thumb_width'] < $modSettings['attachmentThumbWidth'] && $attachment['thumb_height'] < $modSettings['attachmentThumbHeight']))
189
					{
190
						$filename = getAttachmentFilename($attachment['filename'], $attachment['id_attach'], $attachment['id_folder'], false, $attachment['file_hash']);
191
						$attachment = array_merge($attachment, updateAttachmentThumbnail($filename, $attachment['id_attach'], $id_msg, $attachment['id_thumb'], $attachment['filename']));
192
					}
193
194
					// Only adjust dimensions on successful thumbnail creation.
195
					if (!empty($attachment['thumb_width']) && !empty($attachment['thumb_height']))
196
					{
197
						$attachmentData[$i]['width'] = $attachment['thumb_width'];
198
						$attachmentData[$i]['height'] = $attachment['thumb_height'];
199
					}
200
				}
201
202
				if (!empty($attachment['id_thumb']))
203
				{
204
					$attachmentData[$i]['thumbnail'] = array(
205
						'id' => $attachment['id_thumb'],
206
						'href' => getUrl('action', ['action' => 'dlattach', 'topic' => $topic . '.0', 'attach' => $attachment['id_thumb'], 'image']),
207
					);
208
				}
209
				$attachmentData[$i]['thumbnail']['has_thumb'] = !empty($attachment['id_thumb']);
210
211
				// If thumbnails are disabled, check the maximum size of the image.
212
				if (!$attachmentData[$i]['thumbnail']['has_thumb'] && ((!empty($modSettings['max_image_width']) && $attachment['width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachment['height'] > $modSettings['max_image_height'])))
213
				{
214
					if (!empty($modSettings['max_image_width']) && (empty($modSettings['max_image_height']) || $attachment['height'] * $modSettings['max_image_width'] / $attachment['width'] <= $modSettings['max_image_height']))
215
					{
216
						$attachmentData[$i]['width'] = $modSettings['max_image_width'];
217
						$attachmentData[$i]['height'] = floor($attachment['height'] * $modSettings['max_image_width'] / $attachment['width']);
218
					}
219
					elseif (!empty($modSettings['max_image_width']))
220
					{
221
						$attachmentData[$i]['width'] = floor($attachment['width'] * $modSettings['max_image_height'] / $attachment['height']);
222
						$attachmentData[$i]['height'] = $modSettings['max_image_height'];
223
					}
224
				}
225
				elseif ($attachmentData[$i]['thumbnail']['has_thumb'])
226
				{
227
					// Data attributes for use in expandThumb
228
					$attachmentData[$i]['thumbnail']['lightbox'] = 'data-lightboxmessage="' . $id_msg . '" data-lightboximage="' . $attachment['id_attach'] . '"';
229
230
					/*
231
					// If the image is too large to show inline, make it a popup.
232
					// @todo this needs to be removed or depreciated
233
					if (((!empty($modSettings['max_image_width']) && $attachmentData[$i]['real_width'] > $modSettings['max_image_width']) || (!empty($modSettings['max_image_height']) && $attachmentData[$i]['real_height'] > $modSettings['max_image_height'])))
234
					{
235
						$attachmentData[$i]['thumbnail']['javascript'] = 'return reqWin(\'' . $attachmentData[$i]['href'] . ';image\', ' . ($attachment['width'] + 20) . ', ' . ($attachment['height'] + 20) . ', true);';
236
					}
237
					else
238
					{
239
						$attachmentData[$i]['thumbnail']['javascript'] = 'return expandThumb(' . $attachment['id_attach'] . ');';
240
					}
241
					*/
242
				}
243
244
				if (!$attachmentData[$i]['thumbnail']['has_thumb'])
245
				{
246
					$attachmentData[$i]['downloads']++;
247
				}
248
			}
249
		}
250
251
		// Do we need to instigate a sort?
252
		if ($have_unapproved)
253
		{
254
			// Unapproved attachments go first.
255
			usort($attachmentData, function($a, $b) {
256
				if ($a['is_approved'] === $b['is_approved'])
257
				{
258
					return 0;
259
				}
260
261
				return $a['is_approved'] > $b['is_approved'] ? -1 : 1;
262
			});
263
		}
264
265
		return $attachmentData;
266
	}
267
}
268