summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/Controller/ContactsMenuController.php61
-rw-r--r--core/css/header.scss34
-rw-r--r--core/css/icons.scss4
-rw-r--r--core/css/styles.scss115
-rw-r--r--core/img/places/contacts.svg1
-rw-r--r--core/js/contactsmenu.js515
-rw-r--r--core/js/core.json1
-rw-r--r--core/js/js.js17
-rw-r--r--core/js/tests/specs/contactsmenuSpec.js255
-rw-r--r--core/routes.php1
-rw-r--r--core/templates/layout.user.php5
-rw-r--r--lib/composer/composer/ClassLoader.php10
-rw-r--r--lib/composer/composer/LICENSE2
-rw-r--r--lib/composer/composer/autoload_classmap.php14
-rw-r--r--lib/composer/composer/autoload_static.php14
-rw-r--r--lib/private/Contacts/ContactsMenu/ActionFactory.php57
-rw-r--r--lib/private/Contacts/ContactsMenu/ActionProviderStore.php82
-rw-r--r--lib/private/Contacts/ContactsMenu/Actions/LinkAction.php103
-rw-r--r--lib/private/Contacts/ContactsMenu/ContactsStore.php89
-rw-r--r--lib/private/Contacts/ContactsMenu/Entry.php169
-rw-r--r--lib/private/Contacts/ContactsMenu/Manager.php98
-rw-r--r--lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php56
-rw-r--r--lib/private/Server.php11
-rw-r--r--lib/private/legacy/template.php1
-rw-r--r--lib/public/Contacts/ContactsMenu/IAction.php65
-rw-r--r--lib/public/Contacts/ContactsMenu/IActionFactory.php54
-rw-r--r--lib/public/Contacts/ContactsMenu/IEntry.php66
-rw-r--r--lib/public/Contacts/ContactsMenu/ILinkAction.php43
-rw-r--r--lib/public/Contacts/ContactsMenu/IProvider.php37
-rw-r--r--tests/Core/Controller/ContactsMenuControllerTest.php73
-rw-r--r--tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php67
-rw-r--r--tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php82
-rw-r--r--tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php90
-rw-r--r--tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php121
-rw-r--r--tests/lib/Contacts/ContactsMenu/EntryTest.php113
-rw-r--r--tests/lib/Contacts/ContactsMenu/ManagerTest.php100
-rw-r--r--tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php69
37 files changed, 2666 insertions, 29 deletions
diff --git a/core/Controller/ContactsMenuController.php b/core/Controller/ContactsMenuController.php
new file mode 100644
index 00000000000..edd2f2c8be8
--- /dev/null
+++ b/core/Controller/ContactsMenuController.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Core\Controller;
+
+use OC\Contacts\ContactsMenu\Manager;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\IRequest;
+
+class ContactsMenuController extends Controller {
+
+ /** @var Manager */
+ private $manager;
+
+ /** @var string */
+ private $userId;
+
+ /**
+ * @param IRequest $request
+ * @param string $UserId
+ * @param Manager $manager
+ */
+ public function __construct(IRequest $request, $UserId, Manager $manager) {
+ parent::__construct('core', $request);
+ $this->userId = $UserId;
+ $this->manager = $manager;
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * @param string|null filter
+ * @return JSONResponse
+ */
+ public function index($filter = null) {
+ return $this->manager->getEntries($this->userId, $filter);
+ }
+
+}
diff --git a/core/css/header.scss b/core/css/header.scss
index 619852faf60..50d270a6ff9 100644
--- a/core/css/header.scss
+++ b/core/css/header.scss
@@ -20,10 +20,21 @@
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
+}
- /* Dropdown menu arrow */
- &.menu:after,
- .menu:after {
+/* Header menu */
+.menu {
+ position: absolute;
+ top: 45px;
+ background-color: #fff;
+ box-shadow: 0 1px 10px rgba(150, 150, 150, 0.75);
+ border-radius: 0 0 3px 3px;
+ display: none;
+ box-sizing: border-box;
+ z-index: 2000;
+
+ /* Dropdown arrow */
+ &:after {
border: 10px solid transparent;
border-bottom-color: $color-main-background;
bottom: 100%;
@@ -199,19 +210,12 @@ nav {
#navigation {
position: relative;
- top: 45px;
left: -100%;
width: 160px;
- margin-top: 0;
background-color: $color-main-background;
box-shadow: 0 1px 10px $color-box-shadow;
- border-radius: 3px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- display: none;
- box-sizing: border-box;
- z-index: 2000;
&:after {
+ /* position of dropdown arrow */
left: 47%;
bottom: 100%;
border: solid transparent;
@@ -407,17 +411,9 @@ nav {
}
#expanddiv {
- position: absolute;
right: 13px;
- top: 45px;
- z-index: 2000;
- display: none;
background: $color-main-background;
box-shadow: 0 1px 10px $color-box-shadow;
- border-radius: 3px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- box-sizing: border-box;
&:after {
/* position of dropdown arrow */
right: 13px;
diff --git a/core/css/icons.scss b/core/css/icons.scss
index 1ca29f22600..f9b73f51923 100644
--- a/core/css/icons.scss
+++ b/core/css/icons.scss
@@ -438,6 +438,10 @@ img, object, video, button, textarea, input, select {
background-image: url('../img/places/calendar-dark.svg?v=1');
}
+.icon-contacts {
+ background-image: url('../img/places/contacts.svg?v=1');
+}
+
.icon-contacts-dark {
background-image: url('../img/places/contacts-dark.svg?v=1');
}
diff --git a/core/css/styles.scss b/core/css/styles.scss
index a6970336c12..ed11fd52158 100644
--- a/core/css/styles.scss
+++ b/core/css/styles.scss
@@ -1057,6 +1057,121 @@ span.ui-icon {
margin: 3px 7px 30px 0;
}
+/* ---- CONTACTS MENU ---- */
+
+#contactsmenu {
+ .menutoggle {
+ background-size: 16px 16px;
+ padding: 14px;
+ cursor: pointer;
+ opacity: .7;
+ }
+}
+
+#contactsmenu > .menu {
+ /* show ~4.5 entries */
+ height: 278px;
+ width: 350px;
+ right: 13px;
+
+ &::after {
+ right: 61px;
+ }
+
+ .emptycontent {
+ margin-top: 5vh !important;
+ margin-bottom: 2vh;
+ .icon-loading {
+ position: inherit;
+ }
+ .icon-search {
+ display: inline-block;
+ }
+ }
+
+ .content {
+ max-height: calc(100% - 50px);
+ overflow-y: auto;
+
+ .footer {
+ text-align: center;
+
+ a {
+ display: block;
+ width: 100%;
+ padding: 12px 0;
+ opacity: .5;
+ }
+ }
+ }
+
+ .contact {
+ display: flex;
+ position: relative;
+ align-items: center;
+ padding: 3px 3px 3px 10px;
+ border-bottom: 1px solid #eeeeee;
+
+ :last-of-type {
+ border-bottom: none;
+ }
+
+ .avatar {
+ height: 32px;
+ width: 32px;
+ display: inline-block;
+ }
+
+ .body {
+ flex-grow: 1;
+ padding-left: 8px;
+
+ div {
+ position: relative;
+ width: 100%;
+ }
+
+ .full-name, .last-message {
+ /* TODO: don't use fixed width */
+ max-width: 204px;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+ .last-message {
+ opacity: .5;
+ }
+ }
+
+ .top-action, .second-action, .other-actions {
+ width: 16px;
+ height: 16px;
+ padding: 14px;
+ opacity: .5;
+ cursor: pointer;
+
+ :hover {
+ opacity: 1;
+ }
+ }
+
+ /* actions menu */
+ .menu {
+ top: 47px;
+ margin-right: 13px;
+ }
+ .popovermenu::after {
+ right: 2px;
+ }
+ }
+}
+
+
+#contactsmenu-search {
+ width: calc(100% - 16px);
+ margin: 8px;
+}
+
/* ---- TOOLTIPS ---- */
.extra-data {
diff --git a/core/img/places/contacts.svg b/core/img/places/contacts.svg
new file mode 100644
index 00000000000..fb6a60c0844
--- /dev/null
+++ b/core/img/places/contacts.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" viewbox="0 0 32 32"><path style="block-progression:tb;text-transform:none;text-indent:0" d="M9.24 6.67c-1.955 0-3.613 1.43-3.613 3.275.014.583.066 1.302.414 2.823v.037l.038.038c.112.32.275.503.49.753.215.25.47.544.715.79l.075.076c.048.21.107.436.15.64.117.54.105.922.076 1.053-.84.295-1.885.647-2.823 1.054-.526.228-1.002.433-1.392.677-.39.244-.777.43-.903.978a.473.473 0 0 0 0 .076c-.123 1.13-.31 2.793-.452 3.915a.618.618 0 0 0 .3.603c1.704.92 4.32 1.29 6.927 1.28 2.607-.01 5.202-.403 6.85-1.28a.618.618 0 0 0 .3-.603c-.044-.35-.1-1.14-.15-1.92-.05-.778-.09-1.543-.15-1.994a.607.607 0 0 0-.15-.3c-.524-.626-1.306-1.008-2.22-1.393-.836-.352-1.815-.717-2.786-1.13-.055-.12-.11-.473 0-1.016.03-.144.074-.3.113-.45l.263-.3c.216-.248.447-.506.64-.754.192-.25.35-.462.452-.753l.037-.038c.393-1.588.393-2.25.413-2.823v-.037c0-1.845-1.658-3.275-3.613-3.275zm10.336-3.005c-2.85 0-5.268 2.084-5.268 4.774.02.85.096 1.898.604 4.115v.055l.055.055c.162.466.4.733.713 1.097s.687.793 1.043 1.153c.04.042.068.068.11.11.07.306.155.636.22.932.168.788.15 1.346.11 1.537-1.226.43-2.75.942-4.117 1.536-.768.334-1.462.632-2.03.988-.57.356-1.134.625-1.317 1.427a.67.67 0 0 0 0 .11c-.18 1.648-.452 4.07-.66 5.707a.9.9 0 0 0 .44.878c2.48 1.34 6.295 1.88 10.096 1.865s7.584-.586 9.987-1.865a.9.9 0 0 0 .44-.878c-.067-.512-.148-1.665-.22-2.8-.072-1.133-.134-2.25-.22-2.907a.884.884 0 0 0-.22-.44c-.763-.91-1.903-1.468-3.237-2.03-1.217-.513-2.645-1.045-4.06-1.646-.08-.177-.16-.69 0-1.483.042-.212.108-.44.164-.658.133-.15.237-.272.384-.44.315-.36.652-.735.933-1.098.28-.362.51-.673.66-1.097l.053-.055c.574-2.315.574-3.28.604-4.116V8.44c0-2.69-2.418-4.775-5.268-4.775z" fill="#fff"/></svg>
diff --git a/core/js/contactsmenu.js b/core/js/contactsmenu.js
new file mode 100644
index 00000000000..2991a0e84d1
--- /dev/null
+++ b/core/js/contactsmenu.js
@@ -0,0 +1,515 @@
+/* global OC.Backbone, Handlebars, Promise, _ */
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+(function(OC, $, _, Handlebars) {
+ 'use strict';
+
+ var MENU_TEMPLATE = ''
+ + '<input id="contactsmenu-search" type="search" placeholder="Search contacts …" value="{{searchTerm}}">'
+ + '<div class="content">'
+ + '</div>';
+ var CONTACTS_LIST_TEMPLATE = ''
+ + '{{#unless contacts.length}}'
+ + '<div class="emptycontent">'
+ + ' <a class="icon-search"></a>'
+ + ' <h2>' + t('core', 'No contacts found') + '</h2>'
+ + '</div>'
+ + '{{/unless}}'
+ + '<div id="contactsmenu-contacts"></div>'
+ + '{{#if contactsAppEnabled}}<div class="footer"><a href="{{contactsAppURL}}">' + t('core', 'Show all contacts …') + '</a></div>{{/if}}';
+ var LOADING_TEMPLATE = ''
+ + '<div class="emptycontent">'
+ + ' <a class="icon-loading"></a>'
+ + ' <h2>{{loadingText}}</h2>'
+ + '</div>';
+ var ERROR_TEMPLATE = ''
+ + '<div class="emptycontent">'
+ + ' <h2>' + t('core', 'Could not load your contacts.') + '</h2>'
+ + '</div>';
+ var CONTACT_TEMPLATE = ''
+ + '{{#if contact.avatar}}'
+ + '<img src="{{contact.avatar}}" class="avatar">'
+ + '{{else}}'
+ + '<div class="avatar"></div>'
+ + '{{/if}}'
+ + '<div class="body">'
+ + ' <div class="full-name">{{contact.fullName}}</div>'
+ + ' <div class="last-message">{{contact.lastMessage}}</div>'
+ + '</div>'
+ + '{{#if contact.topAction}}'
+ + '<a class="top-action {{contact.topAction.icon}}" href="{{contact.topAction.hyperlink}}"></a>'
+ + '{{/if}}'
+ + '{{#if contact.hasManyActions}}'
+ + ' <span class="other-actions icon-more"></span>'
+ + ' <div class="menu popovermenu">'
+ + ' <ul>'
+ + ' {{#each contact.actions}}'
+ + ' <li>'
+ + ' <a href="{{hyperlink}}">'
+ + ' <span class="{{icon}}"></span>'
+ + ' <span>{{title}}</span>'
+ + ' </a>'
+ + ' </li>'
+ + ' {{/each}}'
+ + ' </ul>'
+ + ' </div>'
+ + '{{/if}}'
+ + '{{#if contact.hasTwoActions}}'
+ + '<a class="second-action {{contact.secondAction.icon}}" href="{{contact.secondAction.hyperlink}}"></a>'
+ + '{{/if}}';
+
+ /**
+ * @class Contact
+ */
+ var Contact = OC.Backbone.Model.extend({
+ defaults: {
+ fullName: '',
+ lastMessage: '',
+ actions: [],
+ hasOneAction: false,
+ hasTwoActions: false,
+ hasManyActions: false
+ },
+
+ /**
+ * @returns {undefined}
+ */
+ initialize: function() {
+ // Add needed property for easier template rendering
+ if (this.get('actions').length === 0) {
+ this.set('hasOneAction', true);
+ } else if (this.get('actions').length === 1) {
+ this.set('hasTwoActions', true);
+ this.set('secondAction', this.get('actions')[0]);
+ } else {
+ this.set('hasManyActions', true);
+ }
+ }
+ });
+
+ /**
+ * @class ContactCollection
+ */
+ var ContactCollection = OC.Backbone.Collection.extend({
+ model: Contact
+ });
+
+ /**
+ * @class ContactsListView
+ */
+ var ContactsListView = OC.Backbone.View.extend({
+
+ /** @type {ContactsCollection} */
+ _collection: undefined,
+
+ /** @type {array} */
+ _subViews: [],
+
+ /**
+ * @param {object} options
+ * @returns {undefined}
+ */
+ initialize: function(options) {
+ this._collection = options.collection;
+ },
+
+ /**
+ * @returns {self}
+ */
+ render: function() {
+ var self = this;
+ self.$el.html('');
+ self._subViews = [];
+
+ self._collection.forEach(function(contact) {
+ var item = new ContactsListItemView({
+ model: contact
+ });
+ item.render();
+ self.$el.append(item.$el);
+ item.on('toggle:actionmenu', self._onChildActionMenuToggle, self);
+ self._subViews.push(item);
+ });
+
+ return self;
+ },
+
+ /**
+ * Event callback to propagate opening (another) entry's action menu
+ *
+ * @param {type} $src
+ * @returns {undefined}
+ */
+ _onChildActionMenuToggle: function($src) {
+ this._subViews.forEach(function(view) {
+ view.trigger('parent:toggle:actionmenu', $src);
+ });
+ }
+ });
+
+ /**
+ * @class CotnactsListItemView
+ */
+ var ContactsListItemView = OC.Backbone.View.extend({
+
+ /** @type {string} */
+ className: 'contact',
+
+ /** @type {undefined|function} */
+ _template: undefined,
+
+ /** @type {Contact} */
+ _model: undefined,
+
+ /** @type {boolean} */
+ _actionMenuShown: false,
+
+ events: {
+ 'click .icon-more': '_onToggleActionsMenu'
+ },
+
+ /**
+ * @param {object} data
+ * @returns {undefined}
+ */
+ template: function(data) {
+ if (!this._template) {
+ this._template = Handlebars.compile(CONTACT_TEMPLATE);
+ }
+ return this._template(data);
+ },
+
+ /**
+ * @param {object} options
+ * @returns {undefined}
+ */
+ initialize: function(options) {
+ this._model = options.model;
+ this.on('parent:toggle:actionmenu', this._onOtherActionMenuOpened, this);
+ },
+
+ /**
+ * @returns {self}
+ */
+ render: function() {
+ this.$el.html(this.template({
+ contact: this._model.toJSON()
+ }));
+ this.delegateEvents();
+
+ // Show placeholder iff no avatar is available (avatar is rendered as img, not div)
+ this.$('div.avatar').imageplaceholder(this._model.get('fullName'));
+
+ return this;
+ },
+
+ /**
+ * Toggle the visibility of the action popover menu
+ *
+ * @private
+ * @returns {undefined}
+ */
+ _onToggleActionsMenu: function() {
+ this._actionMenuShown = !this._actionMenuShown;
+ if (this._actionMenuShown) {
+ this.$('.menu').show();
+ } else {
+ this.$('.menu').hide();
+ }
+ this.trigger('toggle:actionmenu', this.$el);
+ },
+
+ /**
+ * @private
+ * @argument {jQuery} $src
+ * @returns {undefined}
+ */
+ _onOtherActionMenuOpened: function($src) {
+ if (this.$el.is($src)) {
+ // Ignore
+ return;
+ }
+ this._actionMenuShown = false;
+ this.$('.menu').hide();
+ }
+ });
+
+ /**
+ * @class ContactsMenuView
+ */
+ var ContactsMenuView = OC.Backbone.View.extend({
+
+ /** @type {undefined|function} */
+ _loadingTemplate: undefined,
+
+ /** @type {undefined|function} */
+ _errorTemplate: undefined,
+
+ /** @type {undefined|function} */
+ _contentTemplate: undefined,
+
+ /** @type {undefined|function} */
+ _contactsTemplate: undefined,
+
+ /** @type {undefined|ContactCollection} */
+ _contacts: undefined,
+
+ events: {
+ 'input #contactsmenu-search': '_onSearch'
+ },
+
+ /**
+ * @returns {undefined}
+ */
+ _onSearch: _.debounce(function() {
+ this.trigger('search', this.$('#contactsmenu-search').val());
+ }, 700),
+
+ /**
+ * @param {object} data
+ * @returns {string}
+ */
+ loadingTemplate: function(data) {
+ if (!this._loadingTemplate) {
+ this._loadingTemplate = Handlebars.compile(LOADING_TEMPLATE);
+ }
+ return this._loadingTemplate(data);
+ },
+
+ /**
+ * @param {object} data
+ * @returns {string}
+ */
+ errorTemplate: function(data) {
+ if (!this._errorTemplate) {
+ this._errorTemplate = Handlebars.compile(ERROR_TEMPLATE);
+ }
+ return this._errorTemplate(data);
+ },
+
+ /**
+ * @param {object} data
+ * @returns {string}
+ */
+ contentTemplate: function(data) {
+ if (!this._contentTemplate) {
+ this._contentTemplate = Handlebars.compile(MENU_TEMPLATE);
+ }
+ return this._contentTemplate(data);
+ },
+
+ /**
+ * @param {object} data
+ * @returns {string}
+ */
+ contactsTemplate: function(data) {
+ if (!this._contactsTemplate) {
+ this._contactsTemplate = Handlebars.compile(CONTACTS_LIST_TEMPLATE);
+ }
+ return this._contactsTemplate(data);
+ },
+
+ /**
+ * @param {object} options
+ * @returns {undefined}
+ */
+ initialize: function(options) {
+ this.options = options;
+ },
+
+ /**
+ * @param {string} text
+ * @returns {undefined}
+ */
+ showLoading: function(text) {
+ this.render();
+ this._contacts = undefined;
+ this.$('.content').html(this.loadingTemplate({
+ loadingText: text
+ }));
+ },
+
+ /**
+ * @returns {undefined}
+ */
+ showError: function() {
+ this.render();
+ this._contacts = undefined;
+ this.$('.content').html(this.errorTemplate());
+ },
+
+ /**
+ * @param {object} viewData
+ * @param {string} searchTerm
+ * @returns {undefined}
+ */
+ showContacts: function(viewData, searchTerm) {
+ this._contacts = viewData.contacts;
+ this.render({
+ contacts: viewData.contacts
+ });
+
+ var list = new ContactsListView({
+ collection: viewData.contacts
+ });
+ list.render();
+ this.$('.content').html(this.contactsTemplate({
+ contacts: viewData.contacts,
+ searchTerm: searchTerm,
+ contactsAppEnabled: viewData.contactsAppEnabled,
+ contactsAppURL: OC.generateUrl('/apps/contacts')
+ }));
+ this.$('#contactsmenu-contacts').html(list.$el);
+ },
+
+ /**
+ * @param {object} data
+ * @returns {self}
+ */
+ render: function(data) {
+ var searchVal = this.$('#contactsmenu-search').val();
+ this.$el.html(this.contentTemplate(data));
+
+ // Focus search
+ this.$('#contactsmenu-search').val(searchVal);
+ this.$('#contactsmenu-search').focus();
+ return this;
+ }
+
+ });
+
+ /**
+ * @param {Object} options
+ * @param {jQuery} options.el
+ * @param {jQuery} options.trigger
+ * @class ContactsMenu
+ */
+ var ContactsMenu = function(options) {
+ this.initialize(options);
+ };
+
+ ContactsMenu.prototype = {
+ /** @type {jQuery} */
+ $el: undefined,
+
+ /** @type {jQuery} */
+ _$trigger: undefined,
+
+ /** @type {ContactsMenuView} */
+ _view: undefined,
+
+ /** @type {Promise} */
+ _contactsPromise: undefined,
+
+ /**
+ * @param {Object} options
+ * @param {jQuery} options.el - the element to render the menu in
+ * @param {jQuery} options.trigger - the element to click on to open the menu
+ * @returns {undefined}
+ */
+ initialize: function(options) {
+ this.$el = options.el;
+ this._$trigger = options.trigger;
+
+ this._view = new ContactsMenuView({
+ el: this.$el
+ });
+ this._view.on('search', function(searchTerm) {
+ this._loadContacts(searchTerm);
+ }, this);
+
+ OC.registerMenu(this._$trigger, this.$el, function() {
+ this._toggleVisibility(true);
+ }.bind(this));
+ this.$el.on('beforeHide', function() {
+ this._toggleVisibility(false);
+ }.bind(this));
+ },
+
+ /**
+ * @private
+ * @param {boolean} show
+ * @returns {Promise}
+ */
+ _toggleVisibility: function(show) {
+ if (show) {
+ return this._loadContacts();
+ } else {
+ this.$el.html('');
+ return Promise.resolve();
+ }
+ },
+
+ /**
+ * @private
+ * @param {string|undefined} searchTerm
+ * @returns {Promise}
+ */
+ _getContacts: function(searchTerm) {
+ var url = OC.generateUrl('/contactsmenu/contacts');
+ return Promise.resolve($.ajax(url, {
+ method: 'GET',
+ data: {
+ filter: searchTerm
+ }
+ }));
+ },
+
+ /**
+ * @param {string|undefined} searchTerm
+ * @returns {undefined}
+ */
+ _loadContacts: function(searchTerm) {
+ var self = this;
+
+ if (!self._contactsPromise) {
+ self._contactsPromise = self._getContacts(searchTerm);
+ }
+
+ if (_.isUndefined(searchTerm) || searchTerm === '') {
+ self._view.showLoading(t('core', 'Loading your contacts …'));
+ } else {
+ self._view.showLoading(t('core', 'Looking for {term} …', {
+ term: searchTerm
+ }));
+ }
+ return self._contactsPromise.then(function(data) {
+ // Convert contact entries to Backbone collection
+ data.contacts = new ContactCollection(data.contacts);
+
+ self._view.showContacts(data, searchTerm);
+ }, function(e) {
+ self._view.showError();
+ console.error('could not load contacts', e);
+ }).then(function() {
+ // Delete promise, so that contacts are fetched again when the
+ // menu is opened the next time.
+ delete self._contactsPromise;
+ }).catch(console.error.bind(this));
+ }
+ };
+
+ OC.ContactsMenu = ContactsMenu;
+
+})(OC, $, _, Handlebars);
diff --git a/core/js/core.json b/core/js/core.json
index 6494d4105f8..aadd66a0558 100644
--- a/core/js/core.json
+++ b/core/js/core.json
@@ -40,6 +40,7 @@
"sharedialogresharerinfoview.js",
"sharedialogshareelistview.js",
"octemplate.js",
+ "contactsmenu.js",
"eventsource.js",
"config.js",
"public/appconfig.js",
diff --git a/core/js/js.js b/core/js/js.js
index 95c00dd6448..03d831567d3 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -654,8 +654,13 @@ var OCP = {},
/**
* For menu toggling
* @todo Write documentation
+ *
+ * @param {jQuery} $toggle
+ * @param {jQuery} $menuEl
+ * @param {function|undefined} toggle callback invoked everytime the menu is opened
+ * @returns {undefined}
*/
- registerMenu: function($toggle, $menuEl) {
+ registerMenu: function($toggle, $menuEl, toggle) {
var self = this;
$menuEl.addClass('menu');
$toggle.on('click.menu', function(event) {
@@ -671,7 +676,7 @@ var OCP = {},
// close it
self.hideMenus();
}
- $menuEl.slideToggle(OC.menuSpeed);
+ $menuEl.slideToggle(OC.menuSpeed, toggle);
OC._currentMenu = $menuEl;
OC._currentMenuToggle = $toggle;
});
@@ -1473,8 +1478,16 @@ function initCore() {
});
}
+ function setupContactsMenu() {
+ new OC.ContactsMenu({
+ el: $('#contactsmenu .menu'),
+ trigger: $('#contactsmenu .menutoggle')
+ });
+ }
+
setupMainMenu();
setupUserMenu();
+ setupContactsMenu();
// move triangle of apps dropdown to align with app name triangle
// 2 is the additional offset between the triangles
diff --git a/core/js/tests/specs/contactsmenuSpec.js b/core/js/tests/specs/contactsmenuSpec.js
new file mode 100644
index 00000000000..1db9a6a9554
--- /dev/null
+++ b/core/js/tests/specs/contactsmenuSpec.js
@@ -0,0 +1,255 @@
+/* global expect, sinon, _, spyOn, Promise */
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+describe('Contacts menu', function() {
+ var $triggerEl,
+ $menuEl,
+ menu;
+
+ /**
+ * @private
+ * @returns {Promise}
+ */
+ function openMenu() {
+ return menu._toggleVisibility(true);
+ }
+
+ beforeEach(function(done) {
+ $triggerEl = $('<div class="menutoggle">');
+ $menuEl = $('<div class="menu">');
+
+ menu = new OC.ContactsMenu({
+ el: $menuEl,
+ trigger: $triggerEl
+ });
+ done();
+ });
+
+ it('shows a loading message while data is being fetched', function() {
+ fakeServer.respondWith('GET', OC.generateUrl('/contactsmenu/contacts'), [
+ 200,
+ {},
+ ''
+ ]);
+
+ openMenu();
+
+ expect($menuEl.html()).toContain('Loading your contacts …');
+ });
+
+ it('shows an error message when loading the contacts data fails', function(done) {
+ spyOn(console, 'error');
+ fakeServer.respondWith('GET', OC.generateUrl('/contactsmenu/contacts'), [
+ 500,
+ {},
+ ''
+ ]);
+
+ var opening = openMenu();
+
+ expect($menuEl.html()).toContain('Loading your contacts …');
+ fakeServer.respond();
+
+ opening.then(function() {
+ expect($menuEl.html()).toContain('Could not load your contacts.');
+ expect(console.error).toHaveBeenCalledTimes(1);
+ done();
+ }, function(e) {
+ done.fail(e);
+ });
+ });
+
+ it('loads data successfully', function(done) {
+ spyOn(menu, '_getContacts').and.returnValue(Promise.resolve({
+ contacts: [
+ {
+ id: null,
+ fullName: 'Acosta Lancaster',
+ topAction: {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:deboraoliver%40centrexin.com'
+ },
+ actions: [
+ {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:mathisholland%40virxo.com'
+ },
+ {
+ title: 'Details',
+ icon: 'icon-info',
+ hyperlink: 'https:\/\/localhost\/index.php\/apps\/contacts'
+ }
+ ],
+ lastMessage: ''
+ },
+ {
+ id: null,
+ fullName: 'Adeline Snider',
+ topAction: {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:ceciliasoto%40essensia.com'
+ },
+ actions: [
+ {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:pearliesellers%40inventure.com'
+ },
+ {
+ title: 'Details',
+ icon: 'icon-info',
+ hyperlink: 'https://localhost\/index.php\/apps\/contacts'
+ }
+ ],
+ lastMessage: 'cu'
+ }
+ ],
+ contactsAppEnabled: true
+ }));
+
+ openMenu().then(function() {
+ expect(menu._getContacts).toHaveBeenCalled();
+ expect($menuEl.html()).toContain('Acosta Lancaster');
+ expect($menuEl.html()).toContain('Adeline Snider');
+ expect($menuEl.html()).toContain('Show all contacts …');
+ done();
+ }, function(e) {
+ done.fail(e);
+ });
+
+ });
+
+ it('doesn\'t show a link to the contacts app if it\'s disabled', function(done) {
+ spyOn(menu, '_getContacts').and.returnValue(Promise.resolve({
+ contacts: [
+ {
+ id: null,
+ fullName: 'Acosta Lancaster',
+ topAction: {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:deboraoliver%40centrexin.com'
+ },
+ actions: [
+ {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:mathisholland%40virxo.com'
+ },
+ {
+ title: 'Details',
+ icon: 'icon-info',
+ hyperlink: 'https:\/\/localhost\/index.php\/apps\/contacts'
+ }
+ ],
+ lastMessage: ''
+ }
+ ],
+ contactsAppEnabled: false
+ }));
+
+ openMenu().then(function() {
+ expect(menu._getContacts).toHaveBeenCalled();
+ expect($menuEl.html()).not.toContain('Show all contacts …');
+ done();
+ }, function(e) {
+ done.fail(e);
+ });
+ });
+
+ it('shows only one entry\'s action menu at a time', function(done) {
+ spyOn(menu, '_getContacts').and.returnValue(Promise.resolve({
+ contacts: [
+ {
+ id: null,
+ fullName: 'Acosta Lancaster',
+ topAction: {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:deboraoliver%40centrexin.com'
+ },
+ actions: [
+ {
+ title: 'Details',
+ icon: 'icon-info',
+ hyperlink: 'https:\/\/localhost\/index.php\/apps\/contacts'
+ }
+ ],
+ lastMessage: ''
+ },
+ {
+ id: null,
+ fullName: 'Adeline Snider',
+ topAction: {
+ title: 'Mail',
+ icon: 'icon-mail',
+ hyperlink: 'mailto:ceciliasoto%40essensia.com'
+ },
+ actions: [
+ {
+ title: 'Details',
+ icon: 'icon-info',
+ hyperlink: 'https://localhost\/index.php\/apps\/contacts'
+ }
+ ],
+ lastMessage: 'cu'
+ }
+ ],
+ contactsAppEnabled: true
+ }));
+
+ openMenu().then(function() {
+ expect(menu._getContacts).toHaveBeenCalled();
+ expect($menuEl.html()).toContain('Adeline Snider');
+ expect($menuEl.html()).toContain('Show all contacts …');
+
+ // Both menus are closed at the beginning
+ expect($menuEl.find('.contact').eq(0).find('.menu').is(':visible')).toBeFalsy();
+ expect($menuEl.find('.contact').eq(1).find('.menu').is(':visible')).toBeFalsy();
+
+ // Open the first one
+ $menuEl.find('.contact').eq(0).find('.other-actions').click();
+ expect($menuEl.find('.contact').eq(0).find('.menu').css('display')).toBe('block');
+ expect($menuEl.find('.contact').eq(1).find('.menu').css('display')).toBe('none');
+
+ // Open the second one
+ $menuEl.find('.contact').eq(1).find('.other-actions').click();
+ expect($menuEl.find('.contact').eq(0).find('.menu').css('display')).toBe('none');
+ expect($menuEl.find('.contact').eq(1).find('.menu').css('display')).toBe('block');
+
+ // Close the second one
+ $menuEl.find('.contact').eq(1).find('.other-actions').click();
+ expect($menuEl.find('.contact').eq(0).find('.menu').css('display')).toBe('none');
+ expect($menuEl.find('.contact').eq(1).find('.menu').css('display')).toBe('none');
+
+ done();
+ }, function(e) {
+ done.fail(e);
+ });
+ });
+
+});
diff --git a/core/routes.php b/core/routes.php
index 7a2d9d750cc..0e882c625fc 100644
--- a/core/routes.php
+++ b/core/routes.php
@@ -57,6 +57,7 @@ $application->registerRoutes($this, [
['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'],
['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'],
+ ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'GET'],
],
'ocs' => [
['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 91b7eb3490b..426ed4b1125 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -118,6 +118,10 @@
autocomplete="off" tabindex="5">
<button class="icon-close-white" type="reset"></button>
</form>
+ <div id="contactsmenu">
+ <div class="icon-contacts menutoggle"></div>
+ <div class="menu"></div>
+ </div>
<div id="settings">
<div id="expand" tabindex="6" role="link" class="menutoggle">
<div class="avatardiv<?php if ($_['userAvatarSet']) { print_unescaped(' avatardiv-shown'); } else { print_unescaped('" style="display: none'); } ?>">
@@ -161,5 +165,6 @@
<?php print_unescaped($_['content']); ?>
</div>
</div>
+
</body>
</html>
diff --git a/lib/composer/composer/ClassLoader.php b/lib/composer/composer/ClassLoader.php
index 2c72175e772..4626994fd4d 100644
--- a/lib/composer/composer/ClassLoader.php
+++ b/lib/composer/composer/ClassLoader.php
@@ -374,13 +374,9 @@ class ClassLoader
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
- $subPath = $class;
- while (false !== $lastPos = strrpos($subPath, '\\')) {
- $subPath = substr($subPath, 0, $lastPos);
- $search = $subPath.'\\';
- if (isset($this->prefixDirsPsr4[$search])) {
- foreach ($this->prefixDirsPsr4[$search] as $dir) {
- $length = $this->prefixLengthsPsr4[$first][$search];
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
diff --git a/lib/composer/composer/LICENSE b/lib/composer/composer/LICENSE
index f27399a042d..1a28124886d 100644
--- a/lib/composer/composer/LICENSE
+++ b/lib/composer/composer/LICENSE
@@ -1,5 +1,5 @@
-Copyright (c) Nils Adermann, Jordi Boggiano
+Copyright (c) 2016 Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 0d5f067779d..0e858a54aeb 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -80,6 +80,11 @@ return array(
'OCP\\Console\\ConsoleEvent' => $baseDir . '/lib/public/Console/ConsoleEvent.php',
'OCP\\Constants' => $baseDir . '/lib/public/Constants.php',
'OCP\\Contacts' => $baseDir . '/lib/public/Contacts.php',
+ 'OCP\\Contacts\\ContactsMenu\\IAction' => $baseDir . '/lib/public/Contacts/ContactsMenu/IAction.php',
+ 'OCP\\Contacts\\ContactsMenu\\IActionFactory' => $baseDir . '/lib/public/Contacts/ContactsMenu/IActionFactory.php',
+ 'OCP\\Contacts\\ContactsMenu\\IEntry' => $baseDir . '/lib/public/Contacts/ContactsMenu/IEntry.php',
+ 'OCP\\Contacts\\ContactsMenu\\ILinkAction' => $baseDir . '/lib/public/Contacts/ContactsMenu/ILinkAction.php',
+ 'OCP\\Contacts\\ContactsMenu\\IProvider' => $baseDir . '/lib/public/Contacts/ContactsMenu/IProvider.php',
'OCP\\Contacts\\IManager' => $baseDir . '/lib/public/Contacts/IManager.php',
'OCP\\DB' => $baseDir . '/lib/public/DB.php',
'OCP\\DB\\QueryBuilder\\ICompositeExpression' => $baseDir . '/lib/public/DB/QueryBuilder/ICompositeExpression.php',
@@ -373,6 +378,14 @@ return array(
'OC\\Console\\Application' => $baseDir . '/lib/private/Console/Application.php',
'OC\\Console\\TimestampFormatter' => $baseDir . '/lib/private/Console/TimestampFormatter.php',
'OC\\ContactsManager' => $baseDir . '/lib/private/ContactsManager.php',
+ 'OC\\Contacts\\ContactsMenu\\ActionFactory' => $baseDir . '/lib/private/Contacts/ContactsMenu/ActionFactory.php',
+ 'OC\\Contacts\\ContactsMenu\\ActionProviderStore' => $baseDir . '/lib/private/Contacts/ContactsMenu/ActionProviderStore.php',
+ 'OC\\Contacts\\ContactsMenu\\Actions\\LinkAction' => $baseDir . '/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php',
+ 'OC\\Contacts\\ContactsMenu\\ContactsStore' => $baseDir . '/lib/private/Contacts/ContactsMenu/ContactsStore.php',
+ 'OC\\Contacts\\ContactsMenu\\Entry' => $baseDir . '/lib/private/Contacts/ContactsMenu/Entry.php',
+ 'OC\\Contacts\\ContactsMenu\\Manager' => $baseDir . '/lib/private/Contacts/ContactsMenu/Manager.php',
+ 'OC\\Contacts\\ContactsMenu\\Providers\\DetailsProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/DetailsProvider.php',
+ 'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php',
'OC\\Core\\Application' => $baseDir . '/core/Application.php',
'OC\\Core\\Command\\App\\CheckCode' => $baseDir . '/core/Command/App/CheckCode.php',
'OC\\Core\\Command\\App\\Disable' => $baseDir . '/core/Command/App/Disable.php',
@@ -444,6 +457,7 @@ return array(
'OC\\Core\\Command\\User\\ResetPassword' => $baseDir . '/core/Command/User/ResetPassword.php',
'OC\\Core\\Command\\User\\Setting' => $baseDir . '/core/Command/User/Setting.php',
'OC\\Core\\Controller\\AvatarController' => $baseDir . '/core/Controller/AvatarController.php',
+ 'OC\\Core\\Controller\\ContactsMenuController' => $baseDir . '/core/Controller/ContactsMenuController.php',
'OC\\Core\\Controller\\CssController' => $baseDir . '/core/Controller/CssController.php',
'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php',
'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 82c31c24a21..73fba39b23a 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -110,6 +110,11 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\Console\\ConsoleEvent' => __DIR__ . '/../../..' . '/lib/public/Console/ConsoleEvent.php',
'OCP\\Constants' => __DIR__ . '/../../..' . '/lib/public/Constants.php',
'OCP\\Contacts' => __DIR__ . '/../../..' . '/lib/public/Contacts.php',
+ 'OCP\\Contacts\\ContactsMenu\\IAction' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IAction.php',
+ 'OCP\\Contacts\\ContactsMenu\\IActionFactory' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IActionFactory.php',
+ 'OCP\\Contacts\\ContactsMenu\\IEntry' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IEntry.php',
+ 'OCP\\Contacts\\ContactsMenu\\ILinkAction' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/ILinkAction.php',
+ 'OCP\\Contacts\\ContactsMenu\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IProvider.php',
'OCP\\Contacts\\IManager' => __DIR__ . '/../../..' . '/lib/public/Contacts/IManager.php',
'OCP\\DB' => __DIR__ . '/../../..' . '/lib/public/DB.php',
'OCP\\DB\\QueryBuilder\\ICompositeExpression' => __DIR__ . '/../../..' . '/lib/public/DB/QueryBuilder/ICompositeExpression.php',
@@ -403,6 +408,14 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Console\\Application' => __DIR__ . '/../../..' . '/lib/private/Console/Application.php',
'OC\\Console\\TimestampFormatter' => __DIR__ . '/../../..' . '/lib/private/Console/TimestampFormatter.php',
'OC\\ContactsManager' => __DIR__ . '/../../..' . '/lib/private/ContactsManager.php',
+ 'OC\\Contacts\\ContactsMenu\\ActionFactory' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/ActionFactory.php',
+ 'OC\\Contacts\\ContactsMenu\\ActionProviderStore' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/ActionProviderStore.php',
+ 'OC\\Contacts\\ContactsMenu\\Actions\\LinkAction' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php',
+ 'OC\\Contacts\\ContactsMenu\\ContactsStore' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/ContactsStore.php',
+ 'OC\\Contacts\\ContactsMenu\\Entry' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Entry.php',
+ 'OC\\Contacts\\ContactsMenu\\Manager' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Manager.php',
+ 'OC\\Contacts\\ContactsMenu\\Providers\\DetailsProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/DetailsProvider.php',
+ 'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php',
'OC\\Core\\Application' => __DIR__ . '/../../..' . '/core/Application.php',
'OC\\Core\\Command\\App\\CheckCode' => __DIR__ . '/../../..' . '/core/Command/App/CheckCode.php',
'OC\\Core\\Command\\App\\Disable' => __DIR__ . '/../../..' . '/core/Command/App/Disable.php',
@@ -474,6 +487,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Core\\Command\\User\\ResetPassword' => __DIR__ . '/../../..' . '/core/Command/User/ResetPassword.php',
'OC\\Core\\Command\\User\\Setting' => __DIR__ . '/../../..' . '/core/Command/User/Setting.php',
'OC\\Core\\Controller\\AvatarController' => __DIR__ . '/../../..' . '/core/Controller/AvatarController.php',
+ 'OC\\Core\\Controller\\ContactsMenuController' => __DIR__ . '/../../..' . '/core/Controller/ContactsMenuController.php',
'OC\\Core\\Controller\\CssController' => __DIR__ . '/../../..' . '/core/Controller/CssController.php',
'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php',
'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php',
diff --git a/lib/private/Contacts/ContactsMenu/ActionFactory.php b/lib/private/Contacts/ContactsMenu/ActionFactory.php
new file mode 100644
index 00000000000..1d2a69c904d
--- /dev/null
+++ b/lib/private/Contacts/ContactsMenu/ActionFactory.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\Actions\LinkAction;
+use OCP\Contacts\ContactsMenu\IActionFactory;
+use OCP\Contacts\ContactsMenu\ILinkAction;
+
+class ActionFactory implements IActionFactory {
+
+ /**
+ * @param string $icon
+ * @param string $name
+ * @param string $href
+ * @return ILinkAction
+ */
+ public function newLinkAction($icon, $name, $href) {
+ $action = new LinkAction();
+ $action->setName($name);
+ $action->setIcon($icon);
+ $action->setHref($href);
+ return $action;
+ }
+
+ /**
+ * @param string $icon
+ * @param string $name
+ * @param string $email
+ * @return ILinkAction
+ */
+ public function newEMailAction($icon, $name, $email) {
+ return $this->newLinkAction($icon, $name, 'mailto:' . urlencode($email));
+ }
+
+}
diff --git a/lib/private/Contacts/ContactsMenu/ActionProviderStore.php b/lib/private/Contacts/ContactsMenu/ActionProviderStore.php
new file mode 100644
index 00000000000..cc5996377dd
--- /dev/null
+++ b/lib/private/Contacts/ContactsMenu/ActionProviderStore.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\Providers\EMailProvider;
+use OCP\AppFramework\QueryException;
+use OCP\Contacts\ContactsMenu\IProvider;
+use OCP\ILogger;
+use OCP\IServerContainer;
+
+class ActionProviderStore {
+
+ /** @var IServerContainer */
+ private $serverContainer;
+
+ /** @var ILogger */
+ private $logger;
+
+ /**
+ * @param IServerContainer $serverContainer
+ */
+ public function __construct(IServerContainer $serverContainer, ILogger $logger) {
+ $this->serverContainer = $serverContainer;
+ $this->logger = $logger;
+ }
+
+ /**
+ * @return IProvider[]
+ * @throws Exception
+ */
+ public function getProviders() {
+ // TODO: include apps
+ $providerClasses = $this->getServerProviderClasses();
+ $providers = [];
+
+ foreach ($providerClasses as $class) {
+ try {
+ $providers[] = $this->serverContainer->query($class);
+ } catch (QueryException $ex) {
+ $this->logger->logException($ex, [
+ 'message' => "Could not load contacts menu action provider $class",
+ 'app' => 'core',
+ ]);
+ throw new \Exception("Could not load contacts menu action provider");
+ }
+ }
+
+ return $providers;
+ }
+
+ /**
+ * @return string[]
+ */
+ private function getServerProviderClasses() {
+ return [
+ EMailProvider::class,
+ ];
+ }
+
+}
diff --git a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php
new file mode 100644
index 00000000000..5b8b0524a21
--- /dev/null
+++ b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Contacts\ContactsMenu\Actions;
+
+use OCP\Contacts\ContactsMenu\ILinkAction;
+
+class LinkAction implements ILinkAction {
+
+ /** @var string */
+ private $icon;
+
+ /** @var string */
+ private $name;
+
+ /** @var string */
+ private $href;
+
+ /** @var int */
+ private $priority = 10;
+
+ /**
+ * @param string $icon absolute URI to an icon
+ */
+ public function setIcon($icon) {
+ $this->icon = $icon;
+ }
+
+ /**
+ * @param string $name
+ */
+ public function setName($name) {
+ $this->name = $name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @param int $priority
+ */
+ public function setPriority($priority) {
+ $this->priority = $priority;
+ }
+
+ /**
+ * @return int
+ */
+ public function getPriority() {
+ return $this->priority;
+ }
+
+ /**
+ * @param string $href
+ */
+ public function setHref($href) {
+ $this->href = $href;
+ }
+
+ /**
+ * @return string
+ */
+ public function getHref() {
+ return $this->href;
+ }
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize() {
+ return [
+ 'title' => $this->name,
+ 'icon' => $this->icon,
+ 'hyperlink' => $this->href,
+ ];
+ }
+
+}
diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php
new file mode 100644
index 00000000000..fba970e93fa
--- /dev/null
+++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Contacts\ContactsMenu;
+
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\Contacts\IManager;
+
+class ContactsStore {
+
+ /** @var IManager */
+ private $contactsManager;
+
+ /**
+ * @param IManager $contactsManager
+ */
+ public function __construct(IManager $contactsManager) {
+ $this->contactsManager = $contactsManager;
+ }
+
+ /**
+ * @param string|null $filter
+ * @return IEntry[]
+ */
+ public function getContacts($filter) {
+ $allContacts = $this->contactsManager->search($filter ?: '', [
+ 'FN',
+ ]);
+
+ return array_map(function(array $contact) {
+ return $this->contactArrayToEntry($contact);
+ }, $allContacts);
+ }
+
+ /**
+ * @param array $contact
+ * @return Entry
+ */
+ private function contactArrayToEntry(array $contact) {
+ $entry = new Entry();
+
+ if (isset($contact['id'])) {
+ $entry->setId($contact['id']);
+ }
+
+ if (isset($contact['FN'])) {
+ $entry->setFullName($contact['FN']);
+ }
+
+ $avatarPrefix = "VALUE=uri:";
+ if (isset($contact['PHOTO']) && strpos($contact['PHOTO'], $avatarPrefix) === 0) {
+ $entry->setAvatar(substr($contact['PHOTO'], strlen($avatarPrefix)));
+ }
+
+ if (isset($contact['EMAIL'])) {
+ foreach ($contact['EMAIL'] as $email) {
+ $entry->addEMailAddress($email);
+ }
+ }
+
+ // Attach all other properties to the entry too because some
+ // providers might make use of it.
+ $entry->setProperties($contact);
+
+ return $entry;
+ }
+
+}
diff --git a/lib/private/Contacts/ContactsMenu/Entry.php b/lib/private/Contacts/ContactsMenu/Entry.php
new file mode 100644
index 00000000000..9ea0511b9cc
--- /dev/null
+++ b/lib/private/Contacts/ContactsMenu/Entry.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Contacts\ContactsMenu;
+
+use OCP\Contacts\ContactsMenu\IAction;
+use OCP\Contacts\ContactsMenu\IEntry;
+
+class Entry implements IEntry {
+
+ /** @var string|int|null */
+ private $id = null;
+
+ /** @var string */
+ private $fullName = '';
+
+ /** @var string[] */
+ private $emailAddresses = [];
+
+ /** @var string|null */
+ private $avatar;
+
+ /** @var IAction[] */
+ private $actions = [];
+
+ /** @var array */
+ private $properties = [];
+
+ /**
+ * @param string $id
+ */
+ public function setId($id) {
+ $this->id = $id;
+ }
+
+ /**
+ * @param string $displayName
+ */
+ public function setFullName($displayName) {
+ $this->fullName = $displayName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFullName() {
+ return $this->fullName;
+ }
+
+ /**
+ * @param string $address
+ */
+ public function addEMailAddress($address) {
+ $this->emailAddresses[] = $address;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEMailAddresses() {
+ return $this->emailAddresses;
+ }
+
+ /**
+ * @param string $avatar
+ */
+ public function setAvatar($avatar) {
+ $this->avatar = $avatar;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAvatar() {
+ return $this->avatar;
+ }
+
+ /**
+ * @param IAction $action
+ */
+ public function addAction(IAction $action) {
+ $this->actions[] = $action;
+ $this->sortActions();
+ }
+
+ /**
+ * @return IAction[]
+ */
+ public function getActions() {
+ return $this->actions;
+ }
+
+ /**
+ * sort the actions by priority and name
+ */
+ private function sortActions() {
+ usort($this->actions, function(IAction $action1, IAction $action2) {
+ $prio1 = $action1->getPriority();
+ $prio2 = $action2->getPriority();
+
+ if ($prio1 === $prio2) {
+ // Ascending order for same priority
+ return strcasecmp($action1->getName(), $action2->getName());
+ }
+
+ // Descending order when priority differs
+ return $prio2 - $prio1;
+ });
+ }
+
+ /**
+ * @param array $contact key-value array containing additional properties
+ */
+ public function setProperties(array $contact) {
+ $this->properties = $contact;
+ }
+
+ /**
+ * @param string $key
+ * @return mixed
+ */
+ public function getProperty($key) {
+ if (!isset($this->properties[$key])) {
+ return null;
+ }
+ return $this->properties[$key];
+ }
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize() {
+ $topAction = !empty($this->actions) ? $this->actions[0]->jsonSerialize() : null;
+ $otherActions = array_map(function(IAction $action) {
+ return $action->jsonSerialize();
+ }, array_slice($this->actions, 1));
+
+ return [
+ 'id' => $this->id,
+ 'fullName' => $this->fullName,
+ 'avatar' => $this->getAvatar(),
+ 'topAction' => $topAction,
+ 'actions' => $otherActions,
+ 'lastMessage' => '',
+ ];
+ }
+
+}
diff --git a/lib/private/Contacts/ContactsMenu/Manager.php b/lib/private/Contacts/ContactsMenu/Manager.php
new file mode 100644
index 00000000000..c759c4469da
--- /dev/null
+++ b/lib/private/Contacts/ContactsMenu/Manager.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Contacts\ContactsMenu;
+
+use OCP\App\IAppManager;
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\IURLGenerator;
+
+class Manager {
+
+ /** @var ContactsStore */
+ private $store;
+
+ /** @var ActionProviderStore */
+ private $actionProviderStore;
+
+ /** @var IAppManager */
+ private $appManager;
+
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
+ /**
+ * @param ContactsStore $store
+ * @param ActionProviderStore $actionProviderStore
+ * @param IAppManager $appManager
+ */
+ public function __construct(ContactsStore $store, ActionProviderStore $actionProviderStore, IAppManager $appManager) {
+ $this->store = $store;
+ $this->actionProviderStore = $actionProviderStore;
+ $this->appManager = $appManager;
+ }
+
+ /**
+ * @param string $userId
+ * @param string $filter
+ * @return array
+ */
+ public function getEntries($userId, $filter) {
+ $entries = $this->store->getContacts($filter);
+
+ $sortedEntries = $this->sortEntries($entries);
+ $topEntries = array_slice($sortedEntries, 0, 25);
+ $this->processEntries($topEntries);
+
+ $contactsEnabled = $this->appManager->isEnabledForUser('contacts', $userId);
+ return [
+ 'contacts' => $topEntries,
+ 'contactsAppEnabled' => $contactsEnabled,
+ ];
+ }
+
+ /**
+ * @param IEntry[] $entries
+ * @return IEntry[]
+ */
+ private function sortEntries(array $entries) {
+ usort($entries, function(IEntry $entryA, IEntry $entryB) {
+ return strcasecmp($entryA->getFullName(), $entryB->getFullName());
+ });
+ return $entries;
+ }
+
+ /**
+ * @param IEntry[] $entries
+ */
+ private function processEntries(array $entries) {
+ $providers = $this->actionProviderStore->getProviders();
+ foreach ($entries as $entry) {
+ foreach ($providers as $provider) {
+ $provider->process($entry);
+ }
+ }
+ }
+
+}
diff --git a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php
new file mode 100644
index 00000000000..c74bdc0b8ff
--- /dev/null
+++ b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Contacts\ContactsMenu\Providers;
+
+use OCP\Contacts\ContactsMenu\IActionFactory;
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\Contacts\ContactsMenu\IProvider;
+
+class EMailProvider implements IProvider {
+
+ /** @var IActionFactory */
+ private $actionFactory;
+
+ /**
+ * @param IActionFactory $actionFactory
+ */
+ public function __construct(IActionFactory $actionFactory) {
+ $this->actionFactory = $actionFactory;
+ }
+
+ /**
+ * @param IEntry $entry
+ */
+ public function process(IEntry $entry) {
+ foreach ($entry->getEMailAddresses() as $address) {
+ // TODO: absolute path
+ // TODO: meaningful URL
+ // TODO: l10n
+ $action = $this->actionFactory->newEMailAction('icon-mail', 'Mail', $address);
+ $entry->addAction($action);
+ }
+ }
+
+}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index f40a59ad334..7724feb551b 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -50,6 +50,7 @@ use OC\AppFramework\Utility\SimpleContainer;
use OC\AppFramework\Utility\TimeFactory;
use OC\Authentication\LoginCredentials\Store;
use OC\Command\AsyncBus;
+use OC\Contacts\ContactsMenu\ActionFactory;
use OC\Diagnostics\EventLogger;
use OC\Diagnostics\NullEventLogger;
use OC\Diagnostics\NullQueryLogger;
@@ -108,6 +109,8 @@ use OCP\IDBConnection;
use OCP\IL10N;
use OCP\IServerContainer;
use OCP\ITempManager;
+use OCP\Contacts\ContactsMenu\IActionFactory;
+use OCP\IURLGenerator;
use OCP\RichObjectStrings\IValidator;
use OCP\Security\IContentSecurityPolicyManager;
use OCP\Share\IShareHelper;
@@ -133,9 +136,17 @@ class Server extends ServerContainer implements IServerContainer {
parent::__construct();
$this->webRoot = $webRoot;
+ $this->registerService(\OCP\IServerContainer::class, function(IServerContainer $c) {
+ return $c;
+ });
+
$this->registerAlias(\OCP\Contacts\IManager::class, \OC\ContactsManager::class);
$this->registerAlias('ContactsManager', \OCP\Contacts\IManager::class);
+ $this->registerAlias(IActionFactory::class, ActionFactory::class);
+
+
+
$this->registerService(\OCP\IPreview::class, function (Server $c) {
return new PreviewManager(
$c->getConfig(),
diff --git a/lib/private/legacy/template.php b/lib/private/legacy/template.php
index 19b5e418110..9a919ff12f2 100644
--- a/lib/private/legacy/template.php
+++ b/lib/private/legacy/template.php
@@ -118,6 +118,7 @@ class OC_Template extends \OC\Template\Base {
OC_Util::addScript('jquery-ui-fixes');
OC_Util::addScript('files/fileinfo');
OC_Util::addScript('files/client');
+ OC_Util::addScript('contactsmenu');
if (\OC::$server->getConfig()->getSystemValue('debug')) {
// Add the stuff we need always
diff --git a/lib/public/Contacts/ContactsMenu/IAction.php b/lib/public/Contacts/ContactsMenu/IAction.php
new file mode 100644
index 00000000000..44ad1af5ae8
--- /dev/null
+++ b/lib/public/Contacts/ContactsMenu/IAction.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Contacts\ContactsMenu;
+
+use JsonSerializable;
+
+/**
+ * Apps should use the IActionFactory to create new action objects
+ *
+ * @since 12.0
+ */
+interface IAction extends JsonSerializable {
+
+ /**
+ * @param string $icon absolute URI to an icon
+ * @since 12.0
+ */
+ public function setIcon($icon);
+
+ /**
+ * @return string localized action name, e.g. 'Call'
+ * @since 12.0
+ */
+ public function getName();
+
+ /**
+ * @param string $name localized action name, e.g. 'Call'
+ * @since 12.0
+ */
+ public function setName($name);
+
+ /**
+ * @param int $priority priorize actions, high order ones are shown on top
+ * @since 12.0
+ */
+ public function setPriority($priority);
+
+ /**
+ * @return int priority to priorize actions, high order ones are shown on top
+ * @since 12.0
+ */
+ public function getPriority();
+}
diff --git a/lib/public/Contacts/ContactsMenu/IActionFactory.php b/lib/public/Contacts/ContactsMenu/IActionFactory.php
new file mode 100644
index 00000000000..8778a729a56
--- /dev/null
+++ b/lib/public/Contacts/ContactsMenu/IActionFactory.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Contacts\ContactsMenu;
+
+/**
+ * @since 12.0
+ */
+interface IActionFactory {
+
+ /**
+ * Construct and return a new link action for the contacts menu
+ *
+ * @since 12.0
+ *
+ * @param string $icon full path to the action's icon
+ * @param string $name localized name of the action
+ * @param string $href target URL
+ * @return ILinkAction
+ */
+ public function newLinkAction($icon, $name, $href);
+
+ /**
+ * Construct and return a new email action for the contacts menu
+ *
+ * @since 12.0
+ *
+ * @param string $icon full path to the action's icon
+ * @param string $name localized name of the action
+ * @param string $email target e-mail address
+ * @return ILinkAction
+ */
+ public function newEMailAction($icon, $name, $email);
+}
diff --git a/lib/public/Contacts/ContactsMenu/IEntry.php b/lib/public/Contacts/ContactsMenu/IEntry.php
new file mode 100644
index 00000000000..eb04147a1bc
--- /dev/null
+++ b/lib/public/Contacts/ContactsMenu/IEntry.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Contacts\ContactsMenu;
+
+use JsonSerializable;
+
+/**
+ * @since 12.0
+ */
+interface IEntry extends JsonSerializable {
+
+ /**
+ * @since 12.0
+ * @return string
+ */
+ public function getFullName();
+
+ /**
+ * @since 12.0
+ * @return string[]
+ */
+ public function getEMailAddresses();
+
+ /**
+ * @since 12.0
+ * @return string|null image URI
+ */
+ public function getAvatar();
+
+ /**
+ * @since 12.0
+ * @param IAction $action an action to show in the contacts menu
+ */
+ public function addAction(IAction $action);
+
+ /**
+ * Get an arbitrary property from the contact
+ *
+ * @since 12.0
+ * @param string $key
+ * @return mixed the value of the property or null
+ */
+ public function getProperty($key);
+}
diff --git a/lib/public/Contacts/ContactsMenu/ILinkAction.php b/lib/public/Contacts/ContactsMenu/ILinkAction.php
new file mode 100644
index 00000000000..4e29f757c26
--- /dev/null
+++ b/lib/public/Contacts/ContactsMenu/ILinkAction.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Contacts\ContactsMenu;
+
+/**
+ * @since 12.0
+ */
+interface ILinkAction extends IAction {
+
+ /**
+ * @since 12.0
+ * @param string $href the target URL of the action
+ */
+ public function setHref($href);
+
+ /**
+ * @since 12.0
+ * @return string
+ */
+ public function getHref();
+}
diff --git a/lib/public/Contacts/ContactsMenu/IProvider.php b/lib/public/Contacts/ContactsMenu/IProvider.php
new file mode 100644
index 00000000000..17fcc003720
--- /dev/null
+++ b/lib/public/Contacts/ContactsMenu/IProvider.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCP\Contacts\ContactsMenu;
+
+/**
+ * @since 12.0
+ */
+interface IProvider {
+
+ /**
+ * @since 12.0
+ * @param IEntry $entry
+ */
+ public function process(IEntry $entry);
+}
diff --git a/tests/Core/Controller/ContactsMenuControllerTest.php b/tests/Core/Controller/ContactsMenuControllerTest.php
new file mode 100644
index 00000000000..2483d5e94c3
--- /dev/null
+++ b/tests/Core/Controller/ContactsMenuControllerTest.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Controller;
+
+use OC\Contacts\ContactsMenu\Manager;
+use OC\Core\Controller\ContactsMenuController;
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\IRequest;
+use PHPUnit_Framework_MockObject_MockObject;
+use Test\TestCase;
+
+class ContactsMenuControllerTest extends TestCase {
+
+ /** @var IRequest|PHPUnit_Framework_MockObject_MockObject */
+ private $request;
+
+ /** @var string */
+ private $userId;
+
+ /** @var Manager|PHPUnit_Framework_MockObject_MockObject */
+ private $contactsManager;
+
+ /** @var ContactsMenuController */
+ private $controller;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->request = $this->createMock(IRequest::class);
+ $this->userId = 'user4563';
+ $this->contactsManager = $this->createMock(Manager::class);
+
+ $this->controller = new ContactsMenuController($this->request, $this->userId, $this->contactsManager);
+ }
+
+ public function testIndex() {
+ $entries = [
+ $this->createMock(IEntry::class),
+ $this->createMock(IEntry::class),
+ ];
+ $this->contactsManager->expects($this->once())
+ ->method('getEntries')
+ ->with($this->equalTo($this->userId), $this->equalTo(null))
+ ->willReturn($entries);
+
+ $response = $this->controller->index();
+
+ $this->assertEquals($entries, $response);
+ }
+
+}
diff --git a/tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php b/tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php
new file mode 100644
index 00000000000..d1273c2b9ad
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\ActionFactory;
+use OCP\Contacts\ContactsMenu\IAction;
+use Test\TestCase;
+
+class ActionFactoryTest extends TestCase {
+
+ /** @var ActionFactory */
+ private $actionFactory;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->actionFactory = new ActionFactory();
+ }
+
+ public function testNewLinkAction() {
+ $icon = 'icon-test';
+ $name = 'Test';
+ $href = 'some/url';
+
+ $action = $this->actionFactory->newLinkAction($icon, $name, $href);
+
+ $this->assertInstanceOf(IAction::class, $action);
+ $this->assertEquals($name, $action->getName());
+ $this->assertEquals(10, $action->getPriority());
+ }
+
+ public function testNewEMailAction() {
+ $icon = 'icon-test';
+ $name = 'Test';
+ $href = 'user@example.com';
+
+ $action = $this->actionFactory->newEMailAction($icon, $name, $href);
+
+ $this->assertInstanceOf(IAction::class, $action);
+ $this->assertEquals($name, $action->getName());
+ $this->assertEquals(10, $action->getPriority());
+ $this->assertEquals('mailto:user%40example.com', $action->getHref());
+ }
+
+}
diff --git a/tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php b/tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php
new file mode 100644
index 00000000000..208ce94c6b0
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use Exception;
+use OC\Contacts\ContactsMenu\ActionProviderStore;
+use OC\Contacts\ContactsMenu\Providers\EMailProvider;
+use OCP\AppFramework\QueryException;
+use OCP\ILogger;
+use OCP\IServerContainer;
+use PHPUnit_Framework_MockObject_MockObject;
+use Test\TestCase;
+
+class ActionProviderStoreTest extends TestCase {
+
+ /** @var IServerContainer|PHPUnit_Framework_MockObject_MockObject */
+ private $serverContainer;
+
+ /** @var ILogger|PHPUnit_Framework_MockObject_MockObject */
+ private $logger;
+
+ /** @var ActionProviderStore */
+ private $actionProviderStore;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->serverContainer = $this->createMock(IServerContainer::class);
+ $this->logger = $this->createMock(ILogger::class);
+ $this->actionProviderStore = new ActionProviderStore($this->serverContainer, $this->logger);
+ }
+
+ public function testGetProviders() {
+ $emailProvider = $this->createMock(EMailProvider::class);
+ $this->serverContainer->expects($this->exactly(2))
+ ->method('query')
+ ->will($this->returnValueMap([
+ [EMailProvider::class, $emailProvider],
+ ]));
+
+ $providers = $this->actionProviderStore->getProviders();
+
+ $this->assertCount(1, $providers);
+ $this->assertInstanceOf(EMailProvider::class, $providers[0]);
+ }
+
+ /**
+ * @expectedException Exception
+ */
+ public function testGetProvidersWithQueryException() {
+ $emailProvider = $this->createMock(EMailProvider::class);
+ $detailsProvider = $this->createMock(DetailsProvider::class);
+ $this->serverContainer->expects($this->once())
+ ->method('query')
+ ->willThrowException(new QueryException());
+
+ $providers = $this->actionProviderStore->getProviders();
+ }
+
+}
diff --git a/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php b/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php
new file mode 100644
index 00000000000..31654b40918
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Contacts\ContactsMenu\Actions;
+
+use OC\Contacts\ContactsMenu\Actions\LinkAction;
+use Test\TestCase;
+
+class LinkActionTest extends TestCase {
+
+ private $action;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->action = new LinkAction();
+ }
+
+ public function testSetIcon() {
+ $icon = 'icon-test';
+
+ $this->action->setIcon($icon);
+ $json = $this->action->jsonSerialize();
+
+ $this->assertArrayHasKey('icon', $json);
+ $this->assertEquals($json['icon'], $icon);
+ }
+
+ public function testGetSetName() {
+ $name = 'Jane Doe';
+
+ $this->assertNull($this->action->getName());
+ $this->action->setName($name);
+ $this->assertEquals($name, $this->action->getName());
+ }
+
+ public function testGetSetPriority() {
+ $prio = 50;
+
+ $this->assertEquals(10, $this->action->getPriority());
+ $this->action->setPriority($prio);
+ $this->assertEquals($prio, $this->action->getPriority());
+ }
+
+ public function testSetHref() {
+ $this->action->setHref('/some/url');
+
+ $json = $this->action->jsonSerialize();
+ $this->assertArrayHasKey('hyperlink', $json);
+ $this->assertEquals($json['hyperlink'], '/some/url');
+ }
+
+ public function testJsonSerialize() {
+ $this->action->setIcon('icon-contacts');
+ $this->action->setName('Nickie Works');
+ $this->action->setPriority(33);
+ $this->action->setHref('example.com');
+ $expected = [
+ 'title' => 'Nickie Works',
+ 'icon' => 'icon-contacts',
+ 'hyperlink' => 'example.com',
+ ];
+
+ $json = $this->action->jsonSerialize();
+
+ $this->assertEquals($expected, $json);
+ }
+
+}
diff --git a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php
new file mode 100644
index 00000000000..fa7c57bb409
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\ContactsStore;
+use OCP\Contacts\IManager;
+use PHPUnit_Framework_MockObject_MockObject;
+use Test\TestCase;
+
+class ContactsStoreTest extends TestCase {
+
+ /** @var ContactsStore */
+ private $contactsStore;
+
+ /** @var IManager|PHPUnit_Framework_MockObject_MockObject */
+ private $contactsManager;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->contactsManager = $this->createMock(IManager::class);
+
+ $this->contactsStore = new ContactsStore($this->contactsManager);
+ }
+
+ public function testGetContactsWithoutFilter() {
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN']))
+ ->willReturn([
+ [
+ 'id' => 123,
+ ],
+ [
+ 'id' => 567,
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts('');
+
+ $this->assertCount(2, $entries);
+ $this->assertEquals([
+ 'darren@roner.au'
+ ], $entries[1]->getEMailAddresses());
+ }
+
+ public function testGetContactsWithoutBinaryImage() {
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN']))
+ ->willReturn([
+ [
+ 'id' => 123,
+ ],
+ [
+ 'id' => 567,
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ 'PHOTO' => base64_encode('photophotophoto'),
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts('');
+
+ $this->assertCount(2, $entries);
+ $this->assertNull($entries[1]->getAvatar());
+ }
+
+ public function testGetContactsWithoutAvatarURI() {
+ $this->contactsManager->expects($this->once())
+ ->method('search')
+ ->with($this->equalTo(''), $this->equalTo(['FN']))
+ ->willReturn([
+ [
+ 'id' => 123,
+ ],
+ [
+ 'id' => 567,
+ 'FN' => 'Darren Roner',
+ 'EMAIL' => [
+ 'darren@roner.au'
+ ],
+ 'PHOTO' => 'VALUE=uri:https://photo',
+ ],
+ ]);
+
+ $entries = $this->contactsStore->getContacts('');
+
+ $this->assertCount(2, $entries);
+ $this->assertEquals('https://photo', $entries[1]->getAvatar());
+ }
+
+}
diff --git a/tests/lib/Contacts/ContactsMenu/EntryTest.php b/tests/lib/Contacts/ContactsMenu/EntryTest.php
new file mode 100644
index 00000000000..fa34916f47f
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/EntryTest.php
@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\Actions\LinkAction;
+use OC\Contacts\ContactsMenu\Entry;
+use OCP\Contacts\ContactsMenu\IAction;
+use Test\TestCase;
+
+class EntryTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var Entry */
+ private $entry;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->entry = new Entry();
+ }
+
+ public function testSetId() {
+ $this->entry->setId(123);
+ }
+
+ public function testSetGetFullName() {
+ $fn = 'Danette Chaille';
+ $this->assertEquals('', $this->entry->getFullName());
+ $this->entry->setFullName($fn);
+ $this->assertEquals($fn, $this->entry->getFullName());
+ }
+
+ public function testAddGetEMailAddresses() {
+ $this->assertEmpty($this->entry->getEMailAddresses());
+ $this->entry->addEMailAddress('user@example.com');
+ $this->assertEquals(['user@example.com'], $this->entry->getEMailAddresses());
+ }
+
+ public function testAddAndSortAction() {
+ // Three actions, two with equal priority
+ $action1 = new LinkAction();
+ $action2 = new LinkAction();
+ $action3 = new LinkAction();
+ $action1->setPriority(10);
+ $action1->setName('Bravo');
+
+ $action2->setPriority(0);
+ $action2->setName('Batman');
+
+ $action3->setPriority(10);
+ $action3->setName('Alfa');
+
+ $this->entry->addAction($action1);
+ $this->entry->addAction($action2);
+ $this->entry->addAction($action3);
+ $sorted = $this->entry->getActions();
+
+ $this->assertSame($action3, $sorted[0]);
+ $this->assertSame($action1, $sorted[1]);
+ $this->assertSame($action2, $sorted[2]);
+ }
+
+ public function testSetGetProperties() {
+ $props = [
+ 'prop1' => 123,
+ 'prop2' => 'string',
+ ];
+
+ $this->entry->setProperties($props);
+
+ $this->assertNull($this->entry->getProperty('doesntexist'));
+ $this->assertEquals(123, $this->entry->getProperty('prop1'));
+ $this->assertEquals('string', $this->entry->getProperty('prop2'));
+ }
+
+ public function testJsonSerialize() {
+ $expectedJson = [
+ 'id' => 123,
+ 'fullName' => 'Guadalupe Frisbey',
+ 'topAction' => null,
+ 'actions' => [],
+ 'lastMessage' => '',
+ ];
+
+ $this->entry->setId(123);
+ $this->entry->setFullName('Guadalupe Frisbey');
+ $json = $this->entry->jsonSerialize();
+
+ $this->assertEquals($expectedJson, $json);
+ }
+
+}
diff --git a/tests/lib/Contacts/ContactsMenu/ManagerTest.php b/tests/lib/Contacts/ContactsMenu/ManagerTest.php
new file mode 100644
index 00000000000..bcbcec96817
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/ManagerTest.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Contacts\ContactsMenu;
+
+use OC\Contacts\ContactsMenu\ActionProviderStore;
+use OC\Contacts\ContactsMenu\ContactsStore;
+use OC\Contacts\ContactsMenu\Manager;
+use OCP\App\IAppManager;
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\Contacts\ContactsMenu\IProvider;
+use PHPUnit_Framework_MockObject_MockObject;
+use Test\TestCase;
+
+class ManagerTest extends TestCase {
+
+ /** @var ContactsStore|PHPUnit_Framework_MockObject_MockObject */
+ private $contactsStore;
+
+ /** @var IAppManager|PHPUnit_Framework_MockObject_MockObject */
+ private $appManager;
+
+ /** @var ActionProviderStore|PHPUnit_Framework_MockObject_MockObject */
+ private $actionProviderStore;
+
+ /** @var Manager */
+ private $manager;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->contactsStore = $this->createMock(ContactsStore::class);
+ $this->actionProviderStore = $this->createMock(ActionProviderStore::class);
+ $this->appManager = $this->createMock(IAppManager::class);
+
+ $this->manager = new Manager($this->contactsStore, $this->actionProviderStore, $this->appManager);
+ }
+
+ private function generateTestEntries() {
+ $entries = [];
+ foreach (range('Z', 'A') as $char) {
+ $entry = $this->createMock(IEntry::class);
+ $entry->expects($this->any())
+ ->method('getFullName')
+ ->willReturn('Contact ' . $char);
+ $entries[] = $entry;
+ }
+ return $entries;
+ }
+
+ public function testGetFilteredEntries() {
+ $filter = 'con';
+ $user = 'user849';
+ $entries = $this->generateTestEntries();
+ $provider = $this->createMock(IProvider::class);
+ $this->contactsStore->expects($this->once())
+ ->method('getContacts')
+ ->with($filter)
+ ->willReturn($entries);
+ $this->actionProviderStore->expects($this->once())
+ ->method('getProviders')
+ ->willReturn([$provider]);
+ $provider->expects($this->exactly(25))
+ ->method('process');
+ $this->appManager->expects($this->once())
+ ->method('isEnabledForUser')
+ ->with($this->equalTo('contacts'), $user)
+ ->willReturn(false);
+ $expected = [
+ 'contacts' => array_slice($entries, 0, 25),
+ 'contactsAppEnabled' => false,
+ ];
+
+ $data = $this->manager->getEntries($user, $filter);
+
+ $this->assertEquals($expected, $data);
+ }
+
+}
diff --git a/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php b/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php
new file mode 100644
index 00000000000..af7b21a6485
--- /dev/null
+++ b/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Tests\Contacts\ContactsMenu\Providers;
+
+use OC\Contacts\ContactsMenu\Providers\EMailProvider;
+use OCP\Contacts\ContactsMenu\IActionFactory;
+use OCP\Contacts\ContactsMenu\IEntry;
+use OCP\Contacts\ContactsMenu\ILinkAction;
+use PHPUnit_Framework_MockObject_MockObject;
+use Test\TestCase;
+
+class EMailproviderTest extends TestCase {
+
+ /** @var IActionFactory|PHPUnit_Framework_MockObject_MockObject */
+ private $actionFactory;
+
+ /** @var EMailProvider */
+ private $provider;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->actionFactory = $this->createMock(IActionFactory::class);
+ $this->provider = new EMailProvider($this->actionFactory);
+ }
+
+ public function testProcess() {
+ $entry = $this->createMock(IEntry::class);
+ $action = $this->createMock(ILinkAction::class);
+
+ $entry->expects($this->once())
+ ->method('getEMailAddresses')
+ ->willReturn([
+ 'user@example.com',
+ ]);
+ $this->actionFactory->expects($this->once())
+ ->method('newEMailAction')
+ ->with($this->equalTo('icon-mail'), $this->equalTo('Mail'), $this->equalTo('user@example.com'))
+ ->willReturn($action);
+ $entry->expects($this->once())
+ ->method('addAction')
+ ->with($action);
+
+ $this->provider->process($entry);
+ }
+
+}