From 53ad4f5a25e94053f77627749d19b8bf7d6b506d Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Wed, 18 Oct 2023 18:14:06 +0200 Subject: [PATCH 01/19] #1171: The `textarea` is showing and is working as intended for the purpose of creating and updating the calendar event. BUG: carriage returns don't seem to work WIP: the text is hardcoded as of now, have to use Messages library for translations. --- www/calendar/app-calendar.less | 6 ++++- www/calendar/inner.js | 45 +++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index 6bb76cf8e..53ade5d18 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -209,9 +209,13 @@ .tui-full-calendar-section-date-dash { height: auto; } - .tui-full-calendar-section-title, .tui-full-calendar-section-location { + .tui-full-calendar-section-title, .tui-full-calendar-section-location, .tui-full-calendar-section-description { width: 100%; } + .tui-full-calendar-section-description textarea { + width: 100%; + height: 200px; + } .tui-full-calendar-dropdown-menu { top: 38px; width: 221px; // same as button diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 89370dde4..69f018569 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -99,6 +99,7 @@ define([ }; var newEvent = function (event, cb) { var reminders = APP.notificationsEntries; + var description = APP.description; var startDate = event.start._date; var endDate = event.end._date; @@ -113,6 +114,7 @@ define([ isAllDay: event.isAllDay, end: +endDate, reminders: reminders, + description: description, recurrenceRule: event.recurrenceRule }; @@ -283,6 +285,7 @@ define([ var obj = data.content[uid]; obj.title = obj.title || ""; obj.location = obj.location || ""; + obj.description = obj.description || ""; if (obj.isAllDay && obj.startDay) { obj.start = +Flatpickr.parseDate((obj.startDay)); } if (obj.isAllDay && obj.endDay) { var endDate = Flatpickr.parseDate(obj.endDay); @@ -1023,6 +1026,9 @@ ICS ==> create a new event with the same UID and a RECURRENCE-ID field (with a v if (JSONSortify(oldRec || '') !== JSONSortify(rec)) { changes.recurrenceRule = rec; } + + var description = APP.description; + changes.description = description; } @@ -1900,7 +1906,6 @@ APP.recurrenceRule = { var getNotificationDropdown = function () { var ev = APP.editModalData; var calId = ev.selectedCal.id; - // DEFAULT HERE [10] ==> 10 minutes before the event var id = (ev.id && ev.id.split('|')[0]) || undefined; var _ev = APP.calendar.getSchedule(ev.id, calId); var oldReminders = _ev && _ev.raw && _ev.raw.reminders; @@ -2008,6 +2013,41 @@ APP.recurrenceRule = { ]); }; + var getDescriptionInput = function() { + var ev = APP.editModalData; + var calId = ev.selectedCal.id; + // DEFAULT HERE [10] ==> 10 minutes before the event + var id = (ev.id && ev.id.split('|')[0]) || undefined; + var _ev = APP.calendar.getSchedule(ev.id, calId); + var oldDescription = _ev && _ev.raw && _ev.raw.description; + if (!oldDescription) { + oldDescription = Util.find(APP.calendars, [calId, 'content', 'content', id, 'description']) || ""; + } + + APP.description = oldDescription; + var description = h('textarea.tui-full-calendar-content', { + placeholder: 'Description', // TODO: replace with Message.calendar_description + id: 'tui-full-calendar-description', + }); + + description.value = oldDescription; + + var updateDescription = function(value) { + APP.description = value; + }; + + var $description = $(description); + $description.on('input', function() { + updateDescription(description.value); + }); + + return h('div.tui-full-calendar-popup-section.tui-full-calendar-vlayout-area', [ + h('div.tui-full-calendar-popup-section-item.tui-full-calendar-section-description', [ + description, + ]), + ]); + }; + var createToolbar = function () { var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle', 'notifications']; var configTb = { @@ -2118,6 +2158,9 @@ APP.recurrenceRule = { var div = getNotificationDropdown(); $button.before(div); + var descriptionInput = getDescriptionInput(); + $startDate.parent().parent().before(descriptionInput); + // Use Flatpickr with or without time depending on allday checkbox var $cbox = $el.find('#tui-full-calendar-schedule-allday'); var allDay = $cbox.is(':checked'); From bbb4a46e0f83c76f61da46c0678b8578d2b9a1e3 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Wed, 18 Oct 2023 18:35:43 +0200 Subject: [PATCH 02/19] #1171: Fix nowrap in textareas --- www/calendar/app-calendar.less | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index 53ade5d18..d18519580 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -197,7 +197,9 @@ background-color: @cp_dropdown-bg-hover; } .tui-full-calendar-content { - white-space: nowrap; + &:not(textarea) { + white-space: nowrap; + } overflow: hidden; text-overflow: ellipsis; font: @colortheme_app-font; From 398df9d58fa4089102769aea775b1d36d0e48cf6 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 19 Oct 2023 10:50:47 +0200 Subject: [PATCH 03/19] #1171: Calendar ICS import and export handle the `description` field --- www/calendar/export.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/www/calendar/export.js b/www/calendar/export.js index 694179a9f..5d851d560 100644 --- a/www/calendar/export.js +++ b/www/calendar/export.js @@ -97,7 +97,6 @@ define([ }; - var addEvent = function (arr, data, recId) { var uid = data.id; var dt = getDT(data); @@ -105,6 +104,10 @@ define([ var end = dt.end; var rrule = getRRule(data); + var escapeValue = function(str) { + return str.replace(/\n/g, ["\\n"]); + } + Array.prototype.push.apply(arr, [ 'BEGIN:VEVENT', 'DTSTAMP:'+getICSDate(+new Date()), @@ -115,6 +118,7 @@ define([ rrule, 'SUMMARY:'+ data.title, 'LOCATION:'+ data.location, + 'DESCRIPTION:' + escapeValue(data.description), ].filter(Boolean)); if (Array.isArray(data.reminders)) { @@ -355,6 +359,7 @@ define([ category: 'time', title: ev.getFirstPropertyValue('summary'), location: ev.getFirstPropertyValue('location'), + description: ev.getFirstPropertyValue('description'), isAllDay: isAllDay, start: start, end: end, From 7ceca7d4af446604a06d340e0fddafaff8bf7012 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 19 Oct 2023 11:42:21 +0200 Subject: [PATCH 04/19] #1171: Rename description with body * Rename description in the schedule object -> Use the body property of [tui-calendar](https://github.com/nhn/tui.calendar/) for to show the description in the details popup : --- www/calendar/app-calendar.less | 4 +-- www/calendar/export.js | 8 +++--- www/calendar/inner.js | 47 +++++++++++++++++++--------------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index d18519580..ea17d53dd 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -211,10 +211,10 @@ .tui-full-calendar-section-date-dash { height: auto; } - .tui-full-calendar-section-title, .tui-full-calendar-section-location, .tui-full-calendar-section-description { + .tui-full-calendar-section-title, .tui-full-calendar-section-location, .tui-full-calendar-section-body { width: 100%; } - .tui-full-calendar-section-description textarea { + .tui-full-calendar-section-body textarea { width: 100%; height: 200px; } diff --git a/www/calendar/export.js b/www/calendar/export.js index 5d851d560..ad5f23fa3 100644 --- a/www/calendar/export.js +++ b/www/calendar/export.js @@ -106,7 +106,7 @@ define([ var escapeValue = function(str) { return str.replace(/\n/g, ["\\n"]); - } + }; Array.prototype.push.apply(arr, [ 'BEGIN:VEVENT', @@ -118,7 +118,7 @@ define([ rrule, 'SUMMARY:'+ data.title, 'LOCATION:'+ data.location, - 'DESCRIPTION:' + escapeValue(data.description), + 'DESCRIPTION:' + escapeValue(data.body), ].filter(Boolean)); if (Array.isArray(data.reminders)) { @@ -314,7 +314,7 @@ define([ } // Store other properties - var used = ['dtstart', 'dtend', 'uid', 'summary', 'location', 'dtstamp', 'rrule', 'recurrence-id']; + var used = ['dtstart', 'dtend', 'uid', 'summary', 'location', 'description', 'dtstamp', 'rrule', 'recurrence-id']; var hidden = []; ev.getAllProperties().forEach(function (p) { if (used.indexOf(p.name) !== -1) { return; } @@ -359,7 +359,7 @@ define([ category: 'time', title: ev.getFirstPropertyValue('summary'), location: ev.getFirstPropertyValue('location'), - description: ev.getFirstPropertyValue('description'), + body: ev.getFirstPropertyValue('description'), isAllDay: isAllDay, start: start, end: end, diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 69f018569..d37554155 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -285,7 +285,7 @@ define([ var obj = data.content[uid]; obj.title = obj.title || ""; obj.location = obj.location || ""; - obj.description = obj.description || ""; + obj.body = obj.body || ""; if (obj.isAllDay && obj.startDay) { obj.start = +Flatpickr.parseDate((obj.startDay)); } if (obj.isAllDay && obj.endDay) { var endDate = Flatpickr.parseDate(obj.endDay); @@ -391,6 +391,13 @@ define([ return Messages._getKey('calendar_location', [str]); }, + popupDetailBody: function(schedule) { + var d = schedule.body; + var str = Util.fixHTML(d); + str = str.replace(/(?:\r\n|\r|\n)/g, '
'); + delete APP.body; + return 'Description:
' + str; + }, popupIsAllDay: function() { return Messages.calendar_allDay; }, titlePlaceholder: function() { return Messages.calendar_title; }, locationPlaceholder: function() { return Messages.calendar_loc; }, @@ -1027,8 +1034,8 @@ ICS ==> create a new event with the same UID and a RECURRENCE-ID field (with a v changes.recurrenceRule = rec; } - var description = APP.description; - changes.description = description; + var body = APP.body; + changes.body = body; } @@ -2013,37 +2020,37 @@ APP.recurrenceRule = { ]); }; - var getDescriptionInput = function() { + var getBodyInput = function() { var ev = APP.editModalData; var calId = ev.selectedCal.id; // DEFAULT HERE [10] ==> 10 minutes before the event var id = (ev.id && ev.id.split('|')[0]) || undefined; var _ev = APP.calendar.getSchedule(ev.id, calId); - var oldDescription = _ev && _ev.raw && _ev.raw.description; - if (!oldDescription) { - oldDescription = Util.find(APP.calendars, [calId, 'content', 'content', id, 'description']) || ""; + var oldBody = _ev && _ev.raw && _ev.raw.body; + if (!oldBody) { + oldBody = Util.find(APP.calendars, [calId, 'content', 'content', id, 'body']) || ""; } - APP.description = oldDescription; - var description = h('textarea.tui-full-calendar-content', { + APP.body = oldBody; + var body = h('textarea.tui-full-calendar-content', { placeholder: 'Description', // TODO: replace with Message.calendar_description - id: 'tui-full-calendar-description', + id: 'tui-full-calendar-body', }); - description.value = oldDescription; + body.value = oldBody; - var updateDescription = function(value) { - APP.description = value; + var updateBody = function(value) { + APP.body = value; }; - var $description = $(description); - $description.on('input', function() { - updateDescription(description.value); + var $body = $(body); + $body.on('input', function() { + updateBody(body.value); }); return h('div.tui-full-calendar-popup-section.tui-full-calendar-vlayout-area', [ - h('div.tui-full-calendar-popup-section-item.tui-full-calendar-section-description', [ - description, + h('div.tui-full-calendar-popup-section-item.tui-full-calendar-section-body', [ + body, ]), ]); }; @@ -2158,8 +2165,8 @@ APP.recurrenceRule = { var div = getNotificationDropdown(); $button.before(div); - var descriptionInput = getDescriptionInput(); - $startDate.parent().parent().before(descriptionInput); + var bodyInput = getBodyInput(); + $startDate.parent().parent().before(bodyInput); // Use Flatpickr with or without time depending on allday checkbox var $cbox = $el.find('#tui-full-calendar-schedule-allday'); From d61b8376cc7647f69c47a6b12162bc34c6a72cfa Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 19 Oct 2023 17:12:05 +0200 Subject: [PATCH 05/19] #1171: fix no scroll in long descriptions --- www/calendar/app-calendar.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index ea17d53dd..7f9a4336b 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -199,8 +199,8 @@ .tui-full-calendar-content { &:not(textarea) { white-space: nowrap; + overflow: hidden; } - overflow: hidden; text-overflow: ellipsis; font: @colortheme_app-font; padding: 0 10px; From 8c3c3d8d5183f792b4b50fefea0356f48a453d22 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 19 Oct 2023 17:48:16 +0200 Subject: [PATCH 06/19] #1171: Render markdown with diffMK for details popup * Sanitized markdown raises a warning: ``` marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options marked.min.js:6:44783 ``` --- www/calendar/inner.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index d37554155..0c8cff264 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -24,6 +24,8 @@ define([ '/common/inner/access.js', '/common/inner/properties.js', + '/common/diffMarked.js', + '/common/jscolor.js', '/components/file-saver/FileSaver.min.js', 'css!/lib/calendar/tui-calendar.min.css', @@ -51,7 +53,8 @@ define([ Export, Rec, Flatpickr, - Share, Access, Properties + Share, Access, Properties, + diffMk, ) { Messages.calendar_rec_change_first = "You moved the first repeating event to different calendar. You can only apply this change to all repeated events."; // XXX New translation key @@ -392,11 +395,9 @@ define([ return Messages._getKey('calendar_location', [str]); }, popupDetailBody: function(schedule) { - var d = schedule.body; - var str = Util.fixHTML(d); - str = str.replace(/(?:\r\n|\r|\n)/g, '
'); + var str = schedule.body; delete APP.body; - return 'Description:
' + str; + return 'Description:
' + diffMk.render(str,true); }, popupIsAllDay: function() { return Messages.calendar_allDay; }, titlePlaceholder: function() { return Messages.calendar_title; }, From fba0ccd032772dfd2020476cc985d99748e36aee Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 19 Oct 2023 18:35:32 +0200 Subject: [PATCH 07/19] Lint compliance Lint compliance for the calendar description field (#1171) --- www/calendar/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 0c8cff264..8d9c35d73 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -54,7 +54,7 @@ define([ Rec, Flatpickr, Share, Access, Properties, - diffMk, + diffMk ) { Messages.calendar_rec_change_first = "You moved the first repeating event to different calendar. You can only apply this change to all repeated events."; // XXX New translation key From 087f786f9fd50b682dafcf900579a65dd49ab798 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Fri, 20 Oct 2023 10:49:09 +0200 Subject: [PATCH 08/19] #1171: Add translation keys * Inlined at the beginning of `www/calendar/inner.js` for future integration in weblate on release. --- www/calendar/inner.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 8d9c35d73..f17cefad1 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -57,8 +57,12 @@ define([ diffMk ) { + // XXX New translation keys Messages.calendar_rec_change_first = "You moved the first repeating event to different calendar. You can only apply this change to all repeated events."; // XXX New translation key Messages.calendar_rec_change = "You moved a repeating event to different calendar. You can only apply this change to this event or all repeated events."; // XXX New translation key + Messages.calendar_desc = "Description"; // XXX maybe rename in `description`? + Messages.calendar_description = "Description:{0}{1}"; // XXX + var SaveAs = window.saveAs; var APP = window.APP = { calendars: {} @@ -397,7 +401,7 @@ define([ popupDetailBody: function(schedule) { var str = schedule.body; delete APP.body; - return 'Description:
' + diffMk.render(str,true); + return Messages._getKey('calendar_description', ['
', diffMk.render(str, true)]); }, popupIsAllDay: function() { return Messages.calendar_allDay; }, titlePlaceholder: function() { return Messages.calendar_title; }, @@ -2034,7 +2038,7 @@ APP.recurrenceRule = { APP.body = oldBody; var body = h('textarea.tui-full-calendar-content', { - placeholder: 'Description', // TODO: replace with Message.calendar_description + placeholder: Messages.calendar_desc, id: 'tui-full-calendar-body', }); From 00c1323f07325abae0fa0e6bd1406da1b8257eb3 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Fri, 20 Oct 2023 14:12:53 +0200 Subject: [PATCH 09/19] #1171: ICS line wrapping for descriptions in export Descriptions can be long and RFC5545 states that a line shouldn't be more than 75 bytes long. --- www/calendar/export.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/www/calendar/export.js b/www/calendar/export.js index ad5f23fa3..de61be5e6 100644 --- a/www/calendar/export.js +++ b/www/calendar/export.js @@ -104,8 +104,13 @@ define([ var end = dt.end; var rrule = getRRule(data); - var escapeValue = function(str) { - return str.replace(/\n/g, ["\\n"]); + var formatDescription = function(str) { + var result = str.replace(/\n/g, ["\\n"]); + // XXX Should use ical.js helper foldline instead ↓ + // XXX https://kewisch.github.io/ical.js/api/ICAL.module_helpers.html#.foldline + // In RFC5545: https://www.rfc-editor.org/rfc/rfc5545#section-3.1 + result = result.replace(/([^\n]{1,74})/g, '$1\n '); + return result; }; Array.prototype.push.apply(arr, [ @@ -118,7 +123,7 @@ define([ rrule, 'SUMMARY:'+ data.title, 'LOCATION:'+ data.location, - 'DESCRIPTION:' + escapeValue(data.body), + 'DESCRIPTION:' + formatDescription(data.body), ].filter(Boolean)); if (Array.isArray(data.reminders)) { From b9e698ef14913e20cd8b9a4d10bb9570394dbb48 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Fri, 20 Oct 2023 14:25:52 +0200 Subject: [PATCH 10/19] #1171: Cleaning code --- www/calendar/inner.js | 1 - 1 file changed, 1 deletion(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index f17cefad1..54b9d55ca 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -2028,7 +2028,6 @@ APP.recurrenceRule = { var getBodyInput = function() { var ev = APP.editModalData; var calId = ev.selectedCal.id; - // DEFAULT HERE [10] ==> 10 minutes before the event var id = (ev.id && ev.id.split('|')[0]) || undefined; var _ev = APP.calendar.getSchedule(ev.id, calId); var oldBody = _ev && _ev.raw && _ev.raw.body; From 6b7eedde229010f36b07220d0bb71273755f7a3d Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Fri, 20 Oct 2023 14:41:00 +0200 Subject: [PATCH 11/19] #1171: Use the ical.js implementation for foldline According to [RFC5545](https://www.rfc-editor.org/rfc/rfc5545#section-3.1): ``` Note: It is possible for very simple implementations to generate improperly folded lines in the middle of a UTF-8 multi-octet sequence. For this reason, implementations need to unfold lines in such a way to properly restore the original sequence. ``` It actually happens when an emoji takes the 75th and 76th bit. --- www/calendar/export.js | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/www/calendar/export.js b/www/calendar/export.js index de61be5e6..882c57f94 100644 --- a/www/calendar/export.js +++ b/www/calendar/export.js @@ -96,6 +96,41 @@ define([ return rrule; }; + // XXX Below function: from ical.js, should be imported instead. + // XXX However, if it's done, it would be better to refactor the + // XXX export module to directly use ical.js for exporting ICS. + var foldline = function(aLine) { + var foldLength = 75; + var newLineChar = '\r\n'; + let result = ""; + let line = aLine || "", pos = 0, line_length = 0; + //pos counts position in line for the UTF-16 presentation + //line_length counts the bytes for the UTF-8 presentation + while (line.length) { + let cp = line.codePointAt(pos); + if (cp < 128) { + ++line_length; + } + else if (cp < 2048) { + line_length += 2;//needs 2 UTF-8 bytes + } + else if (cp < 65536) { + line_length += 3; + } + else { + line_length += 4; //cp is less than 1114112 + } + if (line_length < foldLength + 1) { + pos += cp > 65535 ? 2 : 1; + } + else { + result += newLineChar + " " + line.slice(0, Math.max(0, pos)); + line = line.slice(Math.max(0, pos)); + pos = line_length = 0; + } + } + return result.slice(newLineChar.length + 1); + }; var addEvent = function (arr, data, recId) { var uid = data.id; @@ -105,11 +140,12 @@ define([ var rrule = getRRule(data); var formatDescription = function(str) { - var result = str.replace(/\n/g, ["\\n"]); + var componentName = 'DESCRIPTION:'; + var result = componentName + str.replace(/\n/g, ["\\n"]); // XXX Should use ical.js helper foldline instead ↓ // XXX https://kewisch.github.io/ical.js/api/ICAL.module_helpers.html#.foldline // In RFC5545: https://www.rfc-editor.org/rfc/rfc5545#section-3.1 - result = result.replace(/([^\n]{1,74})/g, '$1\n '); + result = foldline(result); return result; }; @@ -123,7 +159,7 @@ define([ rrule, 'SUMMARY:'+ data.title, 'LOCATION:'+ data.location, - 'DESCRIPTION:' + formatDescription(data.body), + formatDescription(data.body), ].filter(Boolean)); if (Array.isArray(data.reminders)) { From 9b2f565c19d25c0411676be4b42c95c425c1f7cc Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Tue, 31 Oct 2023 15:53:14 +0100 Subject: [PATCH 12/19] #1171: rename badly named variable body was a wrong choice of name --- www/calendar/inner.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 54b9d55ca..78bffadf7 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -106,7 +106,7 @@ define([ }; var newEvent = function (event, cb) { var reminders = APP.notificationsEntries; - var description = APP.description; + var eventBody = APP.description; var startDate = event.start._date; var endDate = event.end._date; @@ -121,7 +121,7 @@ define([ isAllDay: event.isAllDay, end: +endDate, reminders: reminders, - description: description, + body: eventBody, recurrenceRule: event.recurrenceRule }; @@ -1039,8 +1039,8 @@ ICS ==> create a new event with the same UID and a RECURRENCE-ID field (with a v changes.recurrenceRule = rec; } - var body = APP.body; - changes.body = body; + var eventBody = APP.eventBody; + changes.body = eventBody; } @@ -2030,31 +2030,31 @@ APP.recurrenceRule = { var calId = ev.selectedCal.id; var id = (ev.id && ev.id.split('|')[0]) || undefined; var _ev = APP.calendar.getSchedule(ev.id, calId); - var oldBody = _ev && _ev.raw && _ev.raw.body; - if (!oldBody) { - oldBody = Util.find(APP.calendars, [calId, 'content', 'content', id, 'body']) || ""; + var oldEventBody = _ev && _ev.raw && _ev.raw.body; + if (!oldEventBody) { + oldEventBody = Util.find(APP.calendars, [calId, 'content', 'content', id, 'body']) || ""; } - APP.body = oldBody; - var body = h('textarea.tui-full-calendar-content', { + APP.eventBody = oldEventBody; + var description = h('textarea.tui-full-calendar-content', { placeholder: Messages.calendar_desc, id: 'tui-full-calendar-body', }); - body.value = oldBody; + description.value = oldEventBody; var updateBody = function(value) { - APP.body = value; + APP.eventBody = value; }; - var $body = $(body); - $body.on('input', function() { - updateBody(body.value); + var $description = $(description); + $description.on('input', function() { + updateBody(description.value); }); return h('div.tui-full-calendar-popup-section.tui-full-calendar-vlayout-area', [ h('div.tui-full-calendar-popup-section-item.tui-full-calendar-section-body', [ - body, + description, ]), ]); }; From a2d0aad4a2a9be26c2398c92d0e435c23f92566d Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Tue, 31 Oct 2023 17:32:30 +0100 Subject: [PATCH 13/19] #1171: Bug with recurring events An issue when getting back the content of an edited only recurring event --- www/calendar/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 78bffadf7..d8ade1e08 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -2030,7 +2030,7 @@ APP.recurrenceRule = { var calId = ev.selectedCal.id; var id = (ev.id && ev.id.split('|')[0]) || undefined; var _ev = APP.calendar.getSchedule(ev.id, calId); - var oldEventBody = _ev && _ev.raw && _ev.raw.body; + var oldEventBody = _ev && _ev.body; if (!oldEventBody) { oldEventBody = Util.find(APP.calendars, [calId, 'content', 'content', id, 'body']) || ""; } From a56b3ace7a5ca361b302e5a34e1a81db181e77d0 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 2 Nov 2023 11:15:14 +0100 Subject: [PATCH 14/19] #1171: Use CodeMirror in the description edition --- www/calendar/app-calendar.less | 29 ++++++++++++++++++-------- www/calendar/inner.js | 37 +++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index 7f9a4336b..460188f92 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -197,10 +197,6 @@ background-color: @cp_dropdown-bg-hover; } .tui-full-calendar-content { - &:not(textarea) { - white-space: nowrap; - overflow: hidden; - } text-overflow: ellipsis; font: @colortheme_app-font; padding: 0 10px; @@ -214,10 +210,6 @@ .tui-full-calendar-section-title, .tui-full-calendar-section-location, .tui-full-calendar-section-body { width: 100%; } - .tui-full-calendar-section-body textarea { - width: 100%; - height: 200px; - } .tui-full-calendar-dropdown-menu { top: 38px; width: 221px; // same as button @@ -251,6 +243,27 @@ border-radius: 2px; } } + .CodeMirror { + margin-top: 5px; + background: @cp_forms-bg; + color: @cryptpad_text_col; + border: 1px solid @cp_forms-border; + border-radius: @variables_radius; + width: 100%; + height: 80px; + font: @colortheme_app-font; + font-size: 16px; + line-height: initial; + pre { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; + } + } + .CodeMirror-placeholder { + color: @cp_forms-placeholder; + } } } .tui-full-calendar-popup-detail { diff --git a/www/calendar/inner.js b/www/calendar/inner.js index d8ade1e08..7a01acf75 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -25,10 +25,17 @@ define([ '/common/inner/properties.js', '/common/diffMarked.js', + '/common/sframe-common-codemirror.js', + 'cm/lib/codemirror', + 'cm/addon/display/autorefresh', + 'cm/addon/display/placeholder', + 'cm/mode/gfm/gfm', '/common/jscolor.js', '/components/file-saver/FileSaver.min.js', 'css!/lib/calendar/tui-calendar.min.css', + 'css!/components/codemirror/lib/codemirror.css', + 'css!/components/codemirror/addon/dialog/dialog.css', 'css!/components/components-font-awesome/css/font-awesome.min.css', 'css!/components/bootstrap/dist/css/bootstrap.min.css', 'less!/calendar/app-calendar.less', @@ -54,7 +61,9 @@ define([ Rec, Flatpickr, Share, Access, Properties, - diffMk + diffMk, + SFCodeMirror, + CodeMirror ) { // XXX New translation keys @@ -2043,20 +2052,30 @@ APP.recurrenceRule = { description.value = oldEventBody; + var block = h('div.tui-full-calendar-popup-section', [ + description, + ]); + + var cm = SFCodeMirror.create("gfm", CodeMirror, description); + var editor = APP.editor = cm.editor; + editor.setOption('lineNumbers', false); + editor.setOption('lineWrapping', true); + editor.setOption('styleActiveLine', false); + editor.setOption('readOnly', false); + editor.setOption('autoRefresh', true); + editor.setOption('gutters', []); + cm.configureTheme(common, function () {}); + editor.setValue(oldEventBody); + var updateBody = function(value) { APP.eventBody = value; }; - var $description = $(description); - $description.on('input', function() { - updateBody(description.value); + editor.on('changes', function() { + updateBody(editor.getValue()); }); - return h('div.tui-full-calendar-popup-section.tui-full-calendar-vlayout-area', [ - h('div.tui-full-calendar-popup-section-item.tui-full-calendar-section-body', [ - description, - ]), - ]); + return block; }; var createToolbar = function () { From a6c01f5b7c979a7f910c7e621c4b93ebec76f838 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 2 Nov 2023 11:42:45 +0100 Subject: [PATCH 15/19] #1171: Integration with #1275 --- www/calendar/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 7a01acf75..78e528e3d 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -115,7 +115,7 @@ define([ }; var newEvent = function (event, cb) { var reminders = APP.notificationsEntries; - var eventBody = APP.description; + var eventBody = APP.eventBody; var startDate = event.start._date; var endDate = event.end._date; From 66a2815993ae4cf741e39ef04d900551281e9be6 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 2 Nov 2023 11:45:01 +0100 Subject: [PATCH 16/19] #1171: Alignment of the description content --- www/calendar/app-calendar.less | 1 + 1 file changed, 1 insertion(+) diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index 460188f92..e41b8ea4d 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -254,6 +254,7 @@ font: @colortheme_app-font; font-size: 16px; line-height: initial; + padding-left: 0.3rem; pre { margin: 0; font-family: inherit; From 4c80de725f998964dd3d9c639525d61a68b874c3 Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 2 Nov 2023 11:58:07 +0100 Subject: [PATCH 17/19] #1171: Use of ical.js for line formatting while exporting --- www/calendar/export.js | 46 +++++------------------------------------- 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/www/calendar/export.js b/www/calendar/export.js index 882c57f94..4d6c15768 100644 --- a/www/calendar/export.js +++ b/www/calendar/export.js @@ -3,8 +3,9 @@ define([ '/customize/pages.js', '/common/common-util.js', - '/calendar/recurrence.js' -], function (Pages, Util, Rec) { + '/calendar/recurrence.js', + '/lib/ical.min.js' +], function (Pages, Util, Rec, ICAL) { var module = {}; var getICSDate = function (str) { @@ -96,42 +97,6 @@ define([ return rrule; }; - // XXX Below function: from ical.js, should be imported instead. - // XXX However, if it's done, it would be better to refactor the - // XXX export module to directly use ical.js for exporting ICS. - var foldline = function(aLine) { - var foldLength = 75; - var newLineChar = '\r\n'; - let result = ""; - let line = aLine || "", pos = 0, line_length = 0; - //pos counts position in line for the UTF-16 presentation - //line_length counts the bytes for the UTF-8 presentation - while (line.length) { - let cp = line.codePointAt(pos); - if (cp < 128) { - ++line_length; - } - else if (cp < 2048) { - line_length += 2;//needs 2 UTF-8 bytes - } - else if (cp < 65536) { - line_length += 3; - } - else { - line_length += 4; //cp is less than 1114112 - } - if (line_length < foldLength + 1) { - pos += cp > 65535 ? 2 : 1; - } - else { - result += newLineChar + " " + line.slice(0, Math.max(0, pos)); - line = line.slice(Math.max(0, pos)); - pos = line_length = 0; - } - } - return result.slice(newLineChar.length + 1); - }; - var addEvent = function (arr, data, recId) { var uid = data.id; var dt = getDT(data); @@ -142,10 +107,9 @@ define([ var formatDescription = function(str) { var componentName = 'DESCRIPTION:'; var result = componentName + str.replace(/\n/g, ["\\n"]); - // XXX Should use ical.js helper foldline instead ↓ - // XXX https://kewisch.github.io/ical.js/api/ICAL.module_helpers.html#.foldline + var ICAL = window.ICAL; // In RFC5545: https://www.rfc-editor.org/rfc/rfc5545#section-3.1 - result = foldline(result); + result = ICAL.helpers.foldline(result); return result; }; From df1b59513ba4c5a2fd1f5560e658c1bb5ca4b37e Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 2 Nov 2023 13:32:57 +0100 Subject: [PATCH 18/19] #1171: Fix a change in the variable names --- www/calendar/inner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 78e528e3d..0ffad3e4e 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -409,7 +409,7 @@ define([ }, popupDetailBody: function(schedule) { var str = schedule.body; - delete APP.body; + delete APP.eventBody; return Messages._getKey('calendar_description', ['
', diffMk.render(str, true)]); }, popupIsAllDay: function() { return Messages.calendar_allDay; }, From 039cb9d59812951d20e9590a8ad6c59e5c6fa81d Mon Sep 17 00:00:00 2001 From: Fabrice Mouhartem Date: Thu, 2 Nov 2023 13:39:17 +0100 Subject: [PATCH 19/19] #1171: Don't send changes in description everytime --- www/calendar/inner.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 0ffad3e4e..450912a56 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -115,7 +115,7 @@ define([ }; var newEvent = function (event, cb) { var reminders = APP.notificationsEntries; - var eventBody = APP.eventBody; + var eventBody = APP.eventBody || ""; var startDate = event.start._date; var endDate = event.end._date; @@ -1048,8 +1048,10 @@ ICS ==> create a new event with the same UID and a RECURRENCE-ID field (with a v changes.recurrenceRule = rec; } - var eventBody = APP.eventBody; - changes.body = eventBody; + var eventBody = APP.eventBody || ""; + if (eventBody !== old.body) { + changes.body = eventBody; + } }