AttachmentsDisplay::prepareAttachmentImage()   D
last analyzed

Complexity

Conditions 24
Paths 100

Size

Total Lines 67
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 24
eloc 33
c 1
b 0
f 0
nc 100
nop 3
dl 0
loc 67
rs 4.1666

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
 * 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\Attachments;
15
16
/**
17
 * Class AttachmentsDisplay
18
 */
19
class AttachmentsDisplay
20
{
21
	/** @var array The good old attachments array */
22
	protected $attachments = [];
23
24
	/** @var array The message array */
25
	protected $messages = [];
26
27
	/** @var bool If unapproved posts/attachments should be shown */
28
	protected $includeUnapproved = false;
29
30
	/**
31
	 * @param int[] $messages
32
	 * @param int[] $posters
33
	 * @param bool $includeUnapproved
34
	 */
35
	public function __construct($messages, $posters, $includeUnapproved)
36
	{
37
		$this->messages = $messages;
38
		$this->includeUnapproved = $includeUnapproved;
39
40
		// Fetch attachments.
41
		if (allowedTo('view_attachments'))
42
		{
43
			// 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
44
			if (isset($posters[-1]))
45
			{
46
				unset($posters[-1]);
47
			}
48
49
			// The filter returns false when:
50
			//  - the attachment is unapproved, and
51
			//  - the viewer is not the poster of the message where the attachment is
52
			$this->getAttachments(
53
				$this->messages,
54
				$this->includeUnapproved,
55
				static fn($attachment_info, $all_posters) => !(!$attachment_info['approved'] && (!isset($all_posters[$attachment_info['id_msg']]) || $all_posters[$attachment_info['id_msg']] !== \ElkArte\User::$info->id)),
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type null|string expected by parameter $filter of ElkArte\Attachments\Atta...splay::getAttachments(). ( Ignorable by Annotation )

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

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