diff --git a/www/calendar/app-calendar.less b/www/calendar/app-calendar.less index 6bb76cf8e..ca6277eed 100644 --- a/www/calendar/app-calendar.less +++ b/www/calendar/app-calendar.less @@ -127,7 +127,6 @@ width: 540px; max-width: 100%; max-height: 100%; - overflow-y: auto; } } #tui-full-calendar-popup-arrow { @@ -197,8 +196,6 @@ background-color: @cp_dropdown-bg-hover; } .tui-full-calendar-content { - white-space: nowrap; - overflow: hidden; text-overflow: ellipsis; font: @colortheme_app-font; padding: 0 10px; @@ -209,7 +206,7 @@ .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-body { width: 100%; } .tui-full-calendar-dropdown-menu { @@ -245,6 +242,28 @@ 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; + padding-left: 0.3rem; + 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/export.js b/www/calendar/export.js index 694179a9f..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,8 +97,6 @@ define([ return rrule; }; - - var addEvent = function (arr, data, recId) { var uid = data.id; var dt = getDT(data); @@ -105,6 +104,15 @@ define([ var end = dt.end; var rrule = getRRule(data); + var formatDescription = function(str) { + var componentName = 'DESCRIPTION:'; + var result = componentName + str.replace(/\n/g, ["\\n"]); + var ICAL = window.ICAL; + // In RFC5545: https://www.rfc-editor.org/rfc/rfc5545#section-3.1 + result = ICAL.helpers.foldline(result); + return result; + }; + Array.prototype.push.apply(arr, [ 'BEGIN:VEVENT', 'DTSTAMP:'+getICSDate(+new Date()), @@ -115,6 +123,7 @@ define([ rrule, 'SUMMARY:'+ data.title, 'LOCATION:'+ data.location, + formatDescription(data.body), ].filter(Boolean)); if (Array.isArray(data.reminders)) { @@ -310,7 +319,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; } @@ -355,6 +364,7 @@ define([ category: 'time', title: ev.getFirstPropertyValue('summary'), location: ev.getFirstPropertyValue('location'), + body: ev.getFirstPropertyValue('description'), isAllDay: isAllDay, start: start, end: end, diff --git a/www/calendar/inner.js b/www/calendar/inner.js index 07bde0e3a..10709d552 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -25,9 +25,18 @@ define([ '/common/inner/access.js', '/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', @@ -53,11 +62,18 @@ define([ Rec, Flatpickr, DatePicker, - Share, Access, Properties + Share, Access, Properties, + diffMk, + SFCodeMirror, + CodeMirror ) { + // 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: {} @@ -101,6 +117,7 @@ define([ }; var newEvent = function (event, cb) { var reminders = APP.notificationsEntries; + var eventBody = APP.eventBody || ""; var startDate = event.start._date; var endDate = event.end._date; @@ -117,8 +134,9 @@ define([ isAllDay: event.isAllDay, end: +endDate, reminders: reminders, - recurrenceRule: event.recurrenceRule, - timeZone: timeZone + body: eventBody, + timeZone: timeZone, + recurrenceRule: event.recurrenceRule }; APP.module.execCommand('CREATE_EVENT', data, function (obj) { @@ -288,6 +306,7 @@ define([ var obj = data.content[uid]; obj.title = obj.title || ""; obj.location = obj.location || ""; + obj.body = obj.body || ""; if (obj.isAllDay && obj.startDay) { obj.start = +DatePicker.parseDate((obj.startDay)); } if (obj.isAllDay && obj.endDay) { var endDate = DatePicker.parseDate(obj.endDay); @@ -393,6 +412,11 @@ define([ return Messages._getKey('calendar_location', [str]); }, + popupDetailBody: function(schedule) { + var str = schedule.body; + delete APP.eventBody; + return Messages._getKey('calendar_description', ['
', diffMk.render(str, true)]); + }, popupIsAllDay: function() { return Messages.calendar_allDay; }, titlePlaceholder: function() { return Messages.calendar_title; }, locationPlaceholder: function() { return Messages.calendar_loc; }, @@ -1028,6 +1052,11 @@ 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 eventBody = APP.eventBody || ""; + if (eventBody !== old.body) { + changes.body = eventBody; + } } @@ -1905,7 +1934,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; @@ -2013,6 +2041,50 @@ APP.recurrenceRule = { ]); }; + var getBodyInput = function() { + var ev = APP.editModalData; + 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.body; + if (!oldEventBody) { + oldEventBody = Util.find(APP.calendars, [calId, 'content', 'content', id, 'body']) || ""; + } + + APP.eventBody = oldEventBody; + var description = h('textarea.tui-full-calendar-content', { + placeholder: Messages.calendar_desc, + id: 'tui-full-calendar-body', + }); + + 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; + }; + + editor.on('changes', function() { + updateBody(editor.getValue()); + }); + + return block; + }; + var createToolbar = function () { var displayed = ['useradmin', 'newpad', 'limit', 'pageTitle', 'notifications']; var configTb = { @@ -2123,6 +2195,9 @@ APP.recurrenceRule = { var div = getNotificationDropdown(); $button.before(div); + 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'); var allDay = $cbox.is(':checked');