diff --git a/appinfo/routes.php b/appinfo/routes.php index df6e76b7..f19f0e8b 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -73,17 +73,17 @@ return [ 'name' => 'Federated#receiveFederatedDelivery', 'url' => '/v1/payload', 'verb' => 'PUT' ], - ['name' => 'Members#search', 'url' => '/v1/circles/{uniqueId}/members', 'verb' => 'GET'], + ['name' => 'Members#searchGlobal', 'url' => '/v1/globalsearch', 'verb' => 'GET'], +// [ +// 'name' => 'Members#importFromGroup', 'url' => '/v1/circles/{uniqueId}/groupmembers', +// 'verb' => 'PUT' +// ], [ - 'name' => 'Members#importFromGroup', 'url' => '/v1/circles/{uniqueId}/groupmembers', - 'verb' => 'PUT' - ], - [ - 'name' => 'Members#addLocalMember', 'url' => '/v1/circles/{uniqueId}/member', 'verb' => 'PUT' - ], - [ - 'name' => 'Members#addEmailAddress', 'url' => '/v1/circles/{uniqueId}/email', 'verb' => 'PUT' + 'name' => 'Members#addMember', 'url' => '/v1/circles/{uniqueId}/member', 'verb' => 'PUT' ], +// [ +// 'name' => 'Members#addEmailAddress', 'url' => '/v1/circles/{uniqueId}/email', 'verb' => 'PUT' +// ], [ 'name' => 'Members#removeMember', 'url' => '/v1/circles/{uniqueId}/member', 'verb' => 'DELETE', diff --git a/contact.json b/contact.json new file mode 100644 index 00000000..e69de29b diff --git a/css/navigation.css b/css/navigation.css index 4cb2d870..595f8cc4 100644 --- a/css/navigation.css +++ b/css/navigation.css @@ -241,7 +241,7 @@ div.circle .owner, div.circle .type, div.circle .resume { } #linkcircle, #addmember, #linkgroup, #joincircle, #leavecircle { - width: 250px; + width: 350px; } #members_search_result > #addmember, #groups_search_result > #linkgroup { @@ -261,7 +261,7 @@ div.circle .owner, div.circle .type, div.circle .resume { overflow-y: auto; position: absolute; top: 45px; - width: 260px; + width: 360px; } .icon-add-user { @@ -337,3 +337,13 @@ div.circle .owner, div.circle .type, div.circle .resume { margin: 15px; } +DIV.result_top { + font-size: 15px; +} + +DIV.result_bot { + font-size: 12px; + padding: 5px; + height: 10px; + font-style: italic; +} \ No newline at end of file diff --git a/js/circles.app.actions.js b/js/circles.app.actions.js index 962e8006..cf92b272 100644 --- a/js/circles.app.actions.js +++ b/js/circles.app.actions.js @@ -145,14 +145,7 @@ var actions = { } curr.searchUser = search; - - $.get(OC.linkToOCS('apps/files_sharing/api/v1', 1) + 'sharees', - { - format: 'json', - search: search, - perPage: 200, - itemType: 'principals' - }, resultMembers.searchMembersResult); + api.searchUsers(search, resultMembers.searchMembersResult); }, diff --git a/js/circles.app.js b/js/circles.app.js index b213efea..61ee8dcc 100644 --- a/js/circles.app.js +++ b/js/circles.app.js @@ -80,6 +80,7 @@ var define = { typeUser: 1, typeGroup: 2, typeMail: 3, + typeContact: 4, levelMember: 1, levelModerator: 4, levelAdmin: 8, diff --git a/js/circles.app.navigation.js b/js/circles.app.navigation.js index c621b5c6..9f667e59 100644 --- a/js/circles.app.navigation.js +++ b/js/circles.app.navigation.js @@ -86,16 +86,16 @@ var nav = { t('circles', 'Please confirm'), function (e) { if (e === true) { - api.addGroupMembers(curr.circle, elements.addMember.val(), - resultMembers.addGroupMembersResult); + api.addMember(curr.circle, elements.addMember.val(), define.typeGroup, + resultMembers.addMemberResult); } }); } else { if (actions.validateEmail(elements.addMember.val())) { - api.addEmail(curr.circle, elements.addMember.val(), - resultMembers.addEmailResult); + api.addMember(curr.circle, elements.addMember.val(), define.typeMail, + resultMembers.addMemberResult); } else { - api.addMember(curr.circle, elements.addMember.val(), + api.addMember(curr.circle, elements.addMember.val(), define.typeUser, resultMembers.addMemberResult); } } @@ -385,7 +385,7 @@ var nav = { var userId = $(this).attr('member-id'); var userType = Number($(this).attr('member-type')); - + if (userId === curr.userId && userType === define.typeUser) { $(this).find('td.username').css('font-weight', 'bold').css('font-style', 'italic'); $(this).css('background', '#e0e0e0'); @@ -430,7 +430,14 @@ var nav = { if (userType === define.typeMail) { statusSelect.append($('', { value: status, - text: t('circles', 'Mail Contact') + text: t('circles', 'Mail Address') + })).val(status); + } + + if (userType === define.typeContact) { + statusSelect.append($('', { + value: status, + text: t('circles', 'Contact') })).val(status); } diff --git a/js/circles.app.results.members.js b/js/circles.app.results.members.js index 58f6c59f..a8bb90d4 100644 --- a/js/circles.app.results.members.js +++ b/js/circles.app.results.members.js @@ -37,6 +37,58 @@ var resultMembers = { + generateItemResult: function (search, value) { + + switch (value.type) { + case define.typeUser: + return resultMembers.generateItemUser(search, value); + case define.typeGroup: + return resultMembers.generateItemGroup(search, value); + case define.typeContact: + return resultMembers.generateItemContact(search, value); + } + }, + + + enhanceSearchResult: function (search, display) { + display = escapeHTML(display); + if (search.length > 0) { + display = display.replace(new RegExp('(' + search + ')', 'gi'), '$1'); + } + + return display; + }, + + + generateItemUser: function (search, value) { + return '' + + resultMembers.enhanceSearchResult(search, value.data.display) + '' + + '' + t('circles', 'Local User') + ''; + }, + + generateItemGroup: function (search, value) { + return '' + + resultMembers.enhanceSearchResult(search, value.data.display) + '' + + '' + t('circles', 'Local Group') + ''; + }, + + generateItemContact: function (search, value) { + var display = resultMembers.enhanceSearchResult(search, value.data.display); + var email = resultMembers.enhanceSearchResult(search, value.data.email); + var org = resultMembers.enhanceSearchResult(search, value.data.organization); + if (email !== '') { + email = ' - ' + email; + } + + if (org !== '') { + display += ' (' + org + ')'; + } + + return '' + display + '' + + '' + t('circles', 'Contact') + email + ''; + }, + + searchMembersResult: function (response) { elements.membersSearchResult.children().remove(); @@ -46,18 +98,19 @@ var resultMembers = { return; } - elements.fillMembersSearch('users', response.ocs.data.exact.users, response.ocs.data.users); - elements.fillMembersSearch('groups', response.ocs.data.exact.groups, - response.ocs.data.groups); - - if (elements.membersSearchResult.children().length === 0) { - elements.membersSearchResult.fadeOut(0); - return; - } + var currSearch = response.search; + $.each(response.result, function (index, value) { + elements.membersSearchResult.append('' + + resultMembers.generateItemResult(currSearch, value) + ''); + }); $('.members_search').on('click', function () { - curr.searchUserSelected = $(this).attr('searchresult'); - if ($(this).attr('source') === 'groups') { + //curr.searchUserSelected = $(this).attr('searchresult'); + var ident = $(this).attr('data-ident'); + var type = $(this).attr('data-type'); + + if (Number(type) === define.typeGroup) { OC.dialogs.confirm( t('circles', @@ -65,21 +118,46 @@ var resultMembers = { t('circles', 'Please confirm'), function (e) { if (e === true) { - api.addGroupMembers(curr.circle, curr.searchUserSelected, - resultMembers.addGroupMembersResult); + api.addMember(curr.circle, ident, type, resultMembers.addMemberResult); } }); + } else { - api.addMember(curr.circle, curr.searchUserSelected, - resultMembers.addMemberResult); + api.addMember(curr.circle, ident, type, resultMembers.addMemberResult); } }); + + // elements.fillMembersSearch('users', response.ocs.data.exact.users, + // response.ocs.data.users); elements.fillMembersSearch('groups', + // response.ocs.data.exact.groups, response.ocs.data.groups); if + // (elements.membersSearchResult.children().length === 0) { + // elements.membersSearchResult.fadeOut(0); return; } $('.members_search').on('click', + // function () { curr.searchUserSelected = $(this).attr('searchresult'); if + // ($(this).attr('source') === 'groups') { OC.dialogs.confirm( t('circles', 'This + // operation will add/invite all members of the group to the circle'), t('circles', 'Please + // confirm'), function (e) { if (e === true) { api.addGroupMembers(curr.circle, + // curr.searchUserSelected, resultMembers.addGroupMembersResult); } }); } else { + // api.addMember(curr.circle, curr.searchUserSelected, resultMembers.addMemberResult); } }); elements.membersSearchResult.fadeIn(300); }, addMemberResult: function (result) { + console.log(JSON.stringify(result)); + + resultMembers.addMemberUserResult(result); + resultMembers.addMemberGroupResult(result); + resultMembers.addMemberMailResult(result); + // resultMembers.addMemberContactResult(result); + }, + + + addMemberUserResult: function (result) { + if (result.user_type !== define.typeUser) { + return; + } + if (curr.circleDetails.type === define.typeClosed) { resultMembers.inviteMemberResult(result); return; @@ -100,7 +178,10 @@ var resultMembers = { }, - addEmailResult: function (result) { + addMemberMailResult: function (result) { + if (result.user_type !== define.typeMail) { + return; + } if (result.status === 1) { OCA.notification.onSuccess( @@ -134,10 +215,14 @@ var resultMembers = { }, - addGroupMembersResult: function (result) { + addMemberGroupResult: function (result) { + + if (result.user_type !== define.typeGroup) { + return; + } if (curr.circleDetails.type === define.typeClosed) { - resultMembers.inviteGroupMembersResult(result); + resultMembers.inviteMemberGroupResult(result); return; } @@ -157,7 +242,7 @@ var resultMembers = { }, - inviteGroupMembersResult: function (result) { + inviteMemberGroupResult: function (result) { if (result.status === 1) { OCA.notification.onSuccess( diff --git a/js/circles.v1.js b/js/circles.v1.js index 58756728..74cc66b5 100644 --- a/js/circles.v1.js +++ b/js/circles.v1.js @@ -43,6 +43,22 @@ var self = this; + this.searchUsers = function (search, callback) { + + var result = {status: -1}; + $.ajax({ + method: 'GET', + url: OC.generateUrl('/apps/circles/v1/globalsearch'), + data: { + search: search + } + }).done(function (res) { + self.onCallback(callback, res); + }).fail(function () { + self.onCallback(callback, result); + }); + } + /** * API function to create a new Circle. @@ -100,13 +116,14 @@ }; - this.addMember = function (circleId, userId, callback) { + this.addMember = function (circleId, ident, type, callback) { var result = {status: -1}; $.ajax({ method: 'PUT', url: OC.generateUrl('/apps/circles/v1/circles/' + circleId + '/member'), data: { - name: userId + ident: ident, + type: type } }).done(function (res) { self.onCallback(callback, res); @@ -116,36 +133,36 @@ }; - this.addEmail = function (circleId, email, callback) { - var result = {status: -1}; - $.ajax({ - method: 'PUT', - url: OC.generateUrl('/apps/circles/v1/circles/' + circleId + '/email'), - data: { - email: email - } - }).done(function (res) { - self.onCallback(callback, res); - }).fail(function () { - self.onCallback(callback, result); - }); - }; + // this.addEmail = function (circleId, email, callback) { + // var result = {status: -1}; + // $.ajax({ + // method: 'PUT', + // url: OC.generateUrl('/apps/circles/v1/circles/' + circleId + '/email'), + // data: { + // email: email + // } + // }).done(function (res) { + // self.onCallback(callback, res); + // }).fail(function () { + // self.onCallback(callback, result); + // }); + // }; - this.addGroupMembers = function (circleId, groupId, callback) { - var result = {status: -1}; - $.ajax({ - method: 'PUT', - url: OC.generateUrl('/apps/circles/v1/circles/' + circleId + '/groupmembers'), - data: { - name: groupId - } - }).done(function (res) { - self.onCallback(callback, res); - }).fail(function () { - self.onCallback(callback, result); - }); - }; + // this.addGroupMembers = function (circleId, groupId, callback) { + // var result = {status: -1}; + // $.ajax({ + // method: 'PUT', + // url: OC.generateUrl('/apps/circles/v1/circles/' + circleId + '/groupmembers'), + // data: { + // name: groupId + // } + // }).done(function (res) { + // self.onCallback(callback, res); + // }).fail(function () { + // self.onCallback(callback, result); + // }); + // }; this.removeMember = function (circleId, userId, userType, callback) { diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 11d80bb4..e4975a14 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -47,6 +47,7 @@ use OCA\Circles\Service\GroupsService; use OCA\Circles\Service\MembersService; use OCA\Circles\Service\ConfigService; use OCA\Circles\Service\MiscService; +use OCA\Circles\Service\SearchService; use OCA\Circles\Service\SharesService; use OCP\AppFramework\App; use OCP\AppFramework\IAppContainer; @@ -110,6 +111,15 @@ class Application extends App { } ); + $container->registerService( + 'SearchService', function(IAppContainer $c) { + return new SearchService( + $c->query('L10N'), $c->query('UserManager'), $c->query('ConfigService'), + $c->query('MiscService') + ); + } + ); + $container->registerService( 'CirclesService', function(IAppContainer $c) { return new CirclesService( @@ -203,7 +213,7 @@ class Application extends App { 'NavigationController', function(IAppContainer $c) { return new NavigationController( $c->query('AppName'), $c->query('Request'), $c->query('UserId'), $c->query('L10N'), - $c->query('ConfigService'), $c->query('CirclesService'), + $c->query('ConfigService'), $c->query('CirclesService'), $c->query('SearchService'), $c->query('MembersService'), $c->query('GroupsService'), $c->query('SharesService'), $c->query('BroadcastService'), $c->query('FederatedService'), $c->query('MiscService') @@ -215,7 +225,7 @@ class Application extends App { 'CirclesController', function(IAppContainer $c) { return new CirclesController( $c->query('AppName'), $c->query('Request'), $c->query('UserId'), $c->query('L10N'), - $c->query('ConfigService'), $c->query('CirclesService'), + $c->query('ConfigService'), $c->query('CirclesService'), $c->query('SearchService'), $c->query('MembersService'), $c->query('GroupsService'), $c->query('SharesService'), $c->query('BroadcastService'), $c->query('FederatedService'), $c->query('MiscService') @@ -227,7 +237,7 @@ class Application extends App { 'MembersController', function(IAppContainer $c) { return new MembersController( $c->query('AppName'), $c->query('Request'), $c->query('UserId'), $c->query('L10N'), - $c->query('ConfigService'), $c->query('CirclesService'), + $c->query('ConfigService'), $c->query('CirclesService'), $c->query('SearchService'), $c->query('MembersService'), $c->query('GroupsService'), $c->query('SharesService'), $c->query('BroadcastService'), $c->query('FederatedService'), $c->query('MiscService') @@ -239,7 +249,7 @@ class Application extends App { 'GroupsController', function(IAppContainer $c) { return new GroupsController( $c->query('AppName'), $c->query('Request'), $c->query('UserId'), $c->query('L10N'), - $c->query('ConfigService'), $c->query('CirclesService'), + $c->query('ConfigService'), $c->query('CirclesService'), $c->query('SearchService'), $c->query('MembersService'), $c->query('GroupsService'), $c->query('SharesService'), $c->query('BroadcastService'), $c->query('FederatedService'), $c->query('MiscService') @@ -251,7 +261,7 @@ class Application extends App { 'SharesController', function(IAppContainer $c) { return new SharesController( $c->query('AppName'), $c->query('Request'), $c->query('UserId'), $c->query('L10N'), - $c->query('ConfigService'), $c->query('CirclesService'), + $c->query('ConfigService'), $c->query('CirclesService'), $c->query('SearchService'), $c->query('MembersService'), $c->query('GroupsService'), $c->query('SharesService'), $c->query('BroadcastService'), $c->query('FederatedService'), $c->query('MiscService') @@ -263,7 +273,7 @@ class Application extends App { 'FederatedController', function(IAppContainer $c) { return new FederatedController( $c->query('AppName'), $c->query('Request'), $c->query('UserId'), $c->query('L10N'), - $c->query('ConfigService'), $c->query('CirclesService'), + $c->query('ConfigService'), $c->query('CirclesService'), $c->query('SearchService'), $c->query('MembersService'), $c->query('GroupsService'), $c->query('SharesService'), $c->query('BroadcastService'), $c->query('FederatedService'), $c->query('MiscService') diff --git a/lib/Controller/BaseController.php b/lib/Controller/BaseController.php index 065ed798..1211b840 100644 --- a/lib/Controller/BaseController.php +++ b/lib/Controller/BaseController.php @@ -35,6 +35,7 @@ use OCA\Circles\Service\GroupsService; use OCA\Circles\Service\MembersService; use OCA\Circles\Service\MiscService; +use OCA\Circles\Service\SearchService; use OCA\Circles\Service\SharesService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; @@ -52,6 +53,9 @@ class BaseController extends Controller { /** @var ConfigService */ protected $configService; + /** @var SearchService */ + protected $searchService; + /** @var CirclesService */ protected $circlesService; @@ -83,6 +87,7 @@ class BaseController extends Controller { * @param IL10N $l10n * @param ConfigService $configService * @param CirclesService $circlesService + * @param SearchService $searchService * @param MembersService $membersService * @param GroupsService $groupsService * @param SharesService $sharesService @@ -97,6 +102,7 @@ class BaseController extends Controller { IL10N $l10n, ConfigService $configService, CirclesService $circlesService, + SearchService $searchService, MembersService $membersService, GroupsService $groupsService, SharesService $sharesService, @@ -110,6 +116,7 @@ class BaseController extends Controller { $this->l10n = $l10n; $this->configService = $configService; $this->circlesService = $circlesService; + $this->searchService = $searchService; $this->membersService = $membersService; $this->groupsService = $groupsService; $this->sharesService = $sharesService; diff --git a/lib/Controller/FederatedController.php b/lib/Controller/FederatedController.php index 2021d277..26e8d253 100644 --- a/lib/Controller/FederatedController.php +++ b/lib/Controller/FederatedController.php @@ -123,6 +123,7 @@ class FederatedController extends BaseController { * * Note: this function will close the request mid-run from the client but will still * running its process. + * * Called by a remote circle to broadcast a Share item, the function will save the item * in the database and broadcast it locally. A status response is sent to the remote to free * the remote process before starting to broadcast the item to other federated links. diff --git a/lib/Controller/MembersController.php b/lib/Controller/MembersController.php index b32ab839..336d4369 100644 --- a/lib/Controller/MembersController.php +++ b/lib/Controller/MembersController.php @@ -32,25 +32,64 @@ use OCP\AppFramework\Http\DataResponse; class MembersController extends BaseController { +// /** +// * @NoAdminRequired +// * @NoSubAdminRequired +// * +// * @param string $uniqueId +// * @param string $name +// * +// * @deprecated +// * +// * @return DataResponse +// */ +// public function addLocalMember($uniqueId, $name) { +// +// try { +// $data = $this->membersService->addLocalMember($uniqueId, $name); +// } catch (\Exception $e) { +// return $this->fail( +// [ +// 'circle_id' => $uniqueId, +// 'user_id' => $name, +// 'name' => $this->miscService->getDisplayName($name, true), +// 'error' => $e->getMessage() +// ] +// ); +// } +// +// return $this->success( +// [ +// 'circle_id' => $uniqueId, +// 'user_id' => $name, +// 'name' => $this->miscService->getDisplayName($name, true), +// 'members' => $data +// ] +// ); +// } + + /** * @NoAdminRequired * @NoSubAdminRequired * * @param string $uniqueId - * @param string $name + * @param $ident + * @param $type * * @return DataResponse */ - public function addLocalMember($uniqueId, $name) { + public function addMember($uniqueId, $ident, $type) { try { - $data = $this->membersService->addLocalMember($uniqueId, $name); + $data = $this->membersService->addMember($uniqueId, $ident, (int)$type); } catch (\Exception $e) { return $this->fail( [ 'circle_id' => $uniqueId, - 'user_id' => $name, - 'name' => $this->miscService->getDisplayName($name, true), + 'user_id' => $ident, + 'user_type' => (int)$type, + 'name' => $this->miscService->getDisplayName($ident, true), 'error' => $e->getMessage() ] ); @@ -59,47 +98,48 @@ class MembersController extends BaseController { return $this->success( [ 'circle_id' => $uniqueId, - 'user_id' => $name, - 'name' => $this->miscService->getDisplayName($name, true), + 'user_id' => $ident, + 'user_type' => (int)$type, + 'name' => $this->miscService->getDisplayName($ident, true), 'members' => $data ] ); } - /** - * @NoAdminRequired - * @NoSubAdminRequired - * - * @param string $uniqueId - * @param string $email - * - * @return DataResponse - */ - public function addEmailAddress($uniqueId, $email) { - - try { - $data = $this->membersService->addEmailAddress($uniqueId, $email); - } catch (\Exception $e) { - return $this->fail( - [ - 'circle_id' => $uniqueId, - 'email' => $email, - 'name' => $this->miscService->getDisplayName($email, true), - 'error' => $e->getMessage() - ] - ); - } - - return $this->success( - [ - 'circle_id' => $uniqueId, - 'email' => $email, - 'name' => $this->miscService->getDisplayName($email, true), - 'members' => $data - ] - ); - } +// /** +// * @NoAdminRequired +// * @NoSubAdminRequired +// * +// * @param string $uniqueId +// * @param string $email +// * +// * @return DataResponse +// */ +// public function addEmailAddress($uniqueId, $email) { +// +// try { +// $data = $this->membersService->addEmailAddress($uniqueId, $email); +// } catch (\Exception $e) { +// return $this->fail( +// [ +// 'circle_id' => $uniqueId, +// 'email' => $email, +// 'name' => $this->miscService->getDisplayName($email, true), +// 'error' => $e->getMessage() +// ] +// ); +// } +// +// return $this->success( +// [ +// 'circle_id' => $uniqueId, +// 'email' => $email, +// 'name' => $this->miscService->getDisplayName($email, true), +// 'members' => $data +// ] +// ); +// } /** @@ -218,5 +258,29 @@ class MembersController extends BaseController { } + /** + * @NoAdminRequired + * + * @param string $search + * + * @return DataResponse + */ + public function searchGlobal($search) { + + try { + $result = $this->searchService->searchGlobal($search); + } catch (\Exception $e) { + return + $this->fail( + [ + 'search' => $search, + 'error' => $e->getMessage() + ] + ); + } + + return $this->success(['search' => $search, 'result' => $result]); + } + } diff --git a/lib/ISearch.php b/lib/ISearch.php new file mode 100644 index 00000000..5cdfa833 --- /dev/null +++ b/lib/ISearch.php @@ -0,0 +1,41 @@ + + * @copyright 2017 + * @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 . + * + */ + +namespace OCA\Circles; + + +use OCA\Circles\Model\SearchResult; + +interface ISearch { + + /** + * @param string $search + * + * @return SearchResult[] + */ + public function search($search); + +} \ No newline at end of file diff --git a/lib/Model/BaseMember.php b/lib/Model/BaseMember.php index 969f8568..716c23ea 100644 --- a/lib/Model/BaseMember.php +++ b/lib/Model/BaseMember.php @@ -48,6 +48,7 @@ class BaseMember implements \JsonSerializable { const TYPE_USER = 1; const TYPE_GROUP = 2; const TYPE_MAIL = 3; + const TYPE_CONTACT = 4; /** @var string */ private $circleUniqueId; @@ -269,7 +270,7 @@ class BaseMember implements \JsonSerializable { public function jsonSerialize() { - return array( + return [ 'circle_id' => $this->getCircleId(), 'user_id' => $this->getUserId(), 'user_type' => $this->getType(), @@ -279,7 +280,7 @@ class BaseMember implements \JsonSerializable { 'status' => $this->getStatus(), 'note' => $this->getNote(), 'joined' => $this->getJoined() - ); + ]; } public function getLevelString() { diff --git a/lib/Model/SearchResult.php b/lib/Model/SearchResult.php new file mode 100644 index 00000000..e1bee72f --- /dev/null +++ b/lib/Model/SearchResult.php @@ -0,0 +1,134 @@ + + * @copyright 2017 + * @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 . + * + */ + +namespace OCA\Circles\Model; + + +use OCA\Circles\Exceptions\CircleTypeNotValidException; +use OCA\Circles\Exceptions\MemberAlreadyExistsException; +use OCA\Circles\Exceptions\MemberCantJoinCircleException; +use OCA\Circles\Exceptions\MemberDoesNotExistException; +use OCA\Circles\Exceptions\MemberIsBlockedException; +use OCA\Circles\Exceptions\MemberIsNotAdminException; +use OCA\Circles\Exceptions\MemberIsNotModeratorException; +use OCA\Circles\Exceptions\MemberIsNotOwnerException; +use OCA\Circles\Exceptions\MemberIsOwnerException; +use OCA\Circles\Exceptions\MemberTypeCantEditLevelException; +use OCA\Circles\Exceptions\ModeratorIsNotHighEnoughException; + +class SearchResult implements \JsonSerializable { + + /** @var string */ + private $ident; + + /** @var int */ + private $type; + + /** @var array */ + private $data = []; + + + /** + * SearchResult constructor. + * + * @param string $ident + * @param int $type + * @param array $data + */ + function __construct($ident = '', $type = 0, $data = []) { + $this->setIdent($ident); + $this->setType($type); + $this->setData($data); + } + + + /** + * @param string $ident + */ + public function setIdent($ident) { + $this->ident = $ident; + } + + /** + * @return string + */ + public function getIdent() { + return $this->ident; + } + + + /** + * @param int $type + */ + public function setType($type) { + $this->type = $type; + } + + /** + * @return int + */ + public function getType() { + return $this->type; + } + + + /** + * @param array $data + */ + public function setData($data) { + $this->data = $data; + } + + /** + * @return array + */ + public function getData() { + if (!key_exists('display', $this->data)) { + return ['display' => $this->getIdent()]; + } + + return $this->data; + } + + + /** + * Specify data which should be serialized to JSON + * + * @link http://php.net/manual/en/jsonserializable.jsonserialize.php + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource. + * @since 5.4.0 + */ + function jsonSerialize() { + + return [ + 'ident' => $this->getIdent(), + 'type' => $this->getType(), + 'data' => $this->getData() + ]; + + } +} \ No newline at end of file diff --git a/lib/Search/Contacts.php b/lib/Search/Contacts.php new file mode 100644 index 00000000..8d6ddf40 --- /dev/null +++ b/lib/Search/Contacts.php @@ -0,0 +1,86 @@ + + * @copyright 2017 + * @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 . + * + */ + +namespace OCA\Circles\Search; + +use OCA\Circles\ISearch; +use OCA\Circles\Model\Member; +use OCA\Circles\Model\SearchResult; + +class Contacts implements ISearch { + + /** + * {@inheritdoc} + */ + public function search($search) { + + $result = []; + $contactManager = \OC::$server->getContactsManager(); + + // Add 'ADR' to search also in the address + $contacts = $contactManager->search($search, ['FN', 'ORG', 'EMAIL']); + foreach ($contacts as $contact) { + if (key_exists('isLocalSystemBook', $contact) + && $contact['isLocalSystemBook'] === true) { + continue; + } + + $data = $this->generateDataArray($contact); + $result[] = new SearchResult($contact['UID'], Member::TYPE_CONTACT, $data); + } + + return $result; + } + + + /** + * @param array $contact + * + * @return array + */ + private function generateDataArray($contact) { + $data = [ + 'display' => '', + 'email' => '', + 'organization' => '' + ]; + + if (key_exists('EMAIL', $contact)) { + $data['display'] = $data['email'] = $contact['EMAIL']; + } + + if (key_exists('FN', $contact)) { + $data['display'] = $contact['FN']; + } + + if (key_exists('ORG', $contact)) { + $data['organization'] = $contact['ORG']; + } + + return $data; + } +} + diff --git a/lib/Search/LocalGroups.php b/lib/Search/LocalGroups.php new file mode 100644 index 00000000..7c2e3f20 --- /dev/null +++ b/lib/Search/LocalGroups.php @@ -0,0 +1,51 @@ + + * @copyright 2017 + * @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 . + * + */ + +namespace OCA\Circles\Search; + +use OCA\Circles\ISearch; +use OCA\Circles\Model\Member; +use OCA\Circles\Model\SearchResult; + +class LocalGroups implements ISearch { + + /** + * {@inheritdoc} + */ + public function search($search) { + + $result = []; + $groupManager = \OC::$server->getGroupManager(); + + $groups = $groupManager->search($search); + foreach ($groups as $group) { + $result[] = new SearchResult($group->getGID(), Member::TYPE_GROUP); + } + + return $result; + } + +} \ No newline at end of file diff --git a/lib/Search/LocalUsers.php b/lib/Search/LocalUsers.php new file mode 100644 index 00000000..bb46e7e0 --- /dev/null +++ b/lib/Search/LocalUsers.php @@ -0,0 +1,55 @@ + + * @copyright 2017 + * @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 . + * + */ + +namespace OCA\Circles\Search; + +use OCA\Circles\ISearch; +use OCA\Circles\Model\Member; +use OCA\Circles\Model\SearchResult; + +class LocalUsers implements ISearch { + + /** + * {@inheritdoc} + */ + public function search($search) { + + $result = []; + $userManager = \OC::$server->getUserManager(); + + $users = $userManager->search($search); + foreach ($users as $user) { + $result[] = + new SearchResult( + $user->getUID(), Member::TYPE_USER, ['display' => $user->getDisplayName()] + ); + } + + return $result; + } +} + + diff --git a/lib/Service/MembersService.php b/lib/Service/MembersService.php index 8336bcbc..9a30ff50 100644 --- a/lib/Service/MembersService.php +++ b/lib/Service/MembersService.php @@ -27,16 +27,14 @@ namespace OCA\Circles\Service; -use OC\User\NoUserException; use OCA\Circles\Db\CirclesRequest; use OCA\Circles\Db\MembersRequest; use OCA\Circles\Exceptions\CircleTypeNotValidException; use OCA\Circles\Exceptions\EmailAccountInvalidFormatException; use OCA\Circles\Exceptions\GroupDoesNotExistException; use OCA\Circles\Exceptions\MemberAlreadyExistsException; -use OCA\Circles\Exceptions\MemberDoesNotExistException; use OCA\Circles\Model\Circle; -use \OCA\Circles\Model\Member; +use OCA\Circles\Model\Member; use OCP\IL10N; use OCP\IUserManager; @@ -101,78 +99,126 @@ class MembersService { /** * @param string $circleUniqueId - * @param string $name + * @param $ident + * @param $type * * @return array * @throws \Exception */ - public function addLocalMember($circleUniqueId, $name) { + public function addMember($circleUniqueId, $ident, $type) { try { $circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId); $circle->getHigherViewer() ->hasToBeModerator(); + + if (!$this->addMemberMassively($circle, $type, $ident)) { + + $member = $this->membersRequest->getFreshNewMember($circleUniqueId, $ident, $type); + $member->hasToBeInviteAble(); + + $this->addMemberBasedOnItsType($circle, $member); + + $this->membersRequest->updateMember($member); + $this->eventsService->onMemberNew($circle, $member); + } } catch (\Exception $e) { throw $e; } - try { - $member = - $this->membersRequest->getFreshNewMember($circleUniqueId, $name, Member::TYPE_USER); - $member->hasToBeInviteAble(); - - } catch (\Exception $e) { - throw $e; - } - - $member->inviteToCircle($circle->getType()); - $this->membersRequest->updateMember($member); - - $this->eventsService->onMemberNew($circle, $member); - return $this->membersRequest->getMembers( $circle->getUniqueId(), $circle->getHigherViewer() ); } + private function addMemberMassively(Circle $circle, $type, $ident) { + + if ($type === Member::TYPE_GROUP) { + return $this->addGroupMembers($circle, $ident); + } + + return false; + } + + + private function addMemberBasedOnItsType(Circle $circle, Member &$member) { + $this->addLocalMember($circle, $member); + $this->addEmailAddress($member); + } + + /** - * @param string $circleUniqueId - * @param string $email + * @param Circle $circle + * @param Member $member * - * @return array * @throws \Exception */ - public function addEmailAddress($circleUniqueId, $email) { + public function addLocalMember(Circle $circle, Member $member) { - if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + if ($member->getType() !== Member::TYPE_USER) { + return; + } + + $member->inviteToCircle($circle->getType()); + } + + + /** + * @param Circle $circle + * @param string $groupId + * + * @return bool + * @throws \Exception + */ + private function addGroupMembers(Circle $circle, $groupId) { + + $group = \OC::$server->getGroupManager() + ->get($groupId); + if ($group === null) { + throw new GroupDoesNotExistException($this->l10n->t('This group does not exist')); + } + + foreach ($group->getUsers() as $user) { + try { + $member = + $this->membersRequest->getFreshNewMember( + $circle->getUniqueId(), $user->getUID(), Member::TYPE_USER + ); + $member->hasToBeInviteAble(); + + $member->inviteToCircle($circle->getType()); + $this->membersRequest->updateMember($member); + + $this->eventsService->onMemberNew($circle, $member); + } catch (MemberAlreadyExistsException $e) { + } catch (\Exception $e) { + throw $e; + } + } + + return true; + } + + + /** + * @param Member $member + * + * @throws \Exception + */ + public function addEmailAddress(Member $member) { + + if ($member->getType() !== Member::TYPE_MAIL) { + return; + } + + if (!filter_var($member->getUserId(), FILTER_VALIDATE_EMAIL)) { throw new EmailAccountInvalidFormatException( $this->l10n->t('Email format is not valid') ); } - try { - $circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId); - $circle->getHigherViewer() - ->hasToBeModerator(); - - $member = $this->membersRequest->getFreshNewMember( - $circleUniqueId, $email, Member::TYPE_MAIL - ); - $member->hasToBeInviteAble(); - - } catch (\Exception $e) { - throw $e; - } - $member->addMemberToCircle(); - $this->membersRequest->updateMember($member); - - $this->eventsService->onMemberNew($circle, $member); - - return $this->membersRequest->getMembers( - $circle->getUniqueId(), $circle->getHigherViewer() - ); } diff --git a/lib/Service/SearchService.php b/lib/Service/SearchService.php new file mode 100644 index 00000000..2fff3d30 --- /dev/null +++ b/lib/Service/SearchService.php @@ -0,0 +1,101 @@ + + * @copyright 2017 + * @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 . + * + */ + +namespace OCA\Circles\Service; + + +use OCA\Circles\ISearch; +use OCP\IL10N; +use OCP\IUserManager; + +class SearchService { + + /** @var IL10N */ + private $l10n; + + /** @var IUserManager */ + private $userManager; + + /** @var ConfigService */ + private $configService; + + /** @var MiscService */ + private $miscService; + + /** @var string[] */ + private $searchList; + + /** + * MembersService constructor. + * + * @param IL10N $l10n + * @param IUserManager $userManager + * @param ConfigService $configService + * @param MiscService $miscService + */ + public function __construct( + IL10N $l10n, IUserManager $userManager, ConfigService $configService, + MiscService $miscService + ) { + $this->l10n = $l10n; + $this->userManager = $userManager; + $this->configService = $configService; + $this->miscService = $miscService; + + $this->loadSearch(); + } + + + /** + * load list of search engine + */ + public function loadSearch() { + $this->searchList = [ + 'OCA\Circles\Search\LocalUsers', + 'OCA\Circles\Search\LocalGroups', + 'OCA\Circles\Search\Contacts' + ]; + } + + + public function searchGlobal($str) { + + $result = []; + foreach ($this->searchList as $container) { + $searcher = \OC::$server->query((string)$container); + + if (!($searcher instanceof ISearch)) { + $this->miscService->log('Search ' . $container . ' is not compatible exception'); + continue; + } + + $result = array_merge($result, $searcher->search($str)); + } + + return $result; + } + +} \ No newline at end of file