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