From e63b4c48bdfac1faacc382dcf9c44ceff13d130a Mon Sep 17 00:00:00 2001 From: djarrancotleanu Date: Fri, 4 Jul 2025 14:03:30 +1000 Subject: [PATCH] MDL-85366 core: Add proper PHPMailer handling of .ics files --- lib/moodlelib.php | 7 +++++- lib/tests/moodlelib_test.php | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/lib/moodlelib.php b/lib/moodlelib.php index c3e44f9dfbf..46f8a56ac15 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -5875,7 +5875,12 @@ function email_to_user($user, $from, $subject, $messagetext, $messagehtml = '', $attachment = $CFG->dataroot . '/' . $attachment; } - $mail->addAttachment($attachment, $attachname, 'base64', $mimetype); + if ($mimetype == 'text/calendar') { + $icalcontent = file_get_contents($attachment); + $mail->Ical = $icalcontent; + } else { + $mail->addAttachment($attachment, $attachname, 'base64', $mimetype); + } } } diff --git a/lib/tests/moodlelib_test.php b/lib/tests/moodlelib_test.php index df666632012..8d088a87236 100644 --- a/lib/tests/moodlelib_test.php +++ b/lib/tests/moodlelib_test.php @@ -3571,6 +3571,53 @@ EOF; $this->assertStringNotContainsString('Content-Disposition: attachment; filename=' . $filename, $messagebody); } + /** + * Test sending calendar (ICS) file attachments with email_to_user + * + * @covers ::email_to_user + */ + public function test_email_to_user_calendar_attachment(): void { + global $CFG; + + // Create a test calendar file in temp directory. + $temp = make_request_directory(); + $filepath = $temp . '/test_calendar.ics'; + $icalcontent = "BEGIN:VCALENDAR\r\n" . + "VERSION:2.0\r\n" . + "METHOD:REQUEST\r\n" . + "BEGIN:VEVENT\r\n" . + "SUMMARY:Test Event\r\n" . + "DTSTART:20250704T140000\r\n" . + "DTEND:20250704T150000\r\n" . + "END:VEVENT\r\n" . + "END:VCALENDAR"; + file_put_contents($filepath, $icalcontent); + + $user = \core_user::get_support_user(); + $message = 'Test calendar attachment'; + + // Create sink to catch all sent e-mails. + $sink = $this->redirectEmails(); + + $filename = basename($filepath); + email_to_user($user, $user, $message, $message, $message, $filepath, $filename); + + $messages = $sink->get_messages(); + $sink->close(); + + $this->assertCount(1, $messages); + + // Verify calendar content in message body. + $messagebody = reset($messages)->body; + // Check that it's not attached as a regular attachment. + $this->assertStringNotContainsString( + 'Content-Disposition: attachment; filename=' . $filename, + $messagebody + ); + // Check that it's included as iCal content. + $this->assertStringContainsString('Content-Type: text/calendar; method=REQUEST', $messagebody); + } + /** * Test setnew_password_and_mail. */