ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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 int PING_THRESHOLD = 250;
39
40 private readonly ilDBInterface $db;
41 private readonly ilSetting $settings;
42 private readonly ilLogger $logger;
46
50 public function __construct(
51 private readonly ilMailCronOrphanedMails $job,
52 private readonly ExpiredOrOrphanedMailsCollector $collector,
53 ?ilDBInterface $db = null,
54 ?ilSetting $setting = null,
55 ?ilLogger $logger = null,
56 ?callable $delete_directory_callback = null
57 ) {
58 global $DIC;
59
60 $this->db = $db ?? $DIC->database();
61 $this->settings = $setting ?? $DIC->settings();
62 $this->logger = $logger ?? ilLoggerFactory::getLogger('mail');
63 $this->delete_directory_callback = $delete_directory_callback;
64
65 $this->mail_ids_for_path_stmt = $this->db->prepare(
66 'SELECT COUNT(*) cnt FROM mail_attachment WHERE path = ?',
68 );
69 }
70
74 private function determineDeletableAttachmentPaths(): array
75 {
76 $attachment_paths = [];
77
78 $res = $this->db->query(
79 '
80 SELECT path, COUNT(mail_id) cnt_mail_ids
81 FROM mail_attachment
82 WHERE ' . $this->db->in(
83 'mail_id',
84 $this->collector->mailIdsToDelete(),
85 false,
87 ) . ' GROUP BY path'
88 );
89
90 $i = 0;
91 while ($row = $this->db->fetchAssoc($res)) {
92 if ($i > 0 && $i % self::PING_THRESHOLD) {
93 $this->job->ping();
94 }
95
96 $num_usages_total = (int) $this->db->fetchAssoc(
97 $this->db->execute(
98 $this->mail_ids_for_path_stmt,
99 [$row['path']]
100 )
101 )['cnt'];
102 $num_usages_within_deleted_mails = (int) $row['cnt_mail_ids'];
103
104 if ($num_usages_within_deleted_mails >= $num_usages_total) {
105 $attachment_paths[] = $row['path'];
106 }
107
108 ++$i;
109 }
110
111 return $attachment_paths;
112 }
113
114 private function deleteDirectory(string $directory): void
115 {
116 if ($this->delete_directory_callback !== null) {
117 \call_user_func($this->delete_directory_callback, $directory);
118 } else {
119 ilFileUtils::delDir($directory);
120 }
121 }
122
123 private function deleteAttachments(): void
124 {
125 $attachment_paths = $this->determineDeletableAttachmentPaths();
126
127 $i = 0;
128 foreach ($attachment_paths as $path) {
129 if ($i > 0 && $i % self::PING_THRESHOLD) {
130 $this->job->ping();
131 }
132
133 try {
134 $path = CLIENT_DATA_DIR . '/mail/' . $path;
135 $iter = new RecursiveIteratorIterator(
136 new RecursiveDirectoryIterator($path),
137 RecursiveIteratorIterator::CHILD_FIRST
138 );
139
140 foreach ($iter as $file) {
143 $path_name = $file->getPathname();
144 if ($file->isDir()) {
145 $this->deleteDirectory($path_name);
146 $this->logger->info(
147 \sprintf(
148 "Attachment directory '%s' deleted",
149 $path_name
150 )
151 );
152 } elseif (is_file($path_name) && unlink($path_name)) {
153 $this->logger->info(
154 \sprintf(
155 "Attachment file '%s' deleted",
156 $path_name
157 )
158 );
159 } else {
160 $this->logger->info(
161 \sprintf(
162 'Attachment file \'%s\' for mail_id could not be deleted due to missing file system permissions',
163 $path_name
164 )
165 );
166 }
167 }
168
169 $this->deleteDirectory($path);
170 $this->logger->info(
171 \sprintf(
172 "Attachment directory '%s' deleted",
173 $path
174 )
175 );
176 } catch (Throwable $e) {
177 $this->logger->warning($e->getMessage());
178 $this->logger->warning($e->getTraceAsString());
179 } finally {
180 ++$i;
181 }
182 }
183
184 $this->db->manipulate(
185 'DELETE FROM mail_attachment WHERE ' .
186 $this->db->in('mail_id', $this->collector->mailIdsToDelete(), false, ilDBConstants::T_INTEGER)
187 );
188 }
189
190 private function deleteMails(): void
191 {
192 $this->db->manipulate(
193 'DELETE FROM mail WHERE ' .
194 $this->db->in('mail_id', $this->collector->mailIdsToDelete(), false, ilDBConstants::T_INTEGER)
195 );
196 }
197
198 private function deleteMarkedAsNotified(): void
199 {
200 if ((int) $this->settings->get('mail_notify_orphaned', '0') >= 1) {
201 $this->db->manipulate(
202 'DELETE FROM mail_cron_orphaned WHERE ' .
203 $this->db->in('mail_id', $this->collector->mailIdsToDelete(), false, ilDBConstants::T_INTEGER)
204 );
205 } else {
206 $this->db->manipulate('DELETE FROM mail_cron_orphaned');
207 }
208 }
209
210 public function delete(): void
211 {
212 if ($this->collector->mailIdsToDelete() !== []) {
213 $this->deleteAttachments();
214
215 $this->deleteMails();
216
217 $this->logger->info(
218 \sprintf(
219 'Deleted mail_ids: %s',
220 implode(', ', $this->collector->mailIdsToDelete())
221 )
222 );
223
224 $this->deleteMarkedAsNotified();
225 $this->logger->info(
226 \sprintf(
227 'Deleted mail_cron_orphaned mail_ids: %s',
228 implode(', ', $this->collector->mailIdsToDelete())
229 )
230 );
231 }
232 }
233}
__construct(private readonly ilMailCronOrphanedMails $job, private readonly ExpiredOrOrphanedMailsCollector $collector, ?ilDBInterface $db=null, ?ilSetting $setting=null, ?ilLogger $logger=null, ?callable $delete_directory_callback=null)
ilSetting $setting
Definition: class.ilias.php:68
Class ilDBConstants.
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.
ILIAS Setting Class.
const CLIENT_DATA_DIR
Definition: constants.php:46
Interface ilDBInterface.
Interface ilDBStatement.
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
global $DIC
Definition: shib_login.php:26