Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Settings to allow booking rules to send ics attachments #801

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions classes/booking_rules/actions/send_mail.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ class send_mail implements booking_rule_action {
/** @var int $ruleid */
public $ruleid = null;

/** @var string $sendical */
public $sendical = null;

/** @var string $sendicalcreateorcancel */
public $sendicalcreateorcancel = null;

/** @var string $subject */
public $subject = null;

Expand All @@ -68,6 +74,8 @@ public function set_actiondata_from_json(string $json) {
$jsonobject = json_decode($json);
$actiondata = $jsonobject->actiondata;

$this->sendical = $actiondata->sendical ?? '';
$this->sendicalcreateorcancel = $actiondata->sendicalcreateorcancel ?? '';
$this->subject = $actiondata->subject;
$this->template = $actiondata->template;
}
Expand All @@ -81,6 +89,17 @@ public function set_actiondata_from_json(string $json) {
*/
public function add_action_to_mform(MoodleQuickForm &$mform, array &$repeateloptions) {

// Select to send ical.
$mform->addElement('selectyesno', 'action_send_mail_sendical', get_string('sendical', 'mod_booking'));
$mform->addRule('action_send_mail_sendical', null, 'required', null, 'client');
$mform->setType('action_send_mail_sendical', PARAM_INT);

// TODO This should probably only show if the above dropdown is set to yes.
$options = ['create' => get_string('createical', 'mod_booking'), 'cancel' => get_string('cancelical', 'mod_booking')];
$mform->addElement('select', 'action_send_mail_sendicalcreateorcancel',
get_string('sendicalcreateorcancel', 'mod_booking'), $options);
$mform->setType('action_send_mail_sendicalcreateorcancel', PARAM_RAW);

// Mail subject.
$mform->addElement('text', 'action_send_mail_subject', get_string('messagesubject', 'mod_booking'),
['size' => '66']);
Expand Down Expand Up @@ -130,6 +149,8 @@ public function save_action(stdClass &$data) {
$jsonobject->name = $data->name ?? $this->actionname;
$jsonobject->actionname = $this->actionname;
$jsonobject->actiondata = new stdClass();
$jsonobject->actiondata->sendical = $data->action_send_mail_sendical;
$jsonobject->actiondata->sendicalcreateorcancel = $data->action_send_mail_sendicalcreateorcancel;
$jsonobject->actiondata->subject = $data->action_send_mail_subject;
$jsonobject->actiondata->template = $data->action_send_mail_template['text'];
$jsonobject->actiondata->templateformat = $data->action_send_mail_template['format'];
Expand All @@ -147,6 +168,8 @@ public function set_defaults(stdClass &$data, stdClass $record) {
$jsonobject = json_decode($record->rulejson);
$actiondata = $jsonobject->actiondata;

$data->action_send_mail_sendical = $actiondata->sendical;
$data->action_send_mail_sendicalcreateorcancel = $actiondata->sendicalcreateorcancel;
$data->action_send_mail_subject = $actiondata->subject;
$data->action_send_mail_template = [];
$data->action_send_mail_template['text'] = $actiondata->template;
Expand All @@ -172,6 +195,8 @@ public function execute(stdClass $record) {
'userid' => $record->userid,
'optionid' => $record->optionid,
'cmid' => $record->cmid,
'sendical' => $this->sendical,
'sendicalcreateorcancel' => $this->sendicalcreateorcancel,
'customsubject' => $this->subject,
'custommessage' => $this->template,
'installmentnr' => $record->payment_id ?? 0,
Expand Down
23 changes: 22 additions & 1 deletion classes/ical.php
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,29 @@ protected function add_vevent($uid, $dtstart, $dtend, $time = false) {
}

// Make sure we have not tags in full description.
$fulldescription = rtrim(strip_tags(preg_replace( "/<br>|<\/p>/", "\n", $fulldescription)));
$fulldescriptionhtml = $fulldescription;
// Remove CR and CRLF from description as the description must be on one line.
$fulldescriptionhtml = str_replace (array("\r\n", "\n", "\r"), ' ', $fulldescriptionhtml);

// Check for a url and render it as a nice link.
// Regular Expression Pattern for a basic URL.
$pattern = '/\b(?:https?:\/\/)[a-zA-Z0-9\.\-]+(?:\.[a-zA-Z]{2,})(?:\/\S*)?/';
// Array to hold the matched URLs.
$matches = [];
// Perform the pattern match.
preg_match_all($pattern, $fulldescriptionhtml, $matches);

foreach ($matches[0] as $url) {
$fulldescriptionhtml = str_replace($url, '<a href="' . $url . '">Link</a>', $fulldescriptionhtml);
}

$fulldescription = rtrim(strip_tags(preg_replace("/<br>|<\/p>/", "\n", $fulldescription)));
$fulldescription = str_replace("\n", "\\n", $fulldescription );

// Remove CR and CRLF from description as the description must be on one line to work with ical.
$fulldescription = str_replace (array("\r\n", "\n", "\r"), ' ', $fulldescription);


// Make sure that we fall back onto some reasonable no-reply address.
$noreplyaddressdefault = 'noreply@' . get_host_from_url($CFG->wwwroot);
$noreplyaddress = empty($CFG->noreplyaddress) ? $noreplyaddressdefault : $CFG->noreplyaddress;
Expand All @@ -353,6 +373,7 @@ protected function add_vevent($uid, $dtstart, $dtend, $time = false) {
"BEGIN:VEVENT",
"CLASS:PUBLIC",
"DESCRIPTION:{$fulldescription}",
"X-ALT-DESC;FMTTYPE=text/html:{$fulldescriptionhtml}",
"DTEND:{$dtend}",
"DTSTAMP:{$this->dtstamp}",
"DTSTART:{$dtstart}",
Expand Down
83 changes: 80 additions & 3 deletions classes/message_controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ class message_controller {
/** @var float $price price given in installment */
private $price;

/** @var array $rulesettings duedate of installment. */
private $rulesettings;

/** @var array $ruleid the id of the running rule. */
private $ruleid;

/**
* Constructor
*
Expand All @@ -138,6 +144,7 @@ class message_controller {
* @param int $duedate UNIX timestamp for duedate of installment
* @param float $price price of installment
* @param string $rulejson event data
* @param ?int $ruleid the id of the running rule
*/
public function __construct(
int $msgcontrparam,
Expand All @@ -153,10 +160,23 @@ public function __construct(
int $installmentnr = 0,
int $duedate = 0,
float $price = 0.0,
string $rulejson = ''
string $rulejson = '',
?int $ruleid = null
) {

global $USER, $PAGE, $SESSION;
global $USER, $PAGE, $DB;

if (!is_null($ruleid)) {
// For some reason $this->rulejson doesn't get passed to the controller.
// So instead we use the ruleid that we have added to this class.
// Get the rulesjson and convert into an array for later
// There is probably an exisiting method for this, but I couldn't find it.
$this->rulesettings = $DB->get_record('booking_rules', ['id' => $ruleid], 'rulejson');
if ( $this->rulesettings) {
$this->rulesettings = json_decode($this->rulesettings->rulejson);
$this->ruleid = $ruleid;
}
}

$user = singleton_service::get_instance_of_user($userid);
$originallanguage = force_current_language($user->lang);
Expand Down Expand Up @@ -457,9 +477,63 @@ public function send_or_queue(): bool {

} else {

// If the rule has sendical set then we get the ical attachment.
// Create it in file storage and put it in the message object.
if ($this->rulesettings->actiondata->sendical) {

$update = false;
if ($this->rulesettings->actiondata->sendicalcreateorcancel == 'cancel') {
$update = true;
}

// Pass the update param - false will create a remove calendar invite.
// TODO The system still fires an unsubscribe message - I believe this is a hangover of the old non rules booking system.
list($attachments, $attachname) = $this->get_attachments($update);

if (!empty($attachments)) {
// TODO this should probably be a method in the ical class.
// Left here to limit to number of changed files.
// Store the file correctly in order to be able to attach it.
$fs = get_file_storage();
$context = context_system::instance(); // Use a suitable context, such as course or module context.
$tempfilepath = $attachments['booking.ics'];

// Check if the file exists in the temp path.
if (file_exists($tempfilepath)) {

// Prepare file record in Moodle storage.
$filerecord = [
'contextid' => $context->id,
'component' => 'mod_booking', // Change to your component.
'filearea' => 'message_attachments', // A custom file area for attachments.
'itemid' => 0, // Item ID (0 for general use or unique identifier for the message).
'filepath' => '/', // Always use '/' as the root directory.
'filename' => $attachname,
'userid' => $this->messagedata->userto->id,
];

// Create or retrieve the file in Moodle's file storage.
$storedfile = $fs->create_file_from_pathname($filerecord, $tempfilepath);

// Set the file as an attachment.
$this->messagedata->attachment = $storedfile;
$this->messagedata->attachname = $attachname;
} else {
// TODO There is possibly a better way to handle this error nicely - or remove the check entirely.
throw new \moodle_exception('Attachment file not found.');
}

}
}

// In all other cases, use message_send.
if (message_send($this->messagedata)) {

if ($this->rulesettings->actiondata->sendical) {
// Tidy up the now not needed file.
$storedfile->delete();
}

// Use an event to log that a message has been sent.
$event = \mod_booking\event\message_sent::create([
'context' => context_system::instance(),
Expand All @@ -471,6 +545,9 @@ public function send_or_queue(): bool {
'subject' => $this->messagedata->subject,
'objectid' => $this->optionid ?? 0,
'message' => $this->messagedata->fullmessage ?? '',
// Store the full html message as this is useful if the message every needs to be replayed or audited.
'messagehtml' => $this->messagedata->fullmessagehtml ?? '',
'bookingruleid' => $this->ruleid ?? null,
],
]);
$event->trigger();
Expand Down Expand Up @@ -552,7 +629,7 @@ private function get_attachments(bool $updated = false): array {
// Generate ical attachments to go with the message. Check if ical attachments enabled.
if (get_config('booking', 'attachical')) {
$ical = new ical($this->bookingsettings, $this->optionsettings, $this->user, $this->bookingmanager, $updated);
$attachments = $ical->get_attachments(false);
$attachments = $ical->get_attachments($updated);
$attachname = $ical->get_name();
}
}
Expand Down
1 change: 1 addition & 0 deletions classes/task/send_mail_by_rule_adhoc.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public function execute() {
$taskdata->duedate ?? 0,
$taskdata->price ?? 0,
$taskdata->rulejson ?? 0,
$taskdata->ruleid ?? 0 // Send the ruleid as rulejson often seems to not work.
);
} catch (Exception $e) {
if (get_config('booking', 'bookingdebugmode')) {
Expand Down
4 changes: 4 additions & 0 deletions lang/de/booking.php
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@
"Nutzer:innen können nur bis n Tage vor Kursstart stornieren. Negative Werte meinen n Tage NACH Kursstart."
beziehen soll.<br>Dadurch wird auch die <i>Serviceperiode</i> von Kursen im Warenkorb entsprechend festgelegt
(wenn Shopping Cart installiert ist). Dies betrifft auch die Ratenzahlung. Entfernen Sie das ausgewählte Semester, wenn Sie Kursstart anstelle von Semesterstart nutzen möchten.';
$string['cancelical'] = 'Abbrechen';
$string['cancellation'] = 'Stornierung';
$string['cancellationsettings'] = 'Stornierungseinstellungen ' . '<span class="badge bg-success text-light"><i class="fa fa-cogs" aria-hidden="true"></i> PRO</span>';
$string['cancelmyself'] = 'Wieder abmelden';
Expand Down Expand Up @@ -762,6 +763,7 @@
$string['coursestart'] = 'Starten';
$string['coursestarttime'] = 'Kursbeginn';
$string['createdbywunderbyte'] = 'Dieses Buchungsmodul wurde von der Wunderbyte GmbH entwickelt';
$string['createical'] = 'Erstellen';
$string['createnewbookingoption'] = 'Neue Buchungsoption';
$string['createnewbookingoptionfromtemplate'] = 'Neue Buchungsoption von Vorlage erstellen';
$string['createnewmoodlecourse'] = 'Erstelle neuen, leeren Moodle-Kurs';
Expand Down Expand Up @@ -1976,6 +1978,8 @@
$string['sendcopyofmailmessageprefix'] = 'Vorangestellter Text für die Nachricht';
$string['sendcopyofmailsubjectprefix'] = 'Vorangestellter Text für den Betreff';
$string['sendcustommsg'] = 'Persönliche Nachricht senden';
$string['sendical'] = 'Senden Sie die ICS-Datei als Anhang';
$string['sendicalcreateorcancel'] = 'Handelt es sich bei der iCal-Datei um eine Erstellung oder eine Stornierung eines Ereignisses?';
$string['sendmail'] = "Sende E-Mail";
$string['sendmailheading'] = 'E-Mail an alle TrainerInnen der ausgewählten Buchungsoptionen senden';
$string['sendmailinterval'] = 'Eine Nachricht zeitversetzt an mehrere Nutzer:innen schicken';
Expand Down
5 changes: 5 additions & 0 deletions lang/en/booking.php
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@
"Disallow users to cancel their booking n days before start. Minus means, that users can still cancel n
days AFTER course start.".<br>
This will also set the <i>service period</i> of courses in shopping cart accordingly (if shopping cart is installed). This also affects installment payments. You can take out the semester in the Dates section of a booking option, if you want to use the coursestart instead of the semesterstart.';
$string['cancelical'] = 'Cancel';
$string['cancellation'] = 'Cancellation';
$string['cancellationsettings'] = 'Cancellation settings ' . '<span class="badge bg-success text-light"><i class="fa fa-cogs" aria-hidden="true"></i> PRO</span>';
$string['cancelmyself'] = 'Undo my booking';
Expand Down Expand Up @@ -781,6 +782,7 @@
$string['coursestarttime'] = 'Start time of the course';
$string['courseurl'] = 'Course URL';
$string['createdbywunderbyte'] = 'Booking module created by Wunderbyte GmbH';
$string['createical'] = 'Create';
$string['createnewbookingoption'] = 'New booking option';
$string['createnewbookingoptionfromtemplate'] = 'Add a new booking option from template';
$string['createnewmoodlecourse'] = 'Create new empty Moodle course';
Expand Down Expand Up @@ -2038,13 +2040,16 @@
$string['sendcopyofmailmessageprefix'] = 'Message prefix for the copy';
$string['sendcopyofmailsubjectprefix'] = 'Subject prefix for the copy';
$string['sendcustommsg'] = 'Send custom message';
$string['sendical'] = 'Send ICS file as attachment';
$string['sendicalcreateorcancel'] = 'Is the Ical a create or cancel event?';
$string['sendmail'] = 'Send email';
$string['sendmailheading'] = 'Send mail to all teachers of selected bookingoption(s)';
$string['sendmailinterval'] = 'Send a message to multiple users with a time delay';
$string['sendmailtoallbookedusers'] = 'Send e-mail to all booked users';
$string['sendmailtobooker'] = 'Book other users page: Send mail to user who books instead to users who are booked';
$string['sendmailtobooker_help'] = 'Activate this option in order to send booking confirmation mails to the user who books other users instead to users, who have been added to a booking option. This is only relevant for bookings made on the page "book other users".';
$string['sendmailtoteachers'] = 'Send mail to teacher(s)';

$string['sendmessage'] = 'Send message';
$string['sendpollurltoteachers'] = 'Send poll url';
$string['sendreminderemail'] = "Send reminder e-mail";
Expand Down
Loading