rework on the members management + UI + * to include Contacts

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
This commit is contained in:
Maxence Lange 2017-08-24 14:52:46 +02:00
parent 3a5ed4a196
commit 7125932e3c
20 changed files with 877 additions and 167 deletions

View File

@ -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',

0
contact.json Normal file
View File

View File

@ -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;
}

View File

@ -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);
},

View File

@ -80,6 +80,7 @@ var define = {
typeUser: 1,
typeGroup: 2,
typeMail: 3,
typeContact: 4,
levelMember: 1,
levelModerator: 4,
levelAdmin: 8,

View File

@ -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($('<option>', {
value: status,
text: t('circles', 'Mail Contact')
text: t('circles', 'Mail Address')
})).val(status);
}
if (userType === define.typeContact) {
statusSelect.append($('<option>', {
value: status,
text: t('circles', 'Contact')
})).val(status);
}

View File

@ -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'), '<b>$1</b>');
}
return display;
},
generateItemUser: function (search, value) {
return '<div class="result_top">' +
resultMembers.enhanceSearchResult(search, value.data.display) + '</div>' +
'<div class="result_bot">' + t('circles', 'Local User') + '</div>';
},
generateItemGroup: function (search, value) {
return '<div class="result_top">' +
resultMembers.enhanceSearchResult(search, value.data.display) + '</div>' +
'<div class="result_bot">' + t('circles', 'Local Group') + '</div>';
},
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 '<div class="result_top">' + display + '</div>' +
'<div class="result_bot">' + t('circles', 'Contact') + email + '</div>';
},
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('<div class="members_search" data-type="' +
value.type + '" data-ident="' + escapeHTML(value.ident) + '">' +
resultMembers.generateItemResult(currSearch, value) + '</div>');
});
$('.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(

View File

@ -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) {

View File

@ -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')

View File

@ -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;

View File

@ -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.

View File

@ -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]);
}
}

41
lib/ISearch.php Normal file
View File

@ -0,0 +1,41 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@pontapreta.net>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles;
use OCA\Circles\Model\SearchResult;
interface ISearch {
/**
* @param string $search
*
* @return SearchResult[]
*/
public function search($search);
}

View File

@ -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() {

134
lib/Model/SearchResult.php Normal file
View File

@ -0,0 +1,134 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@pontapreta.net>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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 <b>json_encode</b>,
* 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()
];
}
}

86
lib/Search/Contacts.php Normal file
View File

@ -0,0 +1,86 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@pontapreta.net>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@pontapreta.net>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}

55
lib/Search/LocalUsers.php Normal file
View File

@ -0,0 +1,55 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@pontapreta.net>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}

View File

@ -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()
);
}

View File

@ -0,0 +1,101 @@
<?php
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@pontapreta.net>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}