You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

IMipPlugin.php 23KB

8 年之前
7 年之前
7 年之前
8 年之前
8 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
iMIP email improvements (take 2) This PR is a replacement for PR #17195. It is intended to be simpler to review and approve, with fewer changes, some disabled by default. It addresses issues #12391 and #13555, with the following changes: - The plainText of iMIP emails has been upgraded as described in issue #12391. The HTML design style has not been changed. - Some of the HTML and plainText content has been rearranged (simplified header language, moving the event title to from text body to the first item in the bullet list, spelling corrections, moving the description to the end of the list), per issue #12391. - The interface for EMailTemplate has been extended: addBodyListItem now takes an optional `plainIndent` parameter. Existing callers see no change. Where new calls set the new parameter >0, the list item label (metaInfo) is put in column 1, and the value is indented into column 2 (properly accounting for multiple lines, if any). - An optional dav config setting has been added, `invitation_list_attendees`. It defaults to 'no', leaving emails unchanged. If set by the site admin to 'yes', then iMIP emails include, for the organizer and each attendee, their name, email, and a ✔︎ if they have accepted the invitation. - Minor refactoring. Notes: - The labels for organizers and attendees list items are new, and require translation/localization. - Dav config settings are documented in the code, but not in the Administrator's Guide. Signed-off-by: brad2014 <brad2014@users.noreply.github.com>
4 年之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. * @copyright Copyright (c) 2017, Georg Ehrke
  5. *
  6. * @author brad2014 <brad2014@users.noreply.github.com>
  7. * @author Brad Rubenstein <brad@wbr.tech>
  8. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  9. * @author Georg Ehrke <oc.list@georgehrke.com>
  10. * @author Joas Schilling <coding@schilljs.com>
  11. * @author Leon Klingele <leon@struktur.de>
  12. * @author rakekniven <mark.ziegler@rakekniven.de>
  13. * @author Roeland Jago Douma <roeland@famdouma.nl>
  14. * @author Thomas Citharel <nextcloud@tcit.fr>
  15. * @author Thomas Müller <thomas.mueller@tmit.eu>
  16. *
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OCA\DAV\CalDAV\Schedule;
  33. use OCP\AppFramework\Utility\ITimeFactory;
  34. use OCP\Defaults;
  35. use OCP\IConfig;
  36. use OCP\IDBConnection;
  37. use OCP\IL10N;
  38. use OCP\ILogger;
  39. use OCP\IURLGenerator;
  40. use OCP\IUserManager;
  41. use OCP\L10N\IFactory as L10NFactory;
  42. use OCP\Mail\IEMailTemplate;
  43. use OCP\Mail\IMailer;
  44. use OCP\Security\ISecureRandom;
  45. use OCP\Util;
  46. use Sabre\CalDAV\Schedule\IMipPlugin as SabreIMipPlugin;
  47. use Sabre\VObject\Component\VCalendar;
  48. use Sabre\VObject\Component\VEvent;
  49. use Sabre\VObject\DateTimeParser;
  50. use Sabre\VObject\ITip\Message;
  51. use Sabre\VObject\Parameter;
  52. use Sabre\VObject\Property;
  53. use Sabre\VObject\Recur\EventIterator;
  54. /**
  55. * iMIP handler.
  56. *
  57. * This class is responsible for sending out iMIP messages. iMIP is the
  58. * email-based transport for iTIP. iTIP deals with scheduling operations for
  59. * iCalendar objects.
  60. *
  61. * If you want to customize the email that gets sent out, you can do so by
  62. * extending this class and overriding the sendMessage method.
  63. *
  64. * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
  65. * @author Evert Pot (http://evertpot.com/)
  66. * @license http://sabre.io/license/ Modified BSD License
  67. */
  68. class IMipPlugin extends SabreIMipPlugin {
  69. /** @var string */
  70. private $userId;
  71. /** @var IConfig */
  72. private $config;
  73. /** @var IMailer */
  74. private $mailer;
  75. /** @var ILogger */
  76. private $logger;
  77. /** @var ITimeFactory */
  78. private $timeFactory;
  79. /** @var L10NFactory */
  80. private $l10nFactory;
  81. /** @var IURLGenerator */
  82. private $urlGenerator;
  83. /** @var ISecureRandom */
  84. private $random;
  85. /** @var IDBConnection */
  86. private $db;
  87. /** @var Defaults */
  88. private $defaults;
  89. /** @var IUserManager */
  90. private $userManager;
  91. public const MAX_DATE = '2038-01-01';
  92. public const METHOD_REQUEST = 'request';
  93. public const METHOD_REPLY = 'reply';
  94. public const METHOD_CANCEL = 'cancel';
  95. public const IMIP_INDENT = 15; // Enough for the length of all body bullet items, in all languages
  96. /**
  97. * @param IConfig $config
  98. * @param IMailer $mailer
  99. * @param ILogger $logger
  100. * @param ITimeFactory $timeFactory
  101. * @param L10NFactory $l10nFactory
  102. * @param IUrlGenerator $urlGenerator
  103. * @param Defaults $defaults
  104. * @param ISecureRandom $random
  105. * @param IDBConnection $db
  106. * @param string $userId
  107. */
  108. public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
  109. ITimeFactory $timeFactory, L10NFactory $l10nFactory,
  110. IURLGenerator $urlGenerator, Defaults $defaults,
  111. ISecureRandom $random, IDBConnection $db, IUserManager $userManager,
  112. $userId) {
  113. parent::__construct('');
  114. $this->userId = $userId;
  115. $this->config = $config;
  116. $this->mailer = $mailer;
  117. $this->logger = $logger;
  118. $this->timeFactory = $timeFactory;
  119. $this->l10nFactory = $l10nFactory;
  120. $this->urlGenerator = $urlGenerator;
  121. $this->random = $random;
  122. $this->db = $db;
  123. $this->defaults = $defaults;
  124. $this->userManager = $userManager;
  125. }
  126. /**
  127. * Event handler for the 'schedule' event.
  128. *
  129. * @param Message $iTipMessage
  130. * @return void
  131. */
  132. public function schedule(Message $iTipMessage) {
  133. // Not sending any emails if the system considers the update
  134. // insignificant.
  135. if (!$iTipMessage->significantChange) {
  136. if (!$iTipMessage->scheduleStatus) {
  137. $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
  138. }
  139. return;
  140. }
  141. $summary = $iTipMessage->message->VEVENT->SUMMARY;
  142. if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') {
  143. return;
  144. }
  145. if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') {
  146. return;
  147. }
  148. // don't send out mails for events that already took place
  149. $lastOccurrence = $this->getLastOccurrence($iTipMessage->message);
  150. $currentTime = $this->timeFactory->getTime();
  151. if ($lastOccurrence < $currentTime) {
  152. return;
  153. }
  154. // Strip off mailto:
  155. $sender = substr($iTipMessage->sender, 7);
  156. $recipient = substr($iTipMessage->recipient, 7);
  157. $senderName = $iTipMessage->senderName ?: null;
  158. $recipientName = $iTipMessage->recipientName ?: null;
  159. if ($senderName === null || empty(trim($senderName))) {
  160. $user = $this->userManager->get($this->userId);
  161. if ($user) {
  162. // getDisplayName automatically uses the uid
  163. // if no display-name is set
  164. $senderName = $user->getDisplayName();
  165. }
  166. }
  167. /** @var VEvent $vevent */
  168. $vevent = $iTipMessage->message->VEVENT;
  169. $attendee = $this->getCurrentAttendee($iTipMessage);
  170. $defaultLang = $this->l10nFactory->findLanguage();
  171. $lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
  172. $l10n = $this->l10nFactory->get('dav', $lang);
  173. $meetingAttendeeName = $recipientName ?: $recipient;
  174. $meetingInviteeName = $senderName ?: $sender;
  175. $meetingTitle = $vevent->SUMMARY;
  176. $meetingDescription = $vevent->DESCRIPTION;
  177. $meetingUrl = $vevent->URL;
  178. $meetingLocation = $vevent->LOCATION;
  179. $defaultVal = '--';
  180. $method = self::METHOD_REQUEST;
  181. switch (strtolower($iTipMessage->method)) {
  182. case self::METHOD_REPLY:
  183. $method = self::METHOD_REPLY;
  184. break;
  185. case self::METHOD_CANCEL:
  186. $method = self::METHOD_CANCEL;
  187. break;
  188. }
  189. $data = [
  190. 'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
  191. 'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
  192. 'meeting_title' => (string)$meetingTitle ?: $defaultVal,
  193. 'meeting_description' => (string)$meetingDescription ?: $defaultVal,
  194. 'meeting_url' => (string)$meetingUrl ?: $defaultVal,
  195. ];
  196. $fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
  197. $fromName = $l10n->t('%1$s via %2$s', [$senderName, $this->defaults->getName()]);
  198. $message = $this->mailer->createMessage()
  199. ->setFrom([$fromEMail => $fromName])
  200. ->setReplyTo([$sender => $senderName])
  201. ->setTo([$recipient => $recipientName]);
  202. $template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
  203. $template->addHeader();
  204. $summary = ((string) $summary !== '') ? (string) $summary : $l10n->t('Untitled event');
  205. $this->addSubjectAndHeading($template, $l10n, $method, $summary);
  206. $this->addBulletList($template, $l10n, $vevent);
  207. // Only add response buttons to invitation requests: Fix Issue #11230
  208. if (($method == self::METHOD_REQUEST) && $this->getAttendeeRSVP($attendee)) {
  209. /*
  210. ** Only offer invitation accept/reject buttons, which link back to the
  211. ** nextcloud server, to recipients who can access the nextcloud server via
  212. ** their internet/intranet. Issue #12156
  213. **
  214. ** The app setting is stored in the appconfig database table.
  215. **
  216. ** For nextcloud servers accessible to the public internet, the default
  217. ** "invitation_link_recipients" value "yes" (all recipients) is appropriate.
  218. **
  219. ** When the nextcloud server is restricted behind a firewall, accessible
  220. ** only via an internal network or via vpn, you can set "dav.invitation_link_recipients"
  221. ** to the email address or email domain, or comma separated list of addresses or domains,
  222. ** of recipients who can access the server.
  223. **
  224. ** To always deliver URLs, set invitation_link_recipients to "yes".
  225. ** To suppress URLs entirely, set invitation_link_recipients to boolean "no".
  226. */
  227. $recipientDomain = substr(strrchr($recipient, "@"), 1);
  228. $invitationLinkRecipients = explode(',', preg_replace('/\s+/', '', strtolower($this->config->getAppValue('dav', 'invitation_link_recipients', 'yes'))));
  229. if (strcmp('yes', $invitationLinkRecipients[0]) === 0
  230. || in_array(strtolower($recipient), $invitationLinkRecipients)
  231. || in_array(strtolower($recipientDomain), $invitationLinkRecipients)) {
  232. $this->addResponseButtons($template, $l10n, $iTipMessage, $lastOccurrence);
  233. }
  234. }
  235. $template->addFooter();
  236. $message->useTemplate($template);
  237. $attachment = $this->mailer->createAttachment(
  238. $iTipMessage->message->serialize(),
  239. 'event.ics',// TODO(leon): Make file name unique, e.g. add event id
  240. 'text/calendar; method=' . $iTipMessage->method
  241. );
  242. $message->attach($attachment);
  243. try {
  244. $failed = $this->mailer->send($message);
  245. $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
  246. if ($failed) {
  247. $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
  248. $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
  249. }
  250. } catch (\Exception $ex) {
  251. $this->logger->logException($ex, ['app' => 'dav']);
  252. $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
  253. }
  254. }
  255. /**
  256. * check if event took place in the past already
  257. * @param VCalendar $vObject
  258. * @return int
  259. */
  260. private function getLastOccurrence(VCalendar $vObject) {
  261. /** @var VEvent $component */
  262. $component = $vObject->VEVENT;
  263. $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
  264. // Finding the last occurrence is a bit harder
  265. if (!isset($component->RRULE)) {
  266. if (isset($component->DTEND)) {
  267. $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
  268. } elseif (isset($component->DURATION)) {
  269. /** @var \DateTime $endDate */
  270. $endDate = clone $component->DTSTART->getDateTime();
  271. // $component->DTEND->getDateTime() returns DateTimeImmutable
  272. $endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
  273. $lastOccurrence = $endDate->getTimestamp();
  274. } elseif (!$component->DTSTART->hasTime()) {
  275. /** @var \DateTime $endDate */
  276. $endDate = clone $component->DTSTART->getDateTime();
  277. // $component->DTSTART->getDateTime() returns DateTimeImmutable
  278. $endDate = $endDate->modify('+1 day');
  279. $lastOccurrence = $endDate->getTimestamp();
  280. } else {
  281. $lastOccurrence = $firstOccurrence;
  282. }
  283. } else {
  284. $it = new EventIterator($vObject, (string)$component->UID);
  285. $maxDate = new \DateTime(self::MAX_DATE);
  286. if ($it->isInfinite()) {
  287. $lastOccurrence = $maxDate->getTimestamp();
  288. } else {
  289. $end = $it->getDtEnd();
  290. while ($it->valid() && $end < $maxDate) {
  291. $end = $it->getDtEnd();
  292. $it->next();
  293. }
  294. $lastOccurrence = $end->getTimestamp();
  295. }
  296. }
  297. return $lastOccurrence;
  298. }
  299. /**
  300. * @param Message $iTipMessage
  301. * @return null|Property
  302. */
  303. private function getCurrentAttendee(Message $iTipMessage) {
  304. /** @var VEvent $vevent */
  305. $vevent = $iTipMessage->message->VEVENT;
  306. $attendees = $vevent->select('ATTENDEE');
  307. foreach ($attendees as $attendee) {
  308. /** @var Property $attendee */
  309. if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
  310. return $attendee;
  311. }
  312. }
  313. return null;
  314. }
  315. /**
  316. * @param string $default
  317. * @param Property|null $attendee
  318. * @return string
  319. */
  320. private function getAttendeeLangOrDefault($default, Property $attendee = null) {
  321. if ($attendee !== null) {
  322. $lang = $attendee->offsetGet('LANGUAGE');
  323. if ($lang instanceof Parameter) {
  324. return $lang->getValue();
  325. }
  326. }
  327. return $default;
  328. }
  329. /**
  330. * @param Property|null $attendee
  331. * @return bool
  332. */
  333. private function getAttendeeRSVP(Property $attendee = null) {
  334. if ($attendee !== null) {
  335. $rsvp = $attendee->offsetGet('RSVP');
  336. if (($rsvp instanceof Parameter) && (strcasecmp($rsvp->getValue(), 'TRUE') === 0)) {
  337. return true;
  338. }
  339. }
  340. // RFC 5545 3.2.17: default RSVP is false
  341. return false;
  342. }
  343. /**
  344. * @param IL10N $l10n
  345. * @param VEvent $vevent
  346. */
  347. private function generateWhenString(IL10N $l10n, VEvent $vevent) {
  348. $dtstart = $vevent->DTSTART;
  349. if (isset($vevent->DTEND)) {
  350. $dtend = $vevent->DTEND;
  351. } elseif (isset($vevent->DURATION)) {
  352. $isFloating = $vevent->DTSTART->isFloating();
  353. $dtend = clone $vevent->DTSTART;
  354. $endDateTime = $dtend->getDateTime();
  355. $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
  356. $dtend->setDateTime($endDateTime, $isFloating);
  357. } elseif (!$vevent->DTSTART->hasTime()) {
  358. $isFloating = $vevent->DTSTART->isFloating();
  359. $dtend = clone $vevent->DTSTART;
  360. $endDateTime = $dtend->getDateTime();
  361. $endDateTime = $endDateTime->modify('+1 day');
  362. $dtend->setDateTime($endDateTime, $isFloating);
  363. } else {
  364. $dtend = clone $vevent->DTSTART;
  365. }
  366. $isAllDay = $dtstart instanceof Property\ICalendar\Date;
  367. /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
  368. /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
  369. /** @var \DateTimeImmutable $dtstartDt */
  370. $dtstartDt = $dtstart->getDateTime();
  371. /** @var \DateTimeImmutable $dtendDt */
  372. $dtendDt = $dtend->getDateTime();
  373. $diff = $dtstartDt->diff($dtendDt);
  374. $dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
  375. $dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
  376. if ($isAllDay) {
  377. // One day event
  378. if ($diff->days === 1) {
  379. return $l10n->l('date', $dtstartDt, ['width' => 'medium']);
  380. }
  381. // DTEND is exclusive, so if the ics data says 2020-01-01 to 2020-01-05,
  382. // the email should show 2020-01-01 to 2020-01-04.
  383. $dtendDt->modify('-1 day');
  384. //event that spans over multiple days
  385. $localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
  386. $localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
  387. return $localeStart . ' - ' . $localeEnd;
  388. }
  389. /** @var Property\ICalendar\DateTime $dtstart */
  390. /** @var Property\ICalendar\DateTime $dtend */
  391. $isFloating = $dtstart->isFloating();
  392. $startTimezone = $endTimezone = null;
  393. if (!$isFloating) {
  394. $prop = $dtstart->offsetGet('TZID');
  395. if ($prop instanceof Parameter) {
  396. $startTimezone = $prop->getValue();
  397. }
  398. $prop = $dtend->offsetGet('TZID');
  399. if ($prop instanceof Parameter) {
  400. $endTimezone = $prop->getValue();
  401. }
  402. }
  403. $localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
  404. $l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
  405. // always show full date with timezone if timezones are different
  406. if ($startTimezone !== $endTimezone) {
  407. $localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
  408. return $localeStart . ' (' . $startTimezone . ') - ' .
  409. $localeEnd . ' (' . $endTimezone . ')';
  410. }
  411. // show only end time if date is the same
  412. if ($this->isDayEqual($dtstartDt, $dtendDt)) {
  413. $localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
  414. } else {
  415. $localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
  416. $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
  417. }
  418. return $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
  419. }
  420. /**
  421. * @param \DateTime $dtStart
  422. * @param \DateTime $dtEnd
  423. * @return bool
  424. */
  425. private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
  426. return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
  427. }
  428. /**
  429. * @param IEMailTemplate $template
  430. * @param IL10N $l10n
  431. * @param string $method
  432. * @param string $summary
  433. */
  434. private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
  435. $method, $summary) {
  436. if ($method === self::METHOD_CANCEL) {
  437. $template->setSubject('Canceled: ' . $summary);
  438. $template->addHeading($l10n->t('Invitation canceled'));
  439. } elseif ($method === self::METHOD_REPLY) {
  440. $template->setSubject('Re: ' . $summary);
  441. $template->addHeading($l10n->t('Invitation updated'));
  442. } else {
  443. $template->setSubject('Invitation: ' . $summary);
  444. $template->addHeading($l10n->t('Invitation'));
  445. }
  446. }
  447. /**
  448. * @param IEMailTemplate $template
  449. * @param IL10N $l10n
  450. * @param VEVENT $vevent
  451. */
  452. private function addBulletList(IEMailTemplate $template, IL10N $l10n, $vevent) {
  453. if ($vevent->SUMMARY) {
  454. $template->addBodyListItem($vevent->SUMMARY, $l10n->t('Title:'),
  455. $this->getAbsoluteImagePath('caldav/title.svg'),'','',self::IMIP_INDENT);
  456. }
  457. $meetingWhen = $this->generateWhenString($l10n, $vevent);
  458. if ($meetingWhen) {
  459. $template->addBodyListItem($meetingWhen, $l10n->t('Time:'),
  460. $this->getAbsoluteImagePath('caldav/time.svg'),'','',self::IMIP_INDENT);
  461. }
  462. if ($vevent->LOCATION) {
  463. $template->addBodyListItem($vevent->LOCATION, $l10n->t('Location:'),
  464. $this->getAbsoluteImagePath('caldav/location.svg'),'','',self::IMIP_INDENT);
  465. }
  466. if ($vevent->URL) {
  467. $url = $vevent->URL->getValue();
  468. $template->addBodyListItem(sprintf('<a href="%s">%s</a>',
  469. htmlspecialchars($url),
  470. htmlspecialchars($url)),
  471. $l10n->t('Link:'),
  472. $this->getAbsoluteImagePath('caldav/link.svg'),
  473. $url,'',self::IMIP_INDENT);
  474. }
  475. $this->addAttendees($template, $l10n, $vevent);
  476. /* Put description last, like an email body, since it can be arbitrarily long */
  477. if ($vevent->DESCRIPTION) {
  478. $template->addBodyListItem($vevent->DESCRIPTION->getValue(), $l10n->t('Description:'),
  479. $this->getAbsoluteImagePath('caldav/description.svg'),'','',self::IMIP_INDENT);
  480. }
  481. }
  482. /**
  483. * addAttendees: add organizer and attendee names/emails to iMip mail.
  484. *
  485. * Enable with DAV setting: invitation_list_attendees (default: no)
  486. *
  487. * The default is 'no', which matches old behavior, and is privacy preserving.
  488. *
  489. * To enable including attendees in invitation emails:
  490. * % php occ config:app:set dav invitation_list_attendees --value yes
  491. *
  492. * @param IEMailTemplate $template
  493. * @param IL10N $l10n
  494. * @param Message $iTipMessage
  495. * @param int $lastOccurrence
  496. * @author brad2014 on github.com
  497. */
  498. private function addAttendees(IEMailTemplate $template, IL10N $l10n, VEvent $vevent) {
  499. if ($this->config->getAppValue('dav', 'invitation_list_attendees', 'no') === 'no') {
  500. return;
  501. }
  502. if (isset($vevent->ORGANIZER)) {
  503. /** @var Property\ICalendar\CalAddress $organizer */
  504. $organizer = $vevent->ORGANIZER;
  505. $organizerURI = $organizer->getNormalizedValue();
  506. list($scheme,$organizerEmail) = explode(':',$organizerURI,2); # strip off scheme mailto:
  507. /** @var string|null $organizerName */
  508. $organizerName = isset($organizer['CN']) ? $organizer['CN'] : null;
  509. $organizerHTML = sprintf('<a href="%s">%s</a>',
  510. htmlspecialchars($organizerURI),
  511. htmlspecialchars($organizerName ?: $organizerEmail));
  512. $organizerText = sprintf('%s <%s>', $organizerName, $organizerEmail);
  513. if (isset($organizer['PARTSTAT'])) {
  514. /** @var Parameter $partstat */
  515. $partstat = $organizer['PARTSTAT'];
  516. if (strcasecmp($partstat->getValue(), 'ACCEPTED') === 0) {
  517. $organizerHTML .= ' ✔︎';
  518. $organizerText .= ' ✔︎';
  519. }
  520. }
  521. $template->addBodyListItem($organizerHTML, $l10n->t('Organizer:'),
  522. $this->getAbsoluteImagePath('caldav/organizer.svg'),
  523. $organizerText,'',self::IMIP_INDENT);
  524. }
  525. $attendees = $vevent->select('ATTENDEE');
  526. if (count($attendees) === 0) {
  527. return;
  528. }
  529. $attendeesHTML = [];
  530. $attendeesText = [];
  531. foreach ($attendees as $attendee) {
  532. $attendeeURI = $attendee->getNormalizedValue();
  533. list($scheme,$attendeeEmail) = explode(':',$attendeeURI,2); # strip off scheme mailto:
  534. $attendeeName = isset($attendee['CN']) ? $attendee['CN'] : null;
  535. $attendeeHTML = sprintf('<a href="%s">%s</a>',
  536. htmlspecialchars($attendeeURI),
  537. htmlspecialchars($attendeeName ?: $attendeeEmail));
  538. $attendeeText = sprintf('%s <%s>', $attendeeName, $attendeeEmail);
  539. if (isset($attendee['PARTSTAT'])
  540. && strcasecmp($attendee['PARTSTAT'], 'ACCEPTED') === 0) {
  541. $attendeeHTML .= ' ✔︎';
  542. $attendeeText .= ' ✔︎';
  543. }
  544. array_push($attendeesHTML, $attendeeHTML);
  545. array_push($attendeesText, $attendeeText);
  546. }
  547. $template->addBodyListItem(implode('<br/>',$attendeesHTML), $l10n->t('Attendees:'),
  548. $this->getAbsoluteImagePath('caldav/attendees.svg'),
  549. implode("\n",$attendeesText),'',self::IMIP_INDENT);
  550. }
  551. /**
  552. * @param IEMailTemplate $template
  553. * @param IL10N $l10n
  554. * @param Message $iTipMessage
  555. * @param int $lastOccurrence
  556. */
  557. private function addResponseButtons(IEMailTemplate $template, IL10N $l10n,
  558. Message $iTipMessage, $lastOccurrence) {
  559. $token = $this->createInvitationToken($iTipMessage, $lastOccurrence);
  560. $template->addBodyButtonGroup(
  561. $l10n->t('Accept'),
  562. $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.accept', [
  563. 'token' => $token,
  564. ]),
  565. $l10n->t('Decline'),
  566. $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.decline', [
  567. 'token' => $token,
  568. ])
  569. );
  570. $moreOptionsURL = $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.options', [
  571. 'token' => $token,
  572. ]);
  573. $html = vsprintf('<small><a href="%s">%s</a></small>', [
  574. $moreOptionsURL, $l10n->t('More options …')
  575. ]);
  576. $text = $l10n->t('More options at %s', [$moreOptionsURL]);
  577. $template->addBodyText($html, $text);
  578. }
  579. /**
  580. * @param string $path
  581. * @return string
  582. */
  583. private function getAbsoluteImagePath($path) {
  584. return $this->urlGenerator->getAbsoluteURL(
  585. $this->urlGenerator->imagePath('core', $path)
  586. );
  587. }
  588. /**
  589. * @param Message $iTipMessage
  590. * @param int $lastOccurrence
  591. * @return string
  592. */
  593. private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
  594. $token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
  595. /** @var VEvent $vevent */
  596. $vevent = $iTipMessage->message->VEVENT;
  597. $attendee = $iTipMessage->recipient;
  598. $organizer = $iTipMessage->sender;
  599. $sequence = $iTipMessage->sequence;
  600. $recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ?
  601. $vevent->{'RECURRENCE-ID'}->serialize() : null;
  602. $uid = $vevent->{'UID'};
  603. $query = $this->db->getQueryBuilder();
  604. $query->insert('calendar_invitations')
  605. ->values([
  606. 'token' => $query->createNamedParameter($token),
  607. 'attendee' => $query->createNamedParameter($attendee),
  608. 'organizer' => $query->createNamedParameter($organizer),
  609. 'sequence' => $query->createNamedParameter($sequence),
  610. 'recurrenceid' => $query->createNamedParameter($recurrenceId),
  611. 'expiration' => $query->createNamedParameter($lastOccurrence),
  612. 'uid' => $query->createNamedParameter($uid)
  613. ])
  614. ->execute();
  615. return $token;
  616. }
  617. }