[TikiWiki-commits] [Git][tikiwiki/tiki][master] [ENH] calendar: Send and import file attachments on calendar invitation emails
"Victor Emanouilov \(@kroky\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <6a2ab5833f5a1_38195d20665f@gitlab-sidekiq-low-urgency-cpu-bound-v2-69866f7b86-scdjd.mail> |
Victor Emanouilov pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
2aa63a62 by Alfred Syatsukwa at 2026-06-11T12:59:19+00:00
[ENH] calendar: Send and import file attachments on calendar invitation emails
---
* [ENH] calendar: Import invitation attachments from webmail
* [ENH] calendar: Attach event files to calendar invitation emails
See merge request tikiwiki/tiki!10484
- - - - -
3 changed files:
- lib/calendar/calendarlib.php
- lib/core/Tiki/SabreDav/Utilities.php
- lib/cypht/modules/tiki/calendar_modules.php
Changes:
=====================================
lib/calendar/calendarlib.php
=====================================
@@ -2535,6 +2535,48 @@ class CalendarLib extends TikiLib
}
}
+ /**
+ * Upload file contents and attach them to a calendar event.
+ *
+ * @param int $calitemId The calendar item ID
+ * @param array $attachments Array of arrays with keys: filename, filetype, data
+ * @return void
+ */
+ public function uploadAndAttachEventFiles(int $calitemId, array $attachments): void
+ {
+ global $prefs;
+
+ if ($prefs['feature_file_galleries'] !== 'y' || empty($attachments)) {
+ return;
+ }
+
+ $filegallib = TikiLib::lib('filegal');
+ $galleryId = $this->getAttachmentsGalleryId();
+ $gal_info = $filegallib->get_file_gallery($galleryId);
+ if (! is_array($gal_info) || empty($gal_info['galleryId']) || ! $filegallib->can_upload_to($gal_info)) {
+ return;
+ }
+
+ $fileIds = [];
+ foreach ($attachments as $attachment) {
+ $fileId = $filegallib->upload_single_file(
+ $gal_info,
+ $attachment['filename'],
+ strlen($attachment['data']),
+ $attachment['filetype'],
+ $attachment['data']
+ );
+ if ($fileId) {
+ $fileIds[] = (int) $fileId;
+ }
+ }
+ if (empty($fileIds)) {
+ return;
+ }
+
+ $this->setEventAttachments($calitemId, $fileIds);
+ }
+
/**
* Get the file gallery ID to use for calendar event attachments.
* Falls back to global preference or root gallery.
@@ -2545,6 +2587,14 @@ class CalendarLib extends TikiLib
{
global $prefs;
- return (int) ($prefs['calendar_attachments_galleryId'] ?? $prefs['fgal_root_id'] ?? 1);
+ $id = (int) ($prefs['calendar_attachments_galleryId'] ?? 0);
+ if ($id <= 0) {
+ $id = (int) ($prefs['fgal_root_id'] ?? 0);
+ }
+ if ($id <= 0) {
+ $id = 1;
+ }
+
+ return $id;
}
}
=====================================
lib/core/Tiki/SabreDav/Utilities.php
=====================================
@@ -11,6 +11,8 @@ use Sabre\CardDAV;
use Sabre\DAV;
use Sabre\DAVACL;
use Sabre\VObject;
+use Perms;
+use Tiki\FileGallery\File;
use Tiki\Lib\TikiDate;
use TikiLib;
use TikiMail;
@@ -375,6 +377,9 @@ class Utilities
if (isset($component->{'X-Tiki-HideParticipants'})) {
$result['hideParticipants'] = intval($convertToString($component->{'X-Tiki-HideParticipants'}));
}
+ if (isset($component->{'X-Tiki-Attachments'})) {
+ $result['attachments'] = array_filter(array_map('intval', explode(',', $convertToString($component->{'X-Tiki-Attachments'}))));
+ }
if (isset($component->ORGANIZER)) {
$result['organizers'] = [];
$result['real_organizers'] = [];
@@ -673,6 +678,19 @@ class Utilities
if ($row['hideParticipants'] ?? false) {
$data['X-Tiki-HideParticipants'] = '1';
}
+ if (! empty($row['attachments'])) {
+ if (is_array($row['attachments'])) {
+ $fileIds = array_map(function ($attachment) {
+ return is_array($attachment) ? (int) ($attachment['fileId'] ?? 0) : (int) $attachment;
+ }, $row['attachments']);
+ } else {
+ $fileIds = array_filter(array_map('intval', explode(',', (string) $row['attachments'])));
+ }
+ $fileIds = array_filter($fileIds);
+ if ($fileIds) {
+ $data['X-Tiki-Attachments'] = implode(',', $fileIds);
+ }
+ }
$vcalendar = new VObject\Component\VCalendar();
$vevent = $vcalendar->add('VEVENT', $data);
@@ -813,9 +831,45 @@ Invitees: " . ($hideParticipants ? "Participants list has been hidden at organiz
// The other way would be via Mail-in to calendars and a reply-to address configured as a mail-in source.
$mail = new TikiMail($args['user'], $sender_email, $sender_name);
$mail->setSubject($subject);
+ if ($message->method === 'REQUEST' && $calitem) {
+ self::addEventAttachmentsToMail($mail, (int) $calitem['calitemId']);
+ }
$mail->setText($body);
+ // File IDs in X-Tiki-Attachments are instance-local; send files as MIME parts only.
+ if (isset($message->message->VEVENT->{'X-Tiki-Attachments'})) {
+ $message->message->VEVENT->remove('X-Tiki-Attachments');
+ }
$mail->addPart($message->message->serialize(), 'text/calendar; method=' . $message->method . '; name=event.ics');
$mail->send([$recipient]);
}
}
+
+ private static function addEventAttachmentsToMail(TikiMail $mail, int $calitemId): void
+ {
+ global $prefs;
+
+ if ($prefs['feature_file_galleries'] !== 'y') {
+ return;
+ }
+
+ $attachments = TikiLib::lib('calendar')->getEventAttachments($calitemId);
+ if (empty($attachments)) {
+ return;
+ }
+
+ foreach ($attachments as $attachment) {
+ $fileId = (int) $attachment['fileId'];
+ $file = File::id($fileId);
+ if (! $file->exists() || ! Perms::get('file', $fileId)->download_files) {
+ continue;
+ }
+
+ $contents = $file->getContents();
+ if (empty($contents)) {
+ continue;
+ }
+
+ $mail->addAttachment($contents, $file->filename, $file->filetype);
+ }
+ }
}
=====================================
lib/cypht/modules/tiki/calendar_modules.php
=====================================
@@ -281,6 +281,8 @@ class Hm_Handler_add_to_calendar extends Hm_Handler_Module
$data = $this->get('calendar_event');
$data['calendarId'] = $form['calendar_id'];
$data['user'] = $user;
+ // X-Tiki-Attachments holds sender file IDs; import files from the email instead.
+ unset($data['attachments']);
$client = new \Tiki\SabreDav\CaldavClient();
if ($data['rec']) {
@@ -302,6 +304,14 @@ class Hm_Handler_add_to_calendar extends Hm_Handler_Module
$client->saveCalendarObject($data);
}
+ $item = TikiLib::lib('calendar')->find_by_uid(null, $data['uid']);
+ if ($item) {
+ TikiLib::lib('calendar')->uploadAndAttachEventFiles(
+ (int) $item['calitemId'],
+ tiki_get_invitation_email_attachments($this)
+ );
+ }
+
Hm_Msgs::add("Event created");
}
}
@@ -328,6 +338,8 @@ class Hm_Handler_update_in_calendar extends Hm_Handler_Module
return;
}
+ unset($data['attachments']);
+
$perms = Perms::get('event', $existing['calitemId']);
if (! $perms->change_events) {
Hm_Msgs::add(tr("Insufficient permissions to update the event in the calendar"), "danger");
@@ -346,6 +358,11 @@ class Hm_Handler_update_in_calendar extends Hm_Handler_Module
$client->saveCalendarObject($data);
}
+ TikiLib::lib('calendar')->uploadAndAttachEventFiles(
+ (int) $existing['calitemId'],
+ tiki_get_invitation_email_attachments($this)
+ );
+
Hm_Msgs::add("Event updated");
}
}
@@ -523,6 +540,106 @@ class Hm_Output_add_rsvp_actions extends Hm_Output_Module
}
}
+/**
+ * @param \ZBateson\MailMimeParser\Message $message
+ * @return array
+ */
+if (! hm_exists('tiki_extract_calendar_invite_attachments')) {
+ function tiki_extract_calendar_invite_attachments($message)
+ {
+ $attachments = [];
+ $parts = iterator_to_array($message->getAllAttachmentParts());
+ if (empty($parts)) {
+ foreach ($message->getAllParts() as $part) {
+ if ($part->getChildCount() > 0) {
+ continue;
+ }
+ if ($part->getFilename()) {
+ $parts[] = $part;
+ }
+ }
+ }
+
+ foreach ($parts as $part) {
+ $filetype = explode(';', $part->getContentType())[0];
+ if (in_array(strtolower($filetype), ['text/plain', 'text/html', 'text/calendar'], true)) {
+ continue;
+ }
+ $filename = $part->getFilename() ?: 'attachment';
+ if (strtolower(basename($filename)) === 'event.ics') {
+ continue;
+ }
+ $data = $part->getContent();
+ if ($data === '') {
+ continue;
+ }
+ $attachments[] = [
+ 'filename' => basename($filename),
+ 'filetype' => $filetype,
+ 'data' => $data,
+ ];
+ }
+
+ return $attachments;
+ }
+}
+
+/**
+ * @param Hm_Handler_Module $mod
+ * @return array
+ */
+if (! hm_exists('tiki_get_invitation_email_attachments')) {
+ function tiki_get_invitation_email_attachments(Hm_Handler_Module $mod)
+ {
+ global $prefs;
+
+ if ($prefs['feature_file_galleries'] !== 'y') {
+ return [];
+ }
+
+ $list_path = $mod->request->post['list_path'] ?? $mod->get('list_path', '');
+ if (strpos($list_path, 'tracker_folder_') === 0) {
+ list($success, $form) = $mod->process_form(['imap_msg_uid', 'list_path']);
+ if (! $success) {
+ return [];
+ }
+ $email = tiki_parse_message($form['list_path'], $form['imap_msg_uid']);
+ if (! $email || empty($email['message_raw'])) {
+ return [];
+ }
+
+ return tiki_extract_calendar_invite_attachments($email['message_raw']);
+ }
+
+ $imapServerId = $mod->request->post['imap_server_id'] ?? null;
+ $imapMsgUid = $mod->request->post['imap_msg_uid'] ?? null;
+ $folder = $mod->request->post['folder'] ?? null;
+ if (! $imapServerId || ! $imapMsgUid || ! $folder) {
+ list($success, $form) = $mod->process_form(['imap_server_id', 'imap_msg_uid', 'folder']);
+ if (! $success) {
+ return [];
+ }
+ $imapServerId = $form['imap_server_id'];
+ $imapMsgUid = $form['imap_msg_uid'];
+ $folder = $form['folder'];
+ }
+
+ $mailbox = Hm_IMAP_List::get_connected_mailbox($imapServerId, $mod->cache);
+ if (! $mailbox || ! $mailbox->authed()) {
+ return [];
+ }
+
+ $raw = $mailbox->get_message_content(hex2bin($folder), $imapMsgUid, 0);
+ if (! $raw) {
+ return [];
+ }
+
+ $message = \ZBateson\MailMimeParser\Message::from($raw, false);
+
+ return tiki_extract_calendar_invite_attachments($message);
+ }
+}
+
/**
* Search imap message structure for text/calendar parts
* @subpackage tiki/functions
@@ -541,7 +658,7 @@ if (! hm_exists('get_calendar_part')) {
if (is_array($vals) && isset($vals['type'])) {
if ($vals['type'] . '/' . $vals['subtype'] == 'text/calendar') {
$part = $id;
- $method = $vals['attributes']['method'];
+ $method = $vals['attributes']['method'] ?? null;
}
if ($vals['type'] . '/' . $vals['subtype'] == 'text/html') {
$htmlPart = $id;
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/2aa63a62c1c5c40f134e64764803e7cd30f38f13
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/2aa63a62c1c5c40f134e64764803e7cd30f38f13
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs