Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add contactsmenu popover
Browse files Browse the repository at this point in the history
Signed-off-by: Georg Ehrke <[email protected]>
georgehrke committed Apr 24, 2017
1 parent c852349 commit 5bd5623
Showing 12 changed files with 379 additions and 3 deletions.
17 changes: 17 additions & 0 deletions core/Controller/ContactsMenuController.php
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@

use OC\Contacts\ContactsMenu\Manager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\IUserSession;
@@ -59,4 +60,20 @@ public function index($filter = null) {
return $this->manager->getEntries($this->userSession->getUser(), $filter);
}

/**
* @NoAdminRequired
*
* @param integer $shareType
* @param string $shareWith
* @return JSONResponse
*/
public function findOne($shareType, $shareWith) {
$contact = $this->manager->findOne($this->userSession->getUser(), $shareType, $shareWith);

if ($contact) {
return $contact;
} else {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
}
}
15 changes: 15 additions & 0 deletions core/css/share.scss
Original file line number Diff line number Diff line change
@@ -87,6 +87,7 @@
list-style-type: none;
padding: 8px;
> li {
position: relative;
padding-top: 10px;
padding-bottom: 10px;
font-weight: bold;
@@ -103,6 +104,7 @@
padding: 3px 6px;
}
}

.shareOption {
white-space: nowrap;
display: inline-block;
@@ -185,6 +187,19 @@ a {
color: rgba($color-main-text, .4);
}

.contactsmenu-popover {
left: -8px;
right: auto;
padding: 3px 6px;
li.hidden {
display: none !important;
}
&:after {
left: 8px;
right: auto;
}
}

.popovermenu .datepicker {
margin-left: 35px;
}
1 change: 1 addition & 0 deletions core/js/core.json
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
"libraries": [
"jquery-showpassword.js",
"jquery.avatar.js",
"jquery.contactsmenu.js",
"placeholder.js"
],
"modules": [
107 changes: 107 additions & 0 deletions core/js/jquery.contactsmenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* Copyright (c) 2017 Georg Ehrke <[email protected]>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/

(function ($) {
var ENTRY = ''
+ '<li>'
+ ' <a href="{{hyperlink}}">'
+ ' {{#if icon}}<img src="{{icon}}">{{/if}}'
+ ' <span>{{title}}</span>'
+ ' </a>'
+ '</li>';

$.fn.contactsMenu = function(shareWith, shareType, appendTo) {
if (typeof(shareWith) !== 'undefined') {
shareWith = String(shareWith);
} else {
if (typeof(this.data('share-with')) !== 'undefined') {
shareWith = this.data('share-with');
}
}
if (typeof(shareType) !== 'undefined') {
shareType = Number(shareType);
} else {
if (typeof(this.data('share-type')) !== 'undefined') {
shareType = this.data('share-type');
}
}
if (typeof(appendTo) === 'undefined') {
appendTo = this;
}

// 0 - user, 4 - email, 6 - remote
var allowedTypes = [0, 4, 6];
if (allowedTypes.indexOf(shareType) === -1) {
return;
}

var $div = this;
appendTo.append('<div class="menu popovermenu bubble hidden contactsmenu-popover"><ul><li><a><span class="icon-loading-small"></span></a></li></ul></div>');
var $list = appendTo.find('div.contactsmenu-popover');
var url = OC.generateUrl('/contactsmenu/findOne');

$div.click(function() {
$list.show();

if ($list.hasClass('loaded')) {
return;
}

$list.addClass('loaded');
$.ajax(url, {
method: 'GET',
data: {
shareType: shareType,
shareWith: shareWith
}
}).then(function(data) {
$list.find('ul').find('li').addClass('hidden');

var actions;
if (!data.topAction) {
actions = [{
hyperlink: '#',
title: t('core', 'No action available')
}];
} else {
actions = [data.topAction].concat(data.actions);
}

actions.forEach(function(action) {
var template = Handlebars.compile(ENTRY);
$list.find('ul').append(template(action));
});

if (actions.length === 0) {

}
});
});

$(document).click(function(event) {
var clickedList = $.contains($list, event.target);
var clickedLi = $.contains($div, event.target);

$div.each(function() {
if ($(this).is(event.target)) {
clickedLi = true;
}
});

if (clickedList) {
return;
}

if (clickedLi) {
return;
}

$list.hide();

});
};
}(jQuery));
3 changes: 2 additions & 1 deletion core/js/merged-template-prepend.json
Original file line number Diff line number Diff line change
@@ -13,5 +13,6 @@
"mimetypelist.js",
"oc-backbone.js",
"placeholder.js",
"jquery.avatar.js"
"jquery.avatar.js",
"jquery.contactsmenu.js"
]
11 changes: 10 additions & 1 deletion core/js/sharedialogshareelistview.js
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
'{{#each sharees}}' +
'<li data-share-id="{{shareId}}" data-share-type="{{shareType}}" data-share-with="{{shareWith}}">' +
'<div class="avatar {{#if modSeed}}imageplaceholderseed{{/if}}" data-username="{{shareWith}}" data-displayname="{{shareWithDisplayName}}" {{#if modSeed}}data-seed="{{shareWith}} {{shareType}}"{{/if}}></div>' +
'<span class="has-tooltip username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' +
'<span class="username" title="{{shareWithTitle}}">{{shareWithDisplayName}}</span>' +
'<span class="sharingOptionsGroup">' +
'{{#if editPermissionPossible}}' +
'<span class="shareOption">' +
@@ -360,6 +360,15 @@
this.$('.has-tooltip').tooltip({
placement: 'bottom'
});

this.$('ul.shareWithList > li').each(function() {
var $this = $(this);

var shareWith = $this.data('share-with');
var shareType = $this.data('share-type');

$this.find('div.avatar, span.username').contactsMenu(shareWith, shareType, $this);
})
} else {
var permissionChangeShareId = parseInt(this._renderPermissionChange, 10);
var shareWithIndex = this.model.findShareWithIndex(permissionChangeShareId);
1 change: 1 addition & 0 deletions core/routes.php
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@
['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' => 'POST'],
['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'GET'],
],
'ocs' => [
['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'],
44 changes: 44 additions & 0 deletions lib/private/Contacts/ContactsMenu/ContactsStore.php
Original file line number Diff line number Diff line change
@@ -59,6 +59,50 @@ public function getContacts(IUser $user, $filter) {
});
}

/**
* @param IUser $user
* @param integer $shareType
* @param string $shareWith
* @return IEntry|null
*/
public function findOne(IUser $user, $shareType, $shareWith) {
switch($shareType) {
case 0:
case 6:
$filter = ['UID'];
break;
case 4:
$filter = ['EMAIL'];
break;
default:
return null;
}

$userId = $user->getUID();
$allContacts = $this->contactsManager->search($shareWith, $filter);
$contacts = array_filter($allContacts, function($contact) use ($userId) {
return $contact['UID'] !== $userId;
});
$match = null;

foreach ($contacts as $contact) {
if ($shareType === 4 && isset($contact['EMAIL'])) {
if (in_array($shareWith, $contact['EMAIL'])) {
$match = $contact;
break;
}
}
if ($shareType === 0 || $shareType === 6) {
if ($contact['UID'] === $shareWith && $contact['isLocalSystemBook'] === true) {
$match = $contact;
break;
}
}
}

return $match ? $this->contactArrayToEntry($match) : null;
}

/**
* @param array $contact
* @return Entry
17 changes: 16 additions & 1 deletion lib/private/Contacts/ContactsMenu/Manager.php
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ public function __construct(ContactsStore $store, ActionProviderStore $actionPro
}

/**
* @param string $user
* @param IUser $user
* @param string $filter
* @return array
*/
@@ -69,6 +69,21 @@ public function getEntries(IUser $user, $filter) {
];
}

/**
* @param IUser $user
* @param integer $shareType
* @param string $shareWith
* @return IEntry
*/
public function findOne(IUser $user, $shareType, $shareWith) {
$entry = $this->store->findOne($user, $shareType, $shareWith);
if ($entry) {
$this->processEntries([$entry], $user);
}

return $entry;
}

/**
* @param IEntry[] $entries
* @return IEntry[]
31 changes: 31 additions & 0 deletions tests/Core/Controller/ContactsMenuControllerTest.php
Original file line number Diff line number Diff line change
@@ -76,4 +76,35 @@ public function testIndex() {
$this->assertEquals($entries, $response);
}

public function testFindOne() {
$user = $this->createMock(IUser::class);
$entry = $this->createMock(IEntry::class);
$this->userSession->expects($this->once())
->method('getUser')
->willReturn($user);
$this->contactsManager->expects($this->once())
->method('findOne')
->with($this->equalTo($user), $this->equalTo(42), $this->equalTo('test-search-phrase'))
->willReturn($entry);

$response = $this->controller->findOne(42, 'test-search-phrase');

$this->assertEquals($entry, $response);
}

public function testFindOne404() {
$user = $this->createMock(IUser::class);
$this->userSession->expects($this->once())
->method('getUser')
->willReturn($user);
$this->contactsManager->expects($this->once())
->method('findOne')
->with($this->equalTo($user), $this->equalTo(42), $this->equalTo('test-search-phrase'))
->willReturn(null);

$response = $this->controller->findOne(42, 'test-search-phrase');

$this->assertEquals([], $response->getData());
$this->assertEquals(404, $response->getStatus());
}
}
90 changes: 90 additions & 0 deletions tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php
Original file line number Diff line number Diff line change
@@ -157,4 +157,94 @@ public function testGetContactsWithoutAvatarURI() {
$this->assertEquals('https://photo', $entries[1]->getAvatar());
}

public function testFindOneUser() {
$user = $this->createMock(IUser::class);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN']))
->willReturn([
[
'UID' => 123,
],
[
'UID' => 'a567',
'FN' => 'Darren Roner',
'EMAIL' => [
'darren@roner.au'
],
'isLocalSystemBook' => true
],
]);
$user->expects($this->once())
->method('getUID')
->willReturn('user123');

$entry = $this->contactsStore->findOne($user, 0, 'a567');

$this->assertEquals([
'darren@roner.au'
], $entry->getEMailAddresses());
}

public function testFindOneEMail() {
$user = $this->createMock(IUser::class);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN']))
->willReturn([
[
'UID' => 123,
],
[
'UID' => 'a567',
'FN' => 'Darren Roner',
'EMAIL' => [
'darren@roner.au'
]
],
]);
$user->expects($this->once())
->method('getUID')
->willReturn('user123');

$entry = $this->contactsStore->findOne($user, 4, 'darren@roner.au');

$this->assertEquals([
'darren@roner.au'
], $entry->getEMailAddresses());
}

public function testFindOneNotSupportedType() {
$user = $this->createMock(IUser::class);

$entry = $this->contactsStore->findOne($user, 42, 'darren@roner.au');

$this->assertEquals(null, $entry);
}

public function testFindOneNoMatches() {
$user = $this->createMock(IUser::class);
$this->contactsManager->expects($this->once())
->method('search')
->with($this->equalTo(''), $this->equalTo(['FN']))
->willReturn([
[
'UID' => 123,
],
[
'UID' => 'a567',
'FN' => 'Darren Roner',
'EMAIL' => [
'darren@roner.au123'
]
],
]);
$user->expects($this->once())
->method('getUID')
->willReturn('user123');

$entry = $this->contactsStore->findOne($user, 0, 'a567');

$this->assertEquals(null, $entry);
}
}
45 changes: 45 additions & 0 deletions tests/lib/Contacts/ContactsMenu/ManagerTest.php
Original file line number Diff line number Diff line change
@@ -99,4 +99,49 @@ public function testGetFilteredEntries() {
$this->assertEquals($expected, $data);
}

public function testFindOne() {
$shareTypeFilter = 42;
$shareWithFilter = 'foobar';

$user = $this->createMock(IUser::class);
$entry = current($this->generateTestEntries());
$provider = $this->createMock(IProvider::class);
$this->contactsStore->expects($this->once())
->method('findOne')
->with($user, $shareTypeFilter, $shareWithFilter)
->willReturn($entry);
$this->actionProviderStore->expects($this->once())
->method('getProviders')
->with($user)
->willReturn([$provider]);
$provider->expects($this->once())
->method('process');

$data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter);

$this->assertEquals($entry, $data);
}

public function testFindOne404() {
$shareTypeFilter = 42;
$shareWithFilter = 'foobar';

$user = $this->createMock(IUser::class);
$provider = $this->createMock(IProvider::class);
$this->contactsStore->expects($this->once())
->method('findOne')
->with($user, $shareTypeFilter, $shareWithFilter)
->willReturn(null);
$this->actionProviderStore->expects($this->never())
->method('getProviders')
->with($user)
->willReturn([$provider]);
$provider->expects($this->never())
->method('process');

$data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter);

$this->assertEquals(null, $data);
}

}

0 comments on commit 5bd5623

Please sign in to comment.