Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/mail.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteffen Lindner <mail@steffen-lindner.de>2016-05-10 18:46:50 +0300
committerSteffen Lindner <mail@steffen-lindner.de>2016-05-10 18:46:50 +0300
commit748fd49b053c6fe90ded9d10aa2b70b0d812ebf8 (patch)
treedf59df8f5944208fc0472153ed7cac1c0c3add73
parentca98142153ad3a2c5f00b03b3640f43b0c238d5f (diff)
parent1876bb928c8734bde66756994ea420fe4211e7e4 (diff)
Merge pull request #1473 from owncloud/import-ics-attachments
add ability to import ics attachments into the calendar
-rw-r--r--CHANGELOG.md4
-rw-r--r--README.md3
-rw-r--r--bower.json5
-rwxr-xr-xcss/mail.css15
-rw-r--r--js/app.js6
-rw-r--r--js/models/dav/calendar.js26
-rw-r--r--js/radio.js16
-rw-r--r--js/require_config.js11
-rw-r--r--js/service/attachmentservice.js16
-rw-r--r--js/service/davservice.js223
-rw-r--r--js/templates/calendar.html1
-rw-r--r--js/templates/message-attachment.html8
-rw-r--r--js/views/app.js4
-rw-r--r--js/views/calendarspopoverview.js30
-rw-r--r--js/views/calendarview.js31
-rw-r--r--js/views/messageattachment.js104
-rwxr-xr-xlib/controller/messagescontroller.php19
-rw-r--r--lib/controller/pagecontroller.php3
-rw-r--r--templates/index.php1
-rw-r--r--tests/controller/pagecontrollertest.php9
20 files changed, 510 insertions, 25 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c9ef98daf..729eddadf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file.
## 0.4.5 – UNRELEASED
+### Added
+- Ability to import ics attachments into the calendar
+ [#1473](https://github.com/owncloud/mail/pull/1473) @ChristophWurst
+
### Fixed
- Bring back menu toggle button for mobile
[#1483](https://github.com/owncloud/mail/pull/1483) @ChristophWurst
diff --git a/README.md b/README.md
index 39014f106..8d7e6b88b 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
## Why is this so awesome?
-* :rocket: **Integration with other ownCloud apps!** Currently Contacts & Files – more to come.
+* :rocket: **Integration with other ownCloud apps!** Currently Contacts, Calendar & Files – more to come.
* :inbox_tray: **Multiple mail accounts!** Personal and company account? No problem, and a nice unified inbox.
* :lock: **Send & receive encrypted emails!** Using the great [Mailvelope](https://mailvelope.com) browser extension.
* :see_no_evil: **We’re not reinventing the wheel!** Based on the great [Horde](http://horde.org) libraries.
@@ -23,7 +23,6 @@ And in the works for the [coming versions](https://github.com/owncloud/mail/mile
* :books: [Proper grouping of message threads](https://github.com/owncloud/mail/issues/21)
* :zap: [Caching to make everything faster](https://github.com/owncloud/mail/issues/480)
* :paperclip: [Even better attachment support](https://github.com/owncloud/mail/issues/462)
-* :date: [Calendar integration](https://github.com/owncloud/mail/issues/79)
* :package: [Folder management & moving mails](https://github.com/owncloud/mail/issues/411)
## Installation
diff --git a/bower.json b/bower.json
index 8e81edf0d..80d503cfa 100644
--- a/bower.json
+++ b/bower.json
@@ -25,6 +25,9 @@
"jquery-visibility": "~1.0.11",
"requirejs": "2.2.0",
"text": "^2.0.14",
- "underscore": "~1.8.3"
+ "underscore": "~1.8.3",
+ "davclient.js": "https://github.com/evert/davclient.js.git",
+ "es6-promise": "^3.2.1",
+ "ical.js": "^1.2.1"
}
}
diff --git a/css/mail.css b/css/mail.css
index f6aa0b746..898615f64 100755
--- a/css/mail.css
+++ b/css/mail.css
@@ -663,7 +663,8 @@ input.submit-message,
max-height: 120px;
}
.attachment-save-to-cloud,
-.attachment-download {
+.attachment-download,
+.attachment-import {
position: absolute;
height: 32px;
width: 32px;
@@ -675,6 +676,16 @@ input.submit-message,
.attachment-download {
right: 41px;
}
+.attachment-import {
+ right: 79px;
+}
+.attachment-import-popover {
+ right: 42px;
+ top: 56px;
+}
+.attachment-import-popover::after {
+ right: 32px;
+}
/* show icon + text for Download all button
as well as when there is only one attachment */
.attachments-save-to-cloud,
@@ -686,7 +697,7 @@ input.submit-message,
}
.attachment-name {
display: inline-block;
- width: calc(100% - 110px);
+ width: calc(100% - 148px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
diff --git a/js/app.js b/js/app.js
index 57cfb47dc..c2b1e1616 100644
--- a/js/app.js
+++ b/js/app.js
@@ -22,6 +22,9 @@
define(function(require) {
'use strict';
+ // Enable ES6 promise polyfill
+ require('es6-promise').polyfill();
+
var $ = require('jquery');
var Backbone = require('backbone');
var Handlebars = require('handlebars');
@@ -39,6 +42,7 @@ define(function(require) {
require('controller/messagecontroller');
require('service/accountservice');
require('service/attachmentservice');
+ require('service/davservice');
require('service/folderservice');
require('service/messageservice');
require('notification');
@@ -77,6 +81,8 @@ define(function(require) {
Mail = new Mail();
Mail.on('start', function() {
+ this.hasDavSupport = $('#has-dav-support').val() === '1';
+
this.view = new AppView();
Cache.init();
diff --git a/js/models/dav/calendar.js b/js/models/dav/calendar.js
new file mode 100644
index 000000000..e7f0b18bb
--- /dev/null
+++ b/js/models/dav/calendar.js
@@ -0,0 +1,26 @@
+/**
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * ownCloud - Mail
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+define(function(require) {
+ 'use strict';
+
+ var Backbone = require('backbone');
+
+ return Backbone.Model.extend({});
+}); \ No newline at end of file
diff --git a/js/radio.js b/js/radio.js
index f365b0ab8..306bcb009 100644
--- a/js/radio.js
+++ b/js/radio.js
@@ -13,22 +13,24 @@ define(function(require) {
var Radio = require('backbone.radio');
- var uiChannel = Radio.channel('ui');
- var notificationChannel = Radio.channel('notification');
- var stateChannel = Radio.channel('state');
var accountChannel = Radio.channel('account');
var folderChannel = Radio.channel('folder');
+ var davChannel = Radio.channel('dav');
var messageChannel = Radio.channel('message');
var navigationChannel = Radio.channel('navigation');
+ var notificationChannel = Radio.channel('notification');
+ var stateChannel = Radio.channel('state');
+ var uiChannel = Radio.channel('ui');
var channels = {
- ui: uiChannel,
- notification: notificationChannel,
- state: stateChannel,
account: accountChannel,
+ dav: davChannel,
folder: folderChannel,
message: messageChannel,
- navigation: navigationChannel
+ navigation: navigationChannel,
+ notification: notificationChannel,
+ state: stateChannel,
+ ui: uiChannel
};
// Log all events to the console
diff --git a/js/require_config.js b/js/require_config.js
index a990956f9..e9d3482b8 100644
--- a/js/require_config.js
+++ b/js/require_config.js
@@ -21,11 +21,22 @@
*/
backbone: 'vendor/backbone/backbone',
'backbone.radio': 'vendor/backbone.radio/build/backbone.radio',
+ davclient: 'vendor/davclient.js/lib/client',
domready: 'vendor/domReady/domReady',
+ 'es6-promise': 'vendor/es6-promise/es6-promise.min',
handlebars: 'vendor/handlebars/handlebars',
+ ical: 'vendor/ical.js/build/ical.min',
marionette: 'vendor/backbone.marionette/lib/backbone.marionette',
underscore: 'vendor/underscore/underscore',
text: 'vendor/text/text'
+ },
+ shim: {
+ davclient: {
+ exports: 'dav'
+ },
+ ical: {
+ exports: 'ICAL'
+ }
}
});
diff --git a/js/service/attachmentservice.js b/js/service/attachmentservice.js
index a231e9289..0d21bdfa3 100644
--- a/js/service/attachmentservice.js
+++ b/js/service/attachmentservice.js
@@ -25,6 +25,7 @@ define(function(require) {
var Radio = require('radio');
Radio.message.reply('save:cloud', saveToFiles);
+ Radio.message.reply('attachment:download', downloadAttachment);
/**
* @param {Account} account
@@ -63,4 +64,19 @@ define(function(require) {
return defer.promise();
}
+ function downloadAttachment(url) {
+ var defer = $.Deferred();
+
+ $.ajax(url, {
+ success: function(data) {
+ defer.resolve(data);
+ },
+ error: function() {
+ defer.reject();
+ }
+ });
+
+ return defer.promise();
+ }
+
});
diff --git a/js/service/davservice.js b/js/service/davservice.js
new file mode 100644
index 000000000..a73dfcfa2
--- /dev/null
+++ b/js/service/davservice.js
@@ -0,0 +1,223 @@
+/**
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * ownCloud - Mail
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+define(function(require) {
+ 'use strict';
+
+ var _ = require('underscore');
+ var $ = require('jquery');
+ var Backbone = require('backbone');
+ var dav = require('davclient');
+ var ical = require('ical');
+ var OC = require('OC');
+ var Radio = require('radio');
+ var Calendar = require('models/dav/calendar');
+
+ Radio.dav.reply('calendars', getUserCalendars);
+ Radio.dav.reply('calendar:import', importCalendarEvent);
+
+ var client = new dav.Client({
+ baseUrl: OC.linkToRemote('dav/calendars'),
+ xmlNamespaces: {
+ 'DAV:': 'd',
+ 'urn:ietf:params:xml:ns:caldav': 'c',
+ 'http://apple.com/ns/ical/': 'aapl',
+ 'http://owncloud.org/ns': 'oc',
+ 'http://calendarserver.org/ns/': 'cs'
+ }
+ });
+ var props = [
+ '{DAV:}displayname',
+ '{urn:ietf:params:xml:ns:caldav}calendar-description',
+ '{urn:ietf:params:xml:ns:caldav}calendar-timezone',
+ '{http://apple.com/ns/ical/}calendar-order',
+ '{http://apple.com/ns/ical/}calendar-color',
+ '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set',
+ '{http://owncloud.org/ns}calendar-enabled',
+ '{DAV:}acl',
+ '{DAV:}owner',
+ '{http://owncloud.org/ns}invite'
+ ];
+
+ function getResponseCodeFromHTTPResponse(t) {
+ return parseInt(t.split(' ')[1]);
+ }
+
+ function getACLFromResponse(properties) {
+ var canWrite = false;
+ var acl = properties['{DAV:}acl'];
+ if (acl) {
+ for (var k = 0; k < acl.length; k++) {
+ var href = acl[k].getElementsByTagNameNS('DAV:', 'href');
+ if (href.length === 0) {
+ continue;
+ }
+ href = href[0].textContent;
+ var writeNode = acl[k].getElementsByTagNameNS('DAV:', 'write');
+ if (writeNode.length > 0) {
+ canWrite = true;
+ }
+ }
+ }
+ properties.canWrite = canWrite;
+ }
+ ;
+
+ function getCalendarData(properties) {
+ getACLFromResponse(properties);
+
+ var data = {
+ displayname: properties['{DAV:}displayname'],
+ color: properties['{http://apple.com/ns/ical/}calendar-color'],
+ order: properties['{http://apple.com/ns/ical/}calendar-order'],
+ components: {
+ vevent: false
+ },
+ writable: properties.canWrite
+ };
+
+ var components = properties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'] || [];
+ for (var i = 0; i < components.length; i++) {
+ var name = components[i].attributes.getNamedItem('name').textContent.toLowerCase();
+ if (data.components.hasOwnProperty(name)) {
+ data.components[name] = true;
+ }
+ }
+
+ return data;
+ }
+
+ function getUserCalendars() {
+ var defer = $.Deferred();
+ var url = OC.linkToRemote('dav/calendars') + '/' + OC.currentUser + '/';
+
+ client.propFind(url, props, 1, {
+ 'requesttoken': OC.requestToken
+ }).then(function(data) {
+ var calendars = new Backbone.Collection();
+
+ _.each(data.body, function(cal) {
+ if (cal.propStat.length < 1) {
+ return;
+ }
+ if (getResponseCodeFromHTTPResponse(cal.propStat[0].status) === 200) {
+ var properties = getCalendarData(cal.propStat[0].properties);
+ if (properties && properties.components.vevent && properties.writable === true) {
+ properties.url = cal.href;
+ calendars.push(new Calendar(properties));
+ }
+ }
+ });
+ defer.resolve(calendars);
+ }, function() {
+ defer.reject();
+ });
+
+ return defer.promise();
+ }
+
+ function getRandomString() {
+ var str = '';
+ for (var i = 0; i < 7; i++) {
+ str += Math.random().toString(36).substring(7);
+ }
+ return str;
+ }
+
+ function createICalElement() {
+ var root = new ical.Component(['vcalendar', [], []]);
+
+ root.updatePropertyWithValue('prodid', '-//ownCloud Mail');
+
+ return root;
+ }
+
+ function splitCalendar(data) {
+ var timezones = [];
+ var allObjects = {};
+ var jCal = ical.parse(data);
+ var components = new ical.Component(jCal);
+
+ var vtimezones = components.getAllSubcomponents('vtimezone');
+ _.each(vtimezones, function(vtimezone) {
+ timezones.push(vtimezone);
+ });
+
+ var componentNames = ['vevent', 'vjournal', 'vtodo'];
+ _.each(componentNames, function(componentName) {
+ var vobjects = components.getAllSubcomponents(componentName);
+ allObjects[componentName] = {};
+
+ _.each(vobjects, function(vobject) {
+ var uid = vobject.getFirstPropertyValue('uid');
+ allObjects[componentName][uid] = allObjects[componentName][uid] || [];
+ allObjects[componentName][uid].push(vobject);
+ });
+ });
+
+ var split = [];
+ _.each(componentNames, function(componentName) {
+ split[componentName] = [];
+ _.each(allObjects[componentName], function(objects) {
+ var component = createICalElement();
+ _.each(timezones, function(timezone) {
+ component.addSubcomponent(timezone);
+ });
+ _.each(objects, function(object) {
+ component.addSubcomponent(object);
+ });
+ split[componentName].push(component.toString());
+ });
+ });
+
+ return {
+ name: components.getFirstPropertyValue('x-wr-calname'),
+ color: components.getFirstPropertyValue('x-apple-calendar-color'),
+ split: split
+ };
+ }
+
+ function importCalendarEvent(url, data) {
+ var defer = $.Deferred();
+ var xhrs = [];
+
+ var file = splitCalendar(data);
+
+ var componentNames = ['vevent', 'vjournal', 'vtodo'];
+ _.each(componentNames, function(componentName) {
+ _.each(file.split[componentName], function(component) {
+ xhrs.push($.ajax({
+ url: url + getRandomString(),
+ method: 'PUT',
+ contentType: 'text/calendar; charset=utf-8',
+ data: component,
+ error: function() {
+ defer.reject();
+ }
+ }));
+ });
+ });
+
+ $.when.apply($, xhrs).done(function() {
+ defer.resolve();
+ });
+
+ return defer.promise();
+ }
+});
diff --git a/js/templates/calendar.html b/js/templates/calendar.html
new file mode 100644
index 000000000..b10bf7698
--- /dev/null
+++ b/js/templates/calendar.html
@@ -0,0 +1 @@
+<a class="select-calendar" data-calendar-url="{{url}}">{{displayname}}</a> \ No newline at end of file
diff --git a/js/templates/message-attachment.html b/js/templates/message-attachment.html
index 9f902ca28..6171ebae9 100644
--- a/js/templates/message-attachment.html
+++ b/js/templates/message-attachment.html
@@ -4,5 +4,11 @@
{{/if}}
<img class="attachment-icon" src="{{mimeUrl}}" />
<span class="attachment-name" title="{{fileName}} ({{humanFileSize size}})">{{fileName}} <span class="attachment-size">({{humanFileSize size}})</span></span>
+{{#if isCalendarEvent}}
+{{#if hasDavSupport}}
+<button class="button icon-add attachment-import calendar" title="{{ t 'Import into calendar' }}"></button>
+{{/if}}
+{{/if}}
<button class="button icon-download attachment-download" title="{{ t 'Download attachment' }}"></button>
-<button class="icon-folder attachment-save-to-cloud" title="{{ t 'Save to Files' }}"></button> \ No newline at end of file
+<button class="icon-folder attachment-save-to-cloud" title="{{ t 'Save to Files' }}"></button>
+<div class="popovermenu bubble attachment-import-popover hidden"></div> \ No newline at end of file
diff --git a/js/views/app.js b/js/views/app.js
index 08a4ca699..cba1ee983 100644
--- a/js/views/app.js
+++ b/js/views/app.js
@@ -62,6 +62,10 @@ define(function(require) {
window.addEventListener('resize', this.onWindowResize);
+ $(document).on('click', function(e) {
+ Radio.ui.trigger('document:click', e);
+ });
+
// TODO: create marionette view and encapsulate events
$(document).on('click', '#forward-button', function() {
Radio.message.trigger('forward');
diff --git a/js/views/calendarspopoverview.js b/js/views/calendarspopoverview.js
new file mode 100644
index 000000000..f2693cbb2
--- /dev/null
+++ b/js/views/calendarspopoverview.js
@@ -0,0 +1,30 @@
+/**
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * ownCloud - Mail
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+define(function(require) {
+ 'use strict';
+
+ var Marionette = require('marionette');
+ var CalendarView = require('views/calendarview');
+
+ return Marionette.CollectionView.extend({
+ childView: CalendarView,
+ tagName: 'ul'
+ });
+});
diff --git a/js/views/calendarview.js b/js/views/calendarview.js
new file mode 100644
index 000000000..9b2e3ffee
--- /dev/null
+++ b/js/views/calendarview.js
@@ -0,0 +1,31 @@
+/**
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * ownCloud - Mail
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+define(function(require) {
+ 'use strict';
+
+ var Marionette = require('marionette');
+ var Handlebars = require('handlebars');
+ var CalendarTemplate = require('text!templates/calendar.html');
+
+ return Marionette.ItemView.extend({
+ template: Handlebars.compile(CalendarTemplate),
+ tagName: 'li'
+ });
+});
diff --git a/js/views/messageattachment.js b/js/views/messageattachment.js
index 7746b02d0..33e12465f 100644
--- a/js/views/messageattachment.js
+++ b/js/views/messageattachment.js
@@ -23,7 +23,9 @@ define(function(require) {
var $ = require('jquery');
var Handlebars = require('handlebars');
var Marionette = require('marionette');
+ var Radio = require('radio');
var MessageController = require('controller/messagecontroller');
+ var CalendarsPopoverView = require('views/calendarspopoverview');
var MessageAttachmentTemplate = require('text!templates/message-attachment.html');
/**
@@ -33,14 +35,32 @@ define(function(require) {
template: Handlebars.compile(MessageAttachmentTemplate),
ui: {
'downloadButton': '.attachment-download',
- 'saveToCloudButton': '.attachment-save-to-cloud'
+ 'saveToCloudButton': '.attachment-save-to-cloud',
+ 'importCalendarEventButton': '.attachment-import.calendar',
+ 'attachmentImportPopover': '.attachment-import-popover'
},
events: {
- 'click': '_onDownload',
- 'click @ui.saveToCloudButton': '_onSaveToCloud'
+ 'click': '_onClick',
+ 'click @ui.saveToCloudButton': '_onSaveToCloud',
+ 'click @ui.importCalendarEventButton': '_onImportCalendarEvent'
},
- _onDownload: function(e) {
+ templateHelpers: function() {
+ return {
+ hasDavSupport: require('app').hasDavSupport
+ };
+ },
+ initialize: function() {
+ this.listenTo(Radio.ui, 'document:click', this._closeImportPopover);
+ },
+ _onClick: function(e) {
if (!e.isDefaultPrevented()) {
+ var $target = $(e.target);
+ if ($target.hasClass('select-calendar')) {
+ var url = $target.data('calendar-url');
+ this._uploadToCalendar(url);
+ return;
+ }
+
e.preventDefault();
window.location = this.model.get('downloadUrl');
}
@@ -57,16 +77,84 @@ define(function(require) {
// Loading feedback
this.ui.saveToCloudButton.removeClass('icon-folder')
- .addClass('icon-loading-small')
- .prop('disabled', true);
+ .addClass('icon-loading-small')
+ .prop('disabled', true);
var _this = this;
$.when(saving).always(function() {
// Remove loading feedback again
_this.ui.saveToCloudButton.addClass('icon-folder')
- .removeClass('icon-loading-small')
- .prop('disabled', false);
+ .removeClass('icon-loading-small')
+ .prop('disabled', false);
+ });
+ },
+ _onImportCalendarEvent: function(e) {
+ e.preventDefault();
+
+ this.ui.importCalendarEventButton
+ .removeClass('icon-add')
+ .addClass('icon-loading-small');
+
+ var fetchingCalendars = Radio.dav.request('calendars');
+
+ var _this = this;
+ $.when(fetchingCalendars).done(function(calendars) {
+ if (calendars.length > 0) {
+ _this.ui.attachmentImportPopover.removeClass('hidden');
+ var calendarsView = new CalendarsPopoverView({
+ collection: calendars
+ });
+ calendarsView.render();
+ _this.ui.attachmentImportPopover.html(calendarsView.$el);
+ } else {
+ Radio.ui.trigger('error:show', t('mail', 'No writable calendars found'));
+ }
+ });
+ $.when(fetchingCalendars).always(function() {
+ _this.ui.importCalendarEventButton
+ .removeClass('icon-loading-small')
+ .addClass('icon-add');
});
+ },
+ _uploadToCalendar: function(url) {
+ this._closeImportPopover();
+ this.ui.importCalendarEventButton
+ .removeClass('icon-add')
+ .addClass('icon-loading-small');
+
+ var downloadUrl = this.model.get('downloadUrl');
+ var downloadingAttachment = Radio.message.request('attachment:download', downloadUrl);
+
+ var _this = this;
+ $.when(downloadingAttachment).done(function(content) {
+
+ var importingCalendarEvent = Radio.dav.request('calendar:import', url, content);
+
+ $.when(importingCalendarEvent).fail(function() {
+ Radio.ui.trigger('error:show', t('mail', 'Error while importing the calendar event'));
+ });
+ $.when(importingCalendarEvent).always(function() {
+ _this.ui.importCalendarEventButton
+ .removeClass('icon-loading-small')
+ .addClass('icon-add');
+ });
+ });
+ $.when(downloadingAttachment.fail(function() {
+ Radio.ui.trigger('error:show', t('mail', 'Error while downloading calendar event'));
+ _this.ui.importCalendarEventButton
+ .removeClass('icon-loading-small')
+ .addClass('icon-add');
+ }));
+ },
+ _closeImportPopover: function(e) {
+ if (_.isUndefined(e)) {
+ this.ui.attachmentImportPopover.addClass('hidden');
+ return;
+ }
+ var $target = $(e.target);
+ if (this.$el.find($target).length === 0) {
+ this.ui.attachmentImportPopover.addClass('hidden');
+ }
}
});
diff --git a/lib/controller/messagescontroller.php b/lib/controller/messagescontroller.php
index 1b3ddb168..6bc689b15 100755
--- a/lib/controller/messagescontroller.php
+++ b/lib/controller/messagescontroller.php
@@ -397,6 +397,8 @@ class MessagesController extends Controller {
if ($this->attachmentIsImage($attachment)) {
$attachment['isImage'] = true;
+ } else if ($this->attachmentIsCalendarEvent($attachment)) {
+ $attachment['isCalendarEvent'] = true;
}
return $attachment;
}
@@ -405,11 +407,24 @@ class MessagesController extends Controller {
* @param $attachment
*
* Determines if the content of this attachment is an image
+ *
+ * @return boolean
*/
private function attachmentIsImage($attachment) {
- return in_array($attachment['mime'], array('image/jpeg',
+ return in_array(
+ $attachment['mime'], [
+ 'image/jpeg',
'image/png',
- 'image/gif'));
+ 'image/gif'
+ ]);
+ }
+
+ /**
+ * @param type $attachment
+ * @return boolean
+ */
+ private function attachmentIsCalendarEvent($attachment) {
+ return $attachment['mime'] === 'text/calendar';
}
/**
diff --git a/lib/controller/pagecontroller.php b/lib/controller/pagecontroller.php
index 0b5067184..d1081d700 100644
--- a/lib/controller/pagecontroller.php
+++ b/lib/controller/pagecontroller.php
@@ -77,11 +77,14 @@ class PageController extends Controller {
* @return TemplateResponse renders the index page
*/
public function index() {
+ $coreVersion = $this->config->getSystemValue('version', '0.0.0');
+ $hasDavSupport = (int) version_compare($coreVersion, '9.0.0', '>=');
// TODO: remove DEBUG constant check once minimum oc
// core version >= 8.2, see https://github.com/owncloud/core/pull/18510
$response = new TemplateResponse($this->appName, 'index', [
'debug' => (defined('DEBUG') && DEBUG) || $this->config->getSystemValue('debug', false),
'app-version' => $this->config->getAppValue('mail', 'installed_version'),
+ 'has-dav-support' => $hasDavSupport,
]);
// set csp rules for ownCloud 8.1
diff --git a/templates/index.php b/templates/index.php
index ac0b2ed73..4c122ebe1 100644
--- a/templates/index.php
+++ b/templates/index.php
@@ -40,6 +40,7 @@ if ($_['debug']) {
?>
<input type="hidden" id="config-installed-version" value="<?php p($_['app-version']); ?>">
+<input type="hidden" id="has-dav-support" value="<?php p($_['has-dav-support']); ?>">
<div id="user-displayname"
style="display: none"><?php p(\OCP\User::getDisplayName(\OCP\User::getUser())); ?></div>
diff --git a/tests/controller/pagecontrollertest.php b/tests/controller/pagecontrollertest.php
index 01fa3e1b0..21a2bf7a2 100644
--- a/tests/controller/pagecontrollertest.php
+++ b/tests/controller/pagecontrollertest.php
@@ -48,7 +48,11 @@ class PageControllerTest extends TestCase {
}
public function testIndex() {
- $this->config->expects($this->once())
+ $this->config->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('version', '0.0.0')
+ ->will($this->returnValue('8.2.0'));
+ $this->config->expects($this->at(1))
->method('getSystemValue')
->with('debug', false)
->will($this->returnValue(true));
@@ -59,7 +63,8 @@ class PageControllerTest extends TestCase {
$expected = new TemplateResponse($this->appName, 'index', [
'debug' => true,
- 'app-version' => '1.2.3'
+ 'app-version' => '1.2.3',
+ 'has-dav-support' => 0,
]);
// set csp rules for ownCloud 8.1
if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) {