Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
delete.php
001 <?php
002 /**
003 *
004 * This file is part of the phpBB Forum Software package.
005 *
006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
007 * @license GNU General Public License, version 2 (GPL-2.0)
008 *
009 * For full copyright and license information, please see
010 * the docs/CREDITS.txt file.
011 *
012 */
013
014 namespace phpbb\attachment;
015
016 use phpbb\config\config;
017 use phpbb\db\driver\driver_interface;
018 use phpbb\event\dispatcher;
019 use phpbb\filesystem\filesystem;
020
021 /**
022 * Attachment delete class
023 */
024 class delete
025 {
026 /** @var config */
027 protected $config;
028
029 /** @var driver_interface */
030 protected $db;
031
032 /** @var dispatcher */
033 protected $dispatcher;
034
035 /** @var filesystem */
036 protected $filesystem;
037
038 /** @var resync */
039 protected $resync;
040
041 /** @var string phpBB root path */
042 protected $phpbb_root_path;
043
044 /** @var array Attachement IDs */
045 protected $ids;
046
047 /** @var string SQL ID string */
048 private $sql_id;
049
050 /** @var string SQL where string */
051 private $sql_where = '';
052
053 /** @var int Number of deleted items */
054 private $num_deleted;
055
056 /** @var array Post IDs */
057 private $post_ids = array();
058
059 /** @var array Message IDs */
060 private $message_ids = array();
061
062 /** @var array Topic IDs */
063 private $topic_ids = array();
064
065 /** @var array Info of physical file */
066 private $physical = array();
067
068 /**
069 * Attachment delete class constructor
070 *
071 * @param config $config
072 * @param driver_interface $db
073 * @param dispatcher $dispatcher
074 * @param filesystem $filesystem
075 * @param resync $resync
076 * @param string $phpbb_root_path
077 */
078 public function __construct(config $config, driver_interface $db, dispatcher $dispatcher, filesystem $filesystem, resync $resync, $phpbb_root_path)
079 {
080 $this->config = $config;
081 $this->db = $db;
082 $this->dispatcher = $dispatcher;
083 $this->filesystem = $filesystem;
084 $this->resync = $resync;
085 $this->phpbb_root_path = $phpbb_root_path;
086 }
087
088 /**
089 * Delete Attachments
090 *
091 * @param string $mode can be: post|message|topic|attach|user
092 * @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids
093 * @param bool $resync set this to false if you are deleting posts or topics
094 *
095 * @return int|bool Number of deleted attachments or false if something
096 * went wrong during attachment deletion
097 */
098 public function delete($mode, $ids, $resync = true)
099 {
100 if (!$this->set_attachment_ids($ids))
101 {
102 return false;
103 }
104
105 $this->set_sql_constraints($mode);
106
107 $sql_id = $this->sql_id;
108
109 /**
110 * Perform additional actions before collecting data for attachment(s) deletion
111 *
112 * @event core.delete_attachments_collect_data_before
113 * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
114 * @var mixed ids Array or comma separated list of ids corresponding to the mode
115 * @var bool resync Flag indicating if posts/messages/topics should be synchronized
116 * @var string sql_id The field name to collect/delete data for depending on the mode
117 * @since 3.1.7-RC1
118 */
119 $vars = array(
120 'mode',
121 'ids',
122 'resync',
123 'sql_id',
124 );
125 extract($this->dispatcher->trigger_event('core.delete_attachments_collect_data_before', compact($vars)));
126
127 $this->sql_id = $sql_id;
128 unset($sql_id);
129
130 // Collect post and topic ids for later use if we need to touch remaining entries (if resync is enabled)
131 $this->collect_attachment_info($resync);
132
133 // Delete attachments from database
134 $this->delete_attachments_from_db($mode, $ids, $resync);
135
136 $sql_id = $this->sql_id;
137 $post_ids = $this->post_ids;
138 $topic_ids = $this->topic_ids;
139 $message_ids = $this->message_ids;
140 $physical = $this->physical;
141 $num_deleted = $this->num_deleted;
142
143 /**
144 * Perform additional actions after attachment(s) deletion from the database
145 *
146 * @event core.delete_attachments_from_database_after
147 * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
148 * @var mixed ids Array or comma separated list of ids corresponding to the mode
149 * @var bool resync Flag indicating if posts/messages/topics should be synchronized
150 * @var string sql_id The field name to collect/delete data for depending on the mode
151 * @var array post_ids Array with post ids for deleted attachment(s)
152 * @var array topic_ids Array with topic ids for deleted attachment(s)
153 * @var array message_ids Array with private message ids for deleted attachment(s)
154 * @var array physical Array with deleted attachment(s) physical file(s) data
155 * @var int num_deleted The number of deleted attachment(s) from the database
156 * @since 3.1.7-RC1
157 */
158 $vars = array(
159 'mode',
160 'ids',
161 'resync',
162 'sql_id',
163 'post_ids',
164 'topic_ids',
165 'message_ids',
166 'physical',
167 'num_deleted',
168 );
169 extract($this->dispatcher->trigger_event('core.delete_attachments_from_database_after', compact($vars)));
170
171 $this->sql_id = $sql_id;
172 $this->post_ids = $post_ids;
173 $this->topic_ids = $topic_ids;
174 $this->message_ids = $message_ids;
175 $this->physical = $physical;
176 $this->num_deleted = $num_deleted;
177 unset($sql_id, $post_ids, $topic_ids, $message_ids, $physical, $num_deleted);
178
179 if (!$this->num_deleted)
180 {
181 return 0;
182 }
183
184 // Delete attachments from filesystem
185 $this->remove_from_filesystem($mode, $ids, $resync);
186
187 // If we do not resync, we do not need to adjust any message, post, topic or user entries
188 if (!$resync)
189 {
190 return $this->num_deleted;
191 }
192
193 // No more use for the original ids
194 unset($ids);
195
196 // Update post indicators for posts now no longer having attachments
197 $this->resync->resync('post', $this->post_ids);
198
199 // Update message table if messages are affected
200 $this->resync->resync('message', $this->message_ids);
201
202 // Now update the topics. This is a bit trickier, because there could be posts still having attachments within the topic
203 $this->resync->resync('topic', $this->topic_ids);
204
205 return $this->num_deleted;
206 }
207
208 /**
209 * Set attachment IDs
210 *
211 * @param mixed $ids ID or array of IDs
212 *
213 * @return bool True if attachment IDs were set, false if not
214 */
215 protected function set_attachment_ids($ids)
216 {
217 // 0 is as bad as an empty array
218 if (empty($ids))
219 {
220 return false;
221 }
222
223 if (is_array($ids))
224 {
225 $ids = array_unique($ids);
226 $this->ids = array_map('intval', $ids);
227 }
228 else
229 {
230 $this->ids = array((int) $ids);
231 }
232
233 return true;
234 }
235
236 /**
237 * Set SQL constraints based on mode
238 *
239 * @param string $mode Delete mode; can be: post|message|topic|attach|user
240 */
241 private function set_sql_constraints($mode)
242 {
243 switch ($mode)
244 {
245 case 'post':
246 case 'message':
247 $this->sql_id = 'post_msg_id';
248 $this->sql_where = ' AND in_message = ' . ($mode == 'message' ? 1 : 0);
249 break;
250
251 case 'topic':
252 $this->sql_id = 'topic_id';
253 break;
254
255 case 'user':
256 $this->sql_id = 'poster_id';
257 break;
258
259 case 'attach':
260 default:
261 $this->sql_id = 'attach_id';
262 break;
263 }
264 }
265
266 /**
267 * Collect info about attachment IDs
268 *
269 * @param bool $resync Whether topics/posts should be resynced after delete
270 */
271 protected function collect_attachment_info($resync)
272 {
273 // Collect post and topic ids for later use if we need to touch remaining entries (if resync is enabled)
274 $sql = 'SELECT post_msg_id, topic_id, in_message, physical_filename, thumbnail, filesize, is_orphan
275 FROM ' . ATTACHMENTS_TABLE . '
276 WHERE ' . $this->db->sql_in_set($this->sql_id, $this->ids);
277
278 $sql .= $this->sql_where;
279
280 $result = $this->db->sql_query($sql);
281
282 while ($row = $this->db->sql_fetchrow($result))
283 {
284 // We only need to store post/message/topic ids if resync is enabled and the file is not orphaned
285 if ($resync && !$row['is_orphan'])
286 {
287 if (!$row['in_message'])
288 {
289 $this->post_ids[] = $row['post_msg_id'];
290 $this->topic_ids[] = $row['topic_id'];
291 }
292 else
293 {
294 $this->message_ids[] = $row['post_msg_id'];
295 }
296 }
297
298 $this->physical[] = array('filename' => $row['physical_filename'], 'thumbnail' => $row['thumbnail'], 'filesize' => $row['filesize'], 'is_orphan' => $row['is_orphan']);
299 }
300 $this->db->sql_freeresult($result);
301
302 // IDs should be unique
303 $this->post_ids = array_unique($this->post_ids);
304 $this->message_ids = array_unique($this->message_ids);
305 $this->topic_ids = array_unique($this->topic_ids);
306 }
307
308 /**
309 * Delete attachments from database table
310 */
311 protected function delete_attachments_from_db($mode, $ids, $resync)
312 {
313 $sql_id = $this->sql_id;
314 $post_ids = $this->post_ids;
315 $topic_ids = $this->topic_ids;
316 $message_ids = $this->message_ids;
317 $physical = $this->physical;
318
319 /**
320 * Perform additional actions before attachment(s) deletion
321 *
322 * @event core.delete_attachments_before
323 * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
324 * @var mixed ids Array or comma separated list of ids corresponding to the mode
325 * @var bool resync Flag indicating if posts/messages/topics should be synchronized
326 * @var string sql_id The field name to collect/delete data for depending on the mode
327 * @var array post_ids Array with post ids for deleted attachment(s)
328 * @var array topic_ids Array with topic ids for deleted attachment(s)
329 * @var array message_ids Array with private message ids for deleted attachment(s)
330 * @var array physical Array with deleted attachment(s) physical file(s) data
331 * @since 3.1.7-RC1
332 */
333 $vars = array(
334 'mode',
335 'ids',
336 'resync',
337 'sql_id',
338 'post_ids',
339 'topic_ids',
340 'message_ids',
341 'physical',
342 );
343 extract($this->dispatcher->trigger_event('core.delete_attachments_before', compact($vars)));
344
345 $this->sql_id = $sql_id;
346 $this->post_ids = $post_ids;
347 $this->topic_ids = $topic_ids;
348 $this->message_ids = $message_ids;
349 $this->physical = $physical;
350 unset($sql_id, $post_ids, $topic_ids, $message_ids, $physical);
351
352 // Delete attachments
353 $sql = 'DELETE FROM ' . ATTACHMENTS_TABLE . '
354 WHERE ' . $this->db->sql_in_set($this->sql_id, $this->ids);
355
356 $sql .= $this->sql_where;
357
358 $this->db->sql_query($sql);
359 $this->num_deleted = $this->db->sql_affectedrows();
360 }
361
362 /**
363 * Delete attachments from filesystem
364 */
365 protected function remove_from_filesystem($mode, $ids, $resync)
366 {
367 $space_removed = $files_removed = 0;
368
369 foreach ($this->physical as $file_ary)
370 {
371 if ($this->unlink_attachment($file_ary['filename'], 'file', true) && !$file_ary['is_orphan'])
372 {
373 // Only non-orphaned files count to the file size
374 $space_removed += $file_ary['filesize'];
375 $files_removed++;
376 }
377
378 if ($file_ary['thumbnail'])
379 {
380 $this->unlink_attachment($file_ary['filename'], 'thumbnail', true);
381 }
382 }
383
384 $sql_id = $this->sql_id;
385 $post_ids = $this->post_ids;
386 $topic_ids = $this->topic_ids;
387 $message_ids = $this->message_ids;
388 $physical = $this->physical;
389 $num_deleted = $this->num_deleted;
390
391 /**
392 * Perform additional actions after attachment(s) deletion from the filesystem
393 *
394 * @event core.delete_attachments_from_filesystem_after
395 * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
396 * @var mixed ids Array or comma separated list of ids corresponding to the mode
397 * @var bool resync Flag indicating if posts/messages/topics should be synchronized
398 * @var string sql_id The field name to collect/delete data for depending on the mode
399 * @var array post_ids Array with post ids for deleted attachment(s)
400 * @var array topic_ids Array with topic ids for deleted attachment(s)
401 * @var array message_ids Array with private message ids for deleted attachment(s)
402 * @var array physical Array with deleted attachment(s) physical file(s) data
403 * @var int num_deleted The number of deleted attachment(s) from the database
404 * @var int space_removed The size of deleted files(s) from the filesystem
405 * @var int files_removed The number of deleted file(s) from the filesystem
406 * @since 3.1.7-RC1
407 */
408 $vars = array(
409 'mode',
410 'ids',
411 'resync',
412 'sql_id',
413 'post_ids',
414 'topic_ids',
415 'message_ids',
416 'physical',
417 'num_deleted',
418 'space_removed',
419 'files_removed',
420 );
421 extract($this->dispatcher->trigger_event('core.delete_attachments_from_filesystem_after', compact($vars)));
422
423 $this->sql_id = $sql_id;
424 $this->post_ids = $post_ids;
425 $this->topic_ids = $topic_ids;
426 $this->message_ids = $message_ids;
427 $this->physical = $physical;
428 $this->num_deleted = $num_deleted;
429 unset($sql_id, $post_ids, $topic_ids, $message_ids, $physical, $num_deleted);
430
431 if ($space_removed || $files_removed)
432 {
433 $this->config->increment('upload_dir_size', $space_removed * (-1), false);
434 $this->config->increment('num_files', $files_removed * (-1), false);
435 }
436 }
437
438 /**
439 * Delete attachment from filesystem
440 *
441 * @param string $filename Filename of attachment
442 * @param string $mode Delete mode
443 * @param bool $entry_removed Whether entry was removed. Defaults to false
444 * @return bool True if file was removed, false if not
445 */
446 public function unlink_attachment($filename, $mode = 'file', $entry_removed = false)
447 {
448 // Because of copying topics or modifications a physical filename could be assigned more than once. If so, do not remove the file itself.
449 $sql = 'SELECT COUNT(attach_id) AS num_entries
450 FROM ' . ATTACHMENTS_TABLE . "
451 WHERE physical_filename = '" . $this->db->sql_escape(utf8_basename($filename)) . "'";
452 $result = $this->db->sql_query($sql);
453 $num_entries = (int) $this->db->sql_fetchfield('num_entries');
454 $this->db->sql_freeresult($result);
455
456 // Do not remove file if at least one additional entry with the same name exist.
457 if (($entry_removed && $num_entries > 0) || (!$entry_removed && $num_entries > 1))
458 {
459 return false;
460 }
461
462 $filename = ($mode == 'thumbnail') ? 'thumb_' . utf8_basename($filename) : utf8_basename($filename);
463 $filepath = $this->phpbb_root_path . $this->config['upload_path'] . '/' . $filename;
464
465 try
466 {
467 if ($this->filesystem->exists($filepath))
468 {
469 $this->filesystem->remove($this->phpbb_root_path . $this->config['upload_path'] . '/' . $filename);
470 return true;
471 }
472 }
473 catch (\phpbb\filesystem\exception\filesystem_exception $exception)
474 {
475 // Fail is covered by return statement below
476 }
477
478 return false;
479 }
480 }
481