ILIAS  release_8 Revision v8.24
MailDeletionHandler.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
24use ilSetting;
26use ilLogger;
28use ilFileUtils;
31use Throwable;
32use RecursiveIteratorIterator;
33use RecursiveDirectoryIterator;
34use SplFileInfo;
35
37{
38 private const PING_THRESHOLD = 250;
39
48
49 public function __construct(
52 ?ilDBInterface $db = null,
53 ?ilSetting $setting = null,
54 ?ilLogger $logger = null,
55 ?callable $delete_directory_callback = null
56 ) {
57 global $DIC;
58
59 $this->db = $db ?? $DIC->database();
60 $this->settings = $setting ?? $DIC->settings();
61 $this->logger = $logger ?? ilLoggerFactory::getLogger('mail');
62 $this->delete_directory_callback = $delete_directory_callback;
63
64 $this->job = $job;
65 $this->collector = $collector;
66
67 $this->mail_ids_for_path_stmt = $this->db->prepare(
68 'SELECT COUNT(*) cnt FROM mail_attachment WHERE path = ?',
70 );
71 }
72
76 private function determineDeletableAttachmentPaths(): array
77 {
78 $attachment_paths = [];
79
80 $res = $this->db->query(
81 '
82 SELECT path, COUNT(mail_id) cnt_mail_ids
83 FROM mail_attachment
84 WHERE ' . $this->db->in(
85 'mail_id',
86 $this->collector->mailIdsToDelete(),
87 false,
89 ) . ' GROUP BY path'
90 );
91
92 $i = 0;
93 while ($row = $this->db->fetchAssoc($res)) {
94 if ($i > 0 && $i % self::PING_THRESHOLD) {
95 $this->job->ping();
96 }
97
98 $num_usages_total = (int) $this->db->fetchAssoc(
99 $this->db->execute(
100 $this->mail_ids_for_path_stmt,
101 [$row['path']]
102 )
103 )['cnt'];
104 $num_usages_within_deleted_mails = (int) $row['cnt_mail_ids'];
105
106 if ($num_usages_within_deleted_mails >= $num_usages_total) {
107 $attachment_paths[] = $row['path'];
108 }
109
110 ++$i;
111 }
112
113 return $attachment_paths;
114 }
115
116 private function deleteDirectory(string $directory): void
117 {
118 if ($this->delete_directory_callback !== null) {
119 call_user_func($this->delete_directory_callback, $directory);
120 } else {
121 ilFileUtils::delDir($directory);
122 }
123 }
124
125 private function deleteAttachments(): void
126 {
127 $attachment_paths = $this->determineDeletableAttachmentPaths();
128
129 $i = 0;
130 foreach ($attachment_paths as $path) {
131 if ($i > 0 && $i % self::PING_THRESHOLD) {
132 $this->job->ping();
133 }
134
135 try {
136 $path = CLIENT_DATA_DIR . '/mail/' . $path;
137 $iter = new RecursiveIteratorIterator(
138 new RecursiveDirectoryIterator($path),
139 RecursiveIteratorIterator::CHILD_FIRST
140 );
141
142 foreach ($iter as $file) {
145 $path_name = $file->getPathname();
146 if ($file->isDir()) {
147 $this->deleteDirectory($path_name);
148 $this->logger->info(
149 sprintf(
150 "Attachment directory '%s' deleted",
151 $path_name
152 )
153 );
154 } elseif (is_file($path_name) && unlink($path_name)) {
155 $this->logger->info(
156 sprintf(
157 "Attachment file '%s' deleted",
158 $path_name
159 )
160 );
161 } else {
162 $this->logger->info(
163 sprintf(
164 "Attachment file '%s' for mail_id could not be deleted " .
165 "due to missing file system permissions",
166 $path_name
167 )
168 );
169 }
170 }
171
172 $this->deleteDirectory($path);
173 $this->logger->info(
174 sprintf(
175 "Attachment directory '%s' deleted",
176 $path
177 )
178 );
179 } catch (Throwable $e) {
180 $this->logger->warning($e->getMessage());
181 $this->logger->warning($e->getTraceAsString());
182 } finally {
183 ++$i;
184 }
185 }
186
187 $this->db->manipulate(
188 'DELETE FROM mail_attachment WHERE ' .
189 $this->db->in('mail_id', $this->collector->mailIdsToDelete(), false, ilDBConstants::T_INTEGER)
190 );
191 }
192
193 private function deleteMails(): void
194 {
195 $this->db->manipulate(
196 'DELETE FROM mail WHERE ' .
197 $this->db->in('mail_id', $this->collector->mailIdsToDelete(), false, ilDBConstants::T_INTEGER)
198 );
199 }
200
201 private function deleteMarkedAsNotified(): void
202 {
203 if ((int) $this->settings->get('mail_notify_orphaned', '0') >= 1) {
204 $this->db->manipulate(
205 'DELETE FROM mail_cron_orphaned WHERE ' .
206 $this->db->in('mail_id', $this->collector->mailIdsToDelete(), false, ilDBConstants::T_INTEGER)
207 );
208 } else {
209 $this->db->manipulate('DELETE FROM mail_cron_orphaned');
210 }
211 }
212
213 public function delete(): void
214 {
215 if (count($this->collector->mailIdsToDelete()) > 0) {
216 $this->deleteAttachments();
217
218 $this->deleteMails();
219
220 $this->logger->info(
221 sprintf(
222 'Deleted mail_ids: %s',
223 implode(', ', $this->collector->mailIdsToDelete())
224 )
225 );
226
227 $this->deleteMarkedAsNotified();
228 $this->logger->info(
229 sprintf(
230 'Deleted mail_cron_orphaned mail_ids: %s',
231 implode(', ', $this->collector->mailIdsToDelete())
232 )
233 );
234 }
235 }
236}
__construct(ilMailCronOrphanedMails $job, ExpiredOrOrphanedMailsCollector $collector, ?ilDBInterface $db=null, ?ilSetting $setting=null, ?ilLogger $logger=null, ?callable $delete_directory_callback=null)
ilSetting $setting
Definition: class.ilias.php:54
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilFileUtils.
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
static getLogger(string $a_component_id)
Get component logger.
Component logger with individual log levels by component id.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
const CLIENT_DATA_DIR
Definition: constants.php:46
global $DIC
Definition: feed.php:28
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$path
Definition: ltiservices.php:32
$res
Definition: ltiservices.php:69
$i
Definition: metadata.php:41