diff --git a/www/calendar/export.js b/www/calendar/export.js new file mode 100644 index 000000000..0d5921e73 --- /dev/null +++ b/www/calendar/export.js @@ -0,0 +1,121 @@ +// This file is used when a user tries to export the entire CryptDrive. +// Calendars will be exported using this format instead of plain text. +define([ + '/customize/pages.js', +], function (Pages) { + var module = {}; + + var getICSDate = function (str) { + var date = new Date(str); + + var m = date.getUTCMonth() + 1; + var d = date.getUTCDate(); + var h = date.getUTCHours(); + var min = date.getUTCMinutes(); + + var year = date.getUTCFullYear().toString(); + var month = m < 10 ? "0" + m : m.toString(); + var day = d < 10 ? "0" + d : d.toString(); + var hours = h < 10 ? "0" + h : h.toString(); + var minutes = min < 10 ? "0" + min : min.toString(); + + return year + month + day + "T" + hours + minutes + "00Z"; + } + + + var getDate = function (str, end) { + var date = new Date(str); + if (end) { + date.setDate(date.getDate() + 1); + } + var m = date.getUTCMonth() + 1; + var d = date.getUTCDate(); + + var year = date.getUTCFullYear().toString(); + var month = m < 10 ? "0" + m : m.toString(); + var day = d < 10 ? "0" + d : d.toString(); + + return year+month+day; + }; + + var MINUTE = 60; + var HOUR = MINUTE * 60; + var DAY = HOUR * 24; + + + module.main = function (userDoc) { + var content = userDoc.content; + var md = userDoc.metadata; + + var ICS = [ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//CryptPad//CryptPad Calendar '+Pages.versionString+'//EN', + 'METHOD:PUBLISH', + ]; + + Object.keys(content).forEach(function (uid) { + var data = content[uid]; + // DTSTAMP: now... + // UID: uid + var start, end; + if (data.isAllDay && data.startDay && data.endDay) { + start = "DTSTART;VALUE=DATE:" + getDate(data.startDay); + end = "DTEND;VALUE=DATE:" + getDate(data.endDay, true); + } else { + start = "DTSTART:"+getICSDate(data.start); + end = "DTEND:"+getICSDate(data.end); + } + + Array.prototype.push.apply(ICS, [ + 'BEGIN:VEVENT', + 'DTSTAMP:'+getICSDate(+new Date()), + 'UID:'+uid, + start, + end, + 'SUMMARY:'+ data.title, + 'LOCATION:'+ data.location, + ]); + + if (Array.isArray(data.reminders)) { + data.reminders.forEach(function (valueMin) { + var time = valueMin * 60; + var days = Math.floor(time / DAY); + time -= days * DAY; + var hours = Math.floor(time / HOUR); + time -= hours * HOUR; + var minutes = Math.floor(time / MINUTE); + time -= minutes * MINUTE; + var seconds = time; + + var str = "-P" + days + "D"; + if (hours || minutes || seconds) { + str += "T" + hours + "H" + minutes + "M" + seconds + "S"; + } + Array.prototype.push.apply(ICS, [ + 'BEGIN:VALARM', + 'ACTION:DISPLAY', + 'DESCRIPTION:This is an event reminder', + 'TRIGGER:'+str, + 'END:VALARM' + ]); + // XXX ACTION:EMAIL + // XXX ATTENDEE:mailto:xxx@xxx.xxx + // XXX SUMMARY:Alarm notification + }); + } + + // XXX add hidden data (from imports) + + ICS.push('END:VEVENT'); + }); + + ICS.push('END:VCALENDAR'); + + return new Blob([ ICS.join('\n') ], { type: 'text/calendar;charset=utf-8' }); + }; + + return module; +}); + + diff --git a/www/calendar/inner.js b/www/calendar/inner.js index f74f3cfaf..9986206f0 100644 --- a/www/calendar/inner.js +++ b/www/calendar/inner.js @@ -15,12 +15,14 @@ define([ '/customize/messages.js', '/customize/application_config.js', '/lib/calendar/tui-calendar.min.js', + '/calendar/export.js', '/common/inner/share.js', '/common/inner/access.js', '/common/inner/properties.js', '/common/jscolor.js', + '/bower_components/file-saver/FileSaver.min.js', 'css!/lib/calendar/tui-calendar.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'less!/calendar/app-calendar.less', @@ -41,9 +43,11 @@ define([ Messages, AppConfig, Calendar, + Export, Share, Access, Properties ) { + var SaveAs = window.saveAs; var APP = window.APP = { calendars: {} }; @@ -453,6 +457,65 @@ Messages.calendar_allDay = "All day"; return true; } }); + + if (!data.readOnly) { + options.push({ + tag: 'a', + attributes: { + 'class': 'fa fa-upload', + }, + content: h('span', Messages.importButton), + action: function (e) { + e.stopPropagation(); + return true; + } + }); + } + options.push({ + tag: 'a', + attributes: { + 'class': 'fa fa-download', + }, + content: h('span', Messages.exportButton), + action: function (e) { + e.stopPropagation(); + var cal = APP.calendars[id]; + var suggestion = Util.find(cal, ['content', 'metadata', 'title']); + var types = []; + types.push({ + tag: 'a', + attributes: { + 'data-value': '.ics', + 'href': '#' + }, + content: '.ics' + }); + var dropdownConfig = { + text: '.ics', // Button initial text + caretDown: true, + options: types, // Entries displayed in the menu + isSelect: true, + initialValue: '.ics', + common: common + }; + var $select = UIElements.createDropdown(dropdownConfig); + UI.prompt(Messages.exportPrompt, + Util.fixFileName(suggestion), function (filename) + { + if (!(typeof(filename) === 'string' && filename)) { return; } + var ext = $select.getValue(); + filename = filename + ext; + var blob = Export.main(cal.content); + SaveAs(blob, filename); + }, { + typeInput: $select[0] + }); + return true; + } + }); + + + options.push({ tag: 'a', attributes: {