ILIAS  trunk Revision v11.0_alpha-1838-g59fc79e306b
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
MailFolderTableUI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 namespace ILIAS\Mail\Folder;
22 
23 use ilLanguage;
24 use ilMail;
25 use ilMailUserCache;
45 use DateTimeZone;
46 
47 class MailFolderTableUI implements \ILIAS\UI\Component\Table\DataRetrieval
48 {
49  // table actions
50  public const ACTION_SHOW = 'show';
51  public const ACTION_EDIT = 'edit';
52  public const ACTION_REPLY = 'reply';
53  public const ACTION_FORWARD = 'forward';
54  public const ACTION_DOWNLOAD_ATTACHMENT = 'download';
55  public const ACTION_PRINT = 'print';
56  public const ACTION_PROFILE = 'profile';
57  public const ACTION_MOVE_TO = 'moveTo';
58  public const ACTION_DELETE = 'delete';
59  public const ACTION_MARK_READ = 'markRead';
60  public const ACTION_MARK_UNREAD = 'marUnread';
61 
63  private array $avatars = [];
64 
68  public function __construct(
69  private readonly URLBuilder $url_builder,
70  private readonly URLBuilderToken $action_token,
71  private readonly URLBuilderToken $row_id_token,
72  private readonly URLBuilderToken $folder_token,
73  private readonly array $user_folders,
74  private readonly MailFolderData $current_folder,
75  private readonly MailFolderSearch $search,
76  private readonly ilMail $mail,
77  private readonly Factory $ui_factory,
78  private readonly Renderer $ui_renderer,
79  private readonly ilLanguage $lng,
80  private readonly ServerRequestInterface $http_request,
81  private readonly DataFactory $data_factory,
82  private readonly Refinery $refinery,
83  private readonly DateFormat $date_format,
84  private readonly string $time_format,
85  private readonly DateTimeZone $user_time_zone
86  ) {
87  }
88 
89  public function getComponent(): DataTable
90  {
91  return $this->ui_factory
92  ->table()
93  ->data(
94  $this,
95  $this->getTableTitle(),
96  $this->getColumnDefinition(),
97  )
98  ->withId(self::class)
99  ->withOrder(new Order('date', Order::DESC))
100  ->withActions($this->getActions())
101  ->withRequest($this->http_request);
102  }
103 
107  private function getColumnDefinition(): array
108  {
109  if ((int) $this->time_format === \ilCalendarSettings::TIME_FORMAT_12) {
110  $date_format = $this->data_factory->dateFormat()->withTime12($this->date_format);
111  } else {
112  $date_format = $this->data_factory->dateFormat()->withTime24($this->date_format);
113  }
114 
115  $columns = [
116  'status' => $this->ui_factory
117  ->table()
118  ->column()
119  ->statusIcon($this->lng->txt('status'))
120  ->withIsSortable(true),
121 
122  'avatar' => $this->ui_factory
123  ->table()
124  ->column()
125  ->status($this->lng->txt('personal_picture'))
126  ->withIsSortable(true),
127 
128  'sender' => $this->ui_factory
129  ->table()
130  ->column()
131  ->text($this->lng->txt('sender'))
132  ->withIsSortable(true),
133 
134  'recipients' => $this->ui_factory
135  ->table()
136  ->column()
137  ->text($this->lng->txt('recipient'))
138  ->withIsSortable(true),
139 
140  'subject' => $this->ui_factory
141  ->table()
142  ->column()
143  ->link($this->lng->txt('subject'))
144  ->withIsSortable(true),
145 
146  'attachments' => $this->ui_factory
147  ->table()
148  ->column()
149  ->status($this->lng->txt('attachments'))
150  ->withIsSortable(true),
151 
152  'date' => $this->ui_factory
153  ->table()
154  ->column()
155  ->date(
156  $this->lng->txt('date'),
157  $date_format
158  )
159  ->withIsSortable(true),
160  ];
161 
162  if ($this->current_folder->hasOutgoingMails()) {
163  unset($columns['status'], $columns['avatar'], $columns['sender']);
164  } else {
165  unset($columns['recipients']);
166  }
167 
168  return $columns;
169  }
170 
174  private function getActions(): array
175  {
176  $actions = [
177  self::ACTION_SHOW => $this->ui_factory->table()->action()->single(
178  $this->lng->txt('view'),
179  $this->url_builder->withParameter($this->action_token, self::ACTION_SHOW),
180  $this->row_id_token
181  ),
182  self::ACTION_EDIT => $this->ui_factory->table()->action()->single(
183  $this->lng->txt('edit'),
184  $this->url_builder->withParameter($this->action_token, self::ACTION_EDIT),
185  $this->row_id_token
186  ),
187  self::ACTION_REPLY => $this->ui_factory->table()->action()->single(
188  $this->lng->txt('reply'),
189  $this->url_builder->withParameter($this->action_token, self::ACTION_REPLY),
190  $this->row_id_token
191  ),
192  self::ACTION_FORWARD => $this->ui_factory->table()->action()->single(
193  $this->lng->txt('forward'),
194  $this->url_builder->withParameter($this->action_token, self::ACTION_FORWARD),
195  $this->row_id_token
196  ),
197  self::ACTION_DOWNLOAD_ATTACHMENT => $this->ui_factory->table()->action()->single(
198  $this->lng->txt('mail_download_attachment'),
199  $this->url_builder->withParameter($this->action_token, self::ACTION_DOWNLOAD_ATTACHMENT),
200  $this->row_id_token
201  ),
202  self::ACTION_PRINT => $this->ui_factory->table()->action()->single(
203  $this->lng->txt('print'),
204  $this->url_builder->withParameter($this->action_token, self::ACTION_PRINT),
205  $this->row_id_token
206  ),
207  self::ACTION_MARK_READ => $this->ui_factory->table()->action()->multi(
208  $this->lng->txt('mail_mark_read'),
209  $this->url_builder->withParameter($this->action_token, self::ACTION_MARK_READ),
210  $this->row_id_token
211  ),
212  self::ACTION_MARK_UNREAD => $this->ui_factory->table()->action()->multi(
213  $this->lng->txt('mail_mark_unread'),
214  $this->url_builder->withParameter($this->action_token, self::ACTION_MARK_UNREAD),
215  $this->row_id_token
216  ),
217  self::ACTION_DELETE => $this->ui_factory->table()->action()->standard(
218  $this->lng->txt('delete'),
219  $this->url_builder->withParameter($this->action_token, self::ACTION_DELETE),
220  $this->row_id_token
221  )->withAsync(), // for confirmation modal
222  ];
223 
224  foreach ($this->user_folders as $target_folder) {
225  if ($target_folder->getFolderId() !== $this->current_folder->getFolderId()) {
226  $action_title = $this->lng->txt('mail_move_to') . ' ' . $target_folder->getTitle();
227  if ($target_folder->isTrash()) {
228  $action_title .= ' (' . $this->lng->txt('delete') . ')';
229  }
230 
231  $actions[self::ACTION_MOVE_TO . $target_folder->getFolderId()] = $this->ui_factory
232  ->table()
233  ->action()
234  ->multi(
235  $action_title,
236  $this->url_builder
237  ->withParameter($this->action_token, self::ACTION_MOVE_TO)
238  ->withParameter($this->folder_token, (string) $target_folder->getFolderId()),
239  $this->row_id_token
240  );
241  }
242  }
243 
244  if ($this->current_folder->isDrafts()) {
245  unset($actions[self::ACTION_SHOW], $actions[self::ACTION_REPLY], $actions[self::ACTION_FORWARD]);
246  } else {
247  unset($actions[self::ACTION_EDIT]);
248  }
249 
250  if ($this->current_folder->hasOutgoingMails()) {
251  unset($actions[self::ACTION_MARK_READ], $actions[self::ACTION_MARK_UNREAD]);
252  }
253 
254  if (!$this->current_folder->isTrash()) {
255  unset($actions[self::ACTION_DELETE]);
256  }
257 
258  return $actions;
259  }
260 
261  public function getRows(
262  DataRowBuilder $row_builder,
263  array $visible_column_ids,
264  Range $range,
265  Order $order,
266  ?array $filter_data, // not used, because data is filtered by MailDataSearch
267  ?array $additional_parameters // not used
268  ): \Generator {
269  // mapping of table columns to allowed order columns of the mailbox query
270  $order_columns = [
271  'status' => MailBoxOrderColumn::STATUS,
272  'subject' => MailBoxOrderColumn::SUBJECT,
273  'sender' => MailBoxOrderColumn::FROM,
274  'recipients' => MailBoxOrderColumn::RCP_TO,
275  'date' => MailBoxOrderColumn::SEND_TIME,
276  'attachments' => MailBoxOrderColumn::ATTACHMENTS
277  ];
278 
279  [$order_column, $order_direction] = $order->join([], fn($ret, $key, $value) => [$key, $value]);
280 
281  $records = $this->search->getPagedRecords(
282  $range->getLength(),
283  $range->getStart(),
284  $order_columns[$order_column] ?? null,
285  $order_direction
286  );
287 
288  // preload user objects for display of avatar and sender
289  if ($this->current_folder->hasIncomingMails()) {
290  $user_ids = [];
291  foreach ($records as $record) {
292  if ($record->hasPersonalSender()) {
293  $user_ids[$record->getSenderId()] = $record->getSenderId();
294  }
295  }
297  }
298 
299  foreach ($records as $record) {
300  if ($this->current_folder->hasIncomingMails()) {
301  $data = [
302  'status' => $this->getStatus($record),
303  'avatar' => $this->getAvatar($record),
304  'sender' => $this->getSender($record),
305  'subject' => $this->getSubject($record),
306  'attachments' => $this->getAttachments($record),
307  'date' => $this->getDate($record)
308  ];
309  } else {
310  $data = [
311  'recipients' => $this->getRecipients($record),
312  'subject' => $this->getSubject($record),
313  'attachments' => $this->getAttachments($record),
314  'date' => $this->getDate($record)
315  ];
316  }
317 
318  yield $row_builder->buildDataRow(
319  (string) $record->getMailId(),
320  $data
321  )->withDisabledAction(self::ACTION_REPLY, !$record->hasPersonalSender())->withDisabledAction(
322  self::ACTION_DOWNLOAD_ATTACHMENT,
323  !$record->hasAttachments()
324  );
325  }
326  }
327 
328  public function getTotalRowCount(?array $filter_data, ?array $additional_parameters): ?int
329  {
330  return $this->search->getCount();
331  }
332 
333  private function getTableTitle(): string
334  {
335  if ($this->current_folder->hasIncomingMails() && $this->search->getUnread() > 0) {
336  return \sprintf(
337  '%s: %s (%s %s)',
338  $this->current_folder->getTitle(),
339  $this->search->getCount() === 1
340  ? $this->lng->txt('mail_1')
341  : \sprintf($this->lng->txt('mail_s'), $this->search->getCount()),
342  $this->search->getUnread(),
343  $this->lng->txt('unread')
344  );
345  }
346 
347  return \sprintf(
348  '%s: %s',
349  $this->current_folder->getTitle(),
350  $this->search->getCount() === 1
351  ? $this->lng->txt('mail_1')
352  : \sprintf($this->lng->txt('mail_s'), $this->search->getCount()),
353  );
354  }
355 
356  private function getAvatar(MailRecordData $record): string
357  {
358  if (!\array_key_exists($record->getSenderId(), $this->avatars)) {
359  if ($record->getSenderId() === ANONYMOUS_USER_ID) {
360  $avatar = $this->ui_factory->symbol()->avatar()->picture(
361  \ilUtil::getImagePath('logo/HeaderIconAvatar.svg'),
362  $this->getSender($record)
363  );
364  } else {
366  $avatar = $user?->getAvatar();
367  }
368 
369  $this->avatars[$record->getSenderId()] = $avatar
370  ? $this->ui_renderer->render($avatar)
371  : '';
372  }
373 
374  return $this->avatars[$record->getSenderId()];
375  }
376 
377  private function getStatus(MailRecordData $record): Icon
378  {
379  return $record->isRead()
380  ? $this->ui_factory->symbol()->icon()->standard('mailr', $this->lng->txt('mailr'))
381  : $this->ui_factory->symbol()->icon()->standard('mailu', $this->lng->txt('mailu'));
382  }
383 
384  private function getSender(MailRecordData $record): string
385  {
386  if ($record->getSenderId() === ANONYMOUS_USER_ID) {
388  }
389 
391  if ($user !== null) {
392  if ($user->hasPublicProfile()) {
393  return $this->ui_renderer->render(
394  $this->ui_factory->link()->standard(
395  $user->getPublicName(),
396  (string) $this->url_builder
397  ->withParameter($this->action_token, self::ACTION_PROFILE)
398  ->withParameter($this->row_id_token, (string) $record->getMailId())
399  ->buildURI()
400  )
401  );
402  }
403 
404  return $user->getPublicName();
405  }
406 
407  return trim(($record->getImportName() ?? '') . ' (' . $this->lng->txt('user_deleted') . ')');
408  }
409 
410  private function getRecipients(MailRecordData $record): string
411  {
412  return $this->refinery->encode()->htmlSpecialCharsAsEntities()->transform(
413  $this->mail->formatNamesForOutput((string) $record->getRcpTo())
414  );
415  }
416 
417  private function getSubject(MailRecordData $record): Link
418  {
419  return $this->ui_factory->link()->standard(
420  $this->refinery->encode()->htmlSpecialCharsAsEntities()->transform($record->getSubject()),
421  (string) $this->url_builder
422  ->withParameter(
423  $this->action_token,
424  $this->current_folder->isDrafts() ? self::ACTION_EDIT : self::ACTION_SHOW
425  )
426  ->withParameter($this->row_id_token, (string) $record->getMailId())
427  ->buildURI()
428  );
429  }
430 
431  private function getDate(MailRecordData $record): ?DateTimeImmutable
432  {
433  return empty($record->getSendTime()) ? null : $record->getSendTime()->setTimezone($this->user_time_zone);
434  }
435 
436  private function getAttachments(MailRecordData $record): string
437  {
438  return $record->hasAttachments()
439  ? $this->ui_renderer->render($this->ui_factory->symbol()->glyph()->attachment())
440  : '';
441  }
442 }
search()
description: > Example for rendring a search glyph.
Definition: search.php:41
join($init, callable $fn)
Definition: Order.php:75
const ANONYMOUS_USER_ID
Definition: constants.php:27
static getUserObjectById(int $usr_id)
getRows(DataRowBuilder $row_builder, array $visible_column_ids, Range $range, Order $order, ?array $filter_data, ?array $additional_parameters)
This is called by the table to retrieve rows; map data-records to rows using the $row_builder e...
static _getIliasMailerName()
Both the subject and the direction need to be specified when expressing an order. ...
Definition: Order.php:28
buildDataRow(string $id, array $record)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
A Date Format provides a format definition akin to PHP&#39;s date formatting options, but stores the sing...
Definition: DateFormat.php:26
This is how the factory for UI elements looks.
Definition: Factory.php:37
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static preloadUserObjects(array $usr_ids)
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
__construct(private readonly URLBuilder $url_builder, private readonly URLBuilderToken $action_token, private readonly URLBuilderToken $row_id_token, private readonly URLBuilderToken $folder_token, private readonly array $user_folders, private readonly MailFolderData $current_folder, private readonly MailFolderSearch $search, private readonly ilMail $mail, private readonly Factory $ui_factory, private readonly Renderer $ui_renderer, private readonly ilLanguage $lng, private readonly ServerRequestInterface $http_request, private readonly DataFactory $data_factory, private readonly Refinery $refinery, private readonly DateFormat $date_format, private readonly string $time_format, private readonly DateTimeZone $user_time_zone)
getAttachments(MailRecordData $record)
global $lng
Definition: privfeed.php:31
URLBuilder.
Definition: URLBuilder.php:40
const DESC
Definition: Order.php:31
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:28
getTotalRowCount(?array $filter_data, ?array $additional_parameters)
Mainly for the purpose of pagination-support, it is important to know about the total number of recor...