diff options
author | Steffen Lindner <mail@steffen-lindner.de> | 2016-05-10 18:46:50 +0300 |
---|---|---|
committer | Steffen Lindner <mail@steffen-lindner.de> | 2016-05-10 18:46:50 +0300 |
commit | 748fd49b053c6fe90ded9d10aa2b70b0d812ebf8 (patch) | |
tree | df59df8f5944208fc0472153ed7cac1c0c3add73 | |
parent | ca98142153ad3a2c5f00b03b3640f43b0c238d5f (diff) | |
parent | 1876bb928c8734bde66756994ea420fe4211e7e4 (diff) |
Merge pull request #1473 from owncloud/import-ics-attachments
add ability to import ics attachments into the calendar
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | bower.json | 5 | ||||
-rwxr-xr-x | css/mail.css | 15 | ||||
-rw-r--r-- | js/app.js | 6 | ||||
-rw-r--r-- | js/models/dav/calendar.js | 26 | ||||
-rw-r--r-- | js/radio.js | 16 | ||||
-rw-r--r-- | js/require_config.js | 11 | ||||
-rw-r--r-- | js/service/attachmentservice.js | 16 | ||||
-rw-r--r-- | js/service/davservice.js | 223 | ||||
-rw-r--r-- | js/templates/calendar.html | 1 | ||||
-rw-r--r-- | js/templates/message-attachment.html | 8 | ||||
-rw-r--r-- | js/views/app.js | 4 | ||||
-rw-r--r-- | js/views/calendarspopoverview.js | 30 | ||||
-rw-r--r-- | js/views/calendarview.js | 31 | ||||
-rw-r--r-- | js/views/messageattachment.js | 104 | ||||
-rwxr-xr-x | lib/controller/messagescontroller.php | 19 | ||||
-rw-r--r-- | lib/controller/pagecontroller.php | 3 | ||||
-rw-r--r-- | templates/index.php | 1 | ||||
-rw-r--r-- | tests/controller/pagecontrollertest.php | 9 |
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 @@ -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; @@ -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')) { |