migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Knut Ahlers <knut@ahlers.me>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Vincent Petry <vincent@nextcloud.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Group;
/**
* Abstract base class for user management
*/
abstract class Backend implements \OCP\GroupInterface {
/**
* error code for functions not provided by the group backend
*/
public const NOT_IMPLEMENTED = -501;
protected $possibleActions = [
self::CREATE_GROUP => 'createGroup',
self::DELETE_GROUP => 'deleteGroup',
self::ADD_TO_GROUP => 'addToGroup',
self::REMOVE_FROM_GOUP => 'removeFromGroup',
self::COUNT_USERS => 'countUsersInGroup',
self::GROUP_DETAILS => 'getGroupDetails',
self::IS_ADMIN => 'isAdmin',
];
/**
* Get all supported actions
* @return int bitwise-or'ed actions
*
* Returns the supported actions as int to be
* compared with \OC\Group\Backend::CREATE_GROUP etc.
*/
public function getSupportedActions() {
$actions = 0;
foreach ($this->possibleActions as $action => $methodName) {
if (method_exists($this, $methodName)) {
$actions |= $action;
}
}
return $actions;
}
/**
* Check if backend implements actions
* @param int $actions bitwise-or'ed actions
* @return bool
*
* Returns the supported actions as int to be
* compared with \OC\Group\Backend::CREATE_GROUP etc.
*/
public function implementsActions($actions) {
return (bool)($this->getSupportedActions() & $actions);
}
/**
* is user in group?
* @param string $uid uid of the user
* @param string $gid gid of the group
* @return bool
*
* Checks whether the user is member of a group or not.
*/
public function inGroup($uid, $gid) {
return in_array($gid, $this->getUserGroups($uid));
}
/**
* Get all groups a user belongs to
* @param string $uid Name of the user
* @return array an array of group names
*
* This function fetches all groups a user belongs to. It does not check
* if the user exists at all.
*/
public function getUserGroups($uid) {
return [];
}
/**
* get a list of all groups
* @param string $search
* @param int $limit
* @param int $offset
* @return array an array of group names
*
* Returns a list with all groups
*/
public function getGroups($search = '', $limit = -1, $offset = 0) {
return [];
}
/**
* check if a group exists
* @param string $gid
* @return bool
*/
public function groupExists($gid) {
return in_array($gid, $this->getGroups($gid, 1));
}
/**
* get a list of all users in a group
* @param string $gid
* @param string $search
* @param int $limit
* @param int $offset
* @return array<int,string> an array of user ids
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
return [];
}
}
+598
View File
@@ -0,0 +1,598 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Johannes Leuker <j.leuker@hosting.de>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Loki3000 <github@labcms.ru>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author tgrant <tom.grant760@gmail.com>
* @author Tom Grant <TomG736@users.noreply.github.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Group;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\User\LazyUser;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Group\Backend\ABackend;
use OCP\Group\Backend\IAddToGroupBackend;
use OCP\Group\Backend\IBatchMethodsBackend;
use OCP\Group\Backend\ICountDisabledInGroup;
use OCP\Group\Backend\ICountUsersBackend;
use OCP\Group\Backend\ICreateGroupBackend;
use OCP\Group\Backend\IDeleteGroupBackend;
use OCP\Group\Backend\IGetDisplayNameBackend;
use OCP\Group\Backend\IGroupDetailsBackend;
use OCP\Group\Backend\INamedBackend;
use OCP\Group\Backend\IRemoveFromGroupBackend;
use OCP\Group\Backend\ISearchableGroupBackend;
use OCP\Group\Backend\ISetDisplayNameBackend;
use OCP\IDBConnection;
use OCP\IUserManager;
/**
* Class for group management in a SQL Database (e.g. MySQL, SQLite)
*/
class Database extends ABackend implements
IAddToGroupBackend,
ICountDisabledInGroup,
ICountUsersBackend,
ICreateGroupBackend,
IDeleteGroupBackend,
IGetDisplayNameBackend,
IGroupDetailsBackend,
IRemoveFromGroupBackend,
ISetDisplayNameBackend,
ISearchableGroupBackend,
IBatchMethodsBackend,
INamedBackend {
/** @var array<string, array{gid: string, displayname: string}> */
private $groupCache = [];
private ?IDBConnection $dbConn;
/**
* \OC\Group\Database constructor.
*
* @param IDBConnection|null $dbConn
*/
public function __construct(IDBConnection $dbConn = null) {
$this->dbConn = $dbConn;
}
/**
* FIXME: This function should not be required!
*/
private function fixDI() {
if ($this->dbConn === null) {
$this->dbConn = \OC::$server->getDatabaseConnection();
}
}
/**
* Try to create a new group
* @param string $gid The name of the group to create
* @return bool
*
* Tries to create a new group. If the group name already exists, false will
* be returned.
*/
public function createGroup(string $gid): bool {
$this->fixDI();
try {
// Add group
$builder = $this->dbConn->getQueryBuilder();
$result = $builder->insert('groups')
->setValue('gid', $builder->createNamedParameter($gid))
->setValue('displayname', $builder->createNamedParameter($gid))
->execute();
} catch (UniqueConstraintViolationException $e) {
$result = 0;
}
// Add to cache
$this->groupCache[$gid] = [
'gid' => $gid,
'displayname' => $gid
];
return $result === 1;
}
/**
* delete a group
* @param string $gid gid of the group to delete
* @return bool
*
* Deletes a group and removes it from the group_user-table
*/
public function deleteGroup(string $gid): bool {
$this->fixDI();
// Delete the group
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('groups')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
->execute();
// Delete the group-user relation
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('group_user')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
->execute();
// Delete the group-groupadmin relation
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('group_admin')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
->execute();
// Delete from cache
unset($this->groupCache[$gid]);
return true;
}
/**
* is user in group?
* @param string $uid uid of the user
* @param string $gid gid of the group
* @return bool
*
* Checks whether the user is member of a group or not.
*/
public function inGroup($uid, $gid) {
$this->fixDI();
// check
$qb = $this->dbConn->getQueryBuilder();
$cursor = $qb->select('uid')
->from('group_user')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
->execute();
$result = $cursor->fetch();
$cursor->closeCursor();
return $result ? true : false;
}
/**
* Add a user to a group
* @param string $uid Name of the user to add to group
* @param string $gid Name of the group in which add the user
* @return bool
*
* Adds a user to a group.
*/
public function addToGroup(string $uid, string $gid): bool {
$this->fixDI();
// No duplicate entries!
if (!$this->inGroup($uid, $gid)) {
$qb = $this->dbConn->getQueryBuilder();
$qb->insert('group_user')
->setValue('uid', $qb->createNamedParameter($uid))
->setValue('gid', $qb->createNamedParameter($gid))
->execute();
return true;
} else {
return false;
}
}
/**
* Removes a user from a group
* @param string $uid Name of the user to remove from group
* @param string $gid Name of the group from which remove the user
* @return bool
*
* removes the user from a group.
*/
public function removeFromGroup(string $uid, string $gid): bool {
$this->fixDI();
$qb = $this->dbConn->getQueryBuilder();
$qb->delete('group_user')
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
->andWhere($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
->execute();
return true;
}
/**
* Get all groups a user belongs to
* @param string $uid Name of the user
* @return array an array of group names
*
* This function fetches all groups a user belongs to. It does not check
* if the user exists at all.
*/
public function getUserGroups($uid) {
//guests has empty or null $uid
if ($uid === null || $uid === '') {
return [];
}
$this->fixDI();
// No magic!
$qb = $this->dbConn->getQueryBuilder();
$cursor = $qb->select('gu.gid', 'g.displayname')
->from('group_user', 'gu')
->leftJoin('gu', 'groups', 'g', $qb->expr()->eq('gu.gid', 'g.gid'))
->where($qb->expr()->eq('uid', $qb->createNamedParameter($uid)))
->execute();
$groups = [];
while ($row = $cursor->fetch()) {
$groups[] = $row['gid'];
$this->groupCache[$row['gid']] = [
'gid' => $row['gid'],
'displayname' => $row['displayname'],
];
}
$cursor->closeCursor();
return $groups;
}
/**
* get a list of all groups
* @param string $search
* @param int $limit
* @param int $offset
* @return array an array of group names
*
* Returns a list with all groups
*/
public function getGroups(string $search = '', int $limit = -1, int $offset = 0) {
$this->fixDI();
$query = $this->dbConn->getQueryBuilder();
$query->select('gid', 'displayname')
->from('groups')
->orderBy('gid', 'ASC');
if ($search !== '') {
$query->where($query->expr()->iLike('gid', $query->createNamedParameter(
'%' . $this->dbConn->escapeLikeParameter($search) . '%'
)));
$query->orWhere($query->expr()->iLike('displayname', $query->createNamedParameter(
'%' . $this->dbConn->escapeLikeParameter($search) . '%'
)));
}
if ($limit > 0) {
$query->setMaxResults($limit);
}
if ($offset > 0) {
$query->setFirstResult($offset);
}
$result = $query->execute();
$groups = [];
while ($row = $result->fetch()) {
$this->groupCache[$row['gid']] = [
'displayname' => $row['displayname'],
'gid' => $row['gid'],
];
$groups[] = $row['gid'];
}
$result->closeCursor();
return $groups;
}
/**
* check if a group exists
* @param string $gid
* @return bool
*/
public function groupExists($gid) {
$this->fixDI();
// Check cache first
if (isset($this->groupCache[$gid])) {
return true;
}
$qb = $this->dbConn->getQueryBuilder();
$cursor = $qb->select('gid', 'displayname')
->from('groups')
->where($qb->expr()->eq('gid', $qb->createNamedParameter($gid)))
->execute();
$result = $cursor->fetch();
$cursor->closeCursor();
if ($result !== false) {
$this->groupCache[$gid] = [
'gid' => $gid,
'displayname' => $result['displayname'],
];
return true;
}
return false;
}
/**
* {@inheritdoc}
*/
public function groupsExists(array $gids): array {
$notFoundGids = [];
$existingGroups = [];
// In case the data is already locally accessible, not need to do SQL query
// or do a SQL query but with a smaller in clause
foreach ($gids as $gid) {
if (isset($this->groupCache[$gid])) {
$existingGroups[] = $gid;
} else {
$notFoundGids[] = $gid;
}
}
$qb = $this->dbConn->getQueryBuilder();
$qb->select('gid', 'displayname')
->from('groups')
->where($qb->expr()->in('gid', $qb->createParameter('ids')));
foreach (array_chunk($notFoundGids, 1000) as $chunk) {
$qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
$result = $qb->executeQuery();
while ($row = $result->fetch()) {
$this->groupCache[(string)$row['gid']] = [
'displayname' => (string)$row['displayname'],
'gid' => (string)$row['gid'],
];
$existingGroups[] = (string)$row['gid'];
}
$result->closeCursor();
}
return $existingGroups;
}
/**
* Get a list of all users in a group
* @param string $gid
* @param string $search
* @param int $limit
* @param int $offset
* @return array<int,string> an array of user ids
*/
public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0): array {
return array_values(array_map(fn ($user) => $user->getUid(), $this->searchInGroup($gid, $search, $limit, $offset)));
}
public function searchInGroup(string $gid, string $search = '', int $limit = -1, int $offset = 0): array {
$this->fixDI();
$query = $this->dbConn->getQueryBuilder();
$query->select('g.uid', 'u.displayname');
$query->from('group_user', 'g')
->where($query->expr()->eq('gid', $query->createNamedParameter($gid)))
->orderBy('g.uid', 'ASC');
$query->leftJoin('g', 'users', 'u', $query->expr()->eq('g.uid', 'u.uid'));
if ($search !== '') {
$query->leftJoin('u', 'preferences', 'p', $query->expr()->andX(
$query->expr()->eq('p.userid', 'u.uid'),
$query->expr()->eq('p.appid', $query->expr()->literal('settings')),
$query->expr()->eq('p.configkey', $query->expr()->literal('email'))
))
// sqlite doesn't like re-using a single named parameter here
->andWhere(
$query->expr()->orX(
$query->expr()->ilike('g.uid', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
$query->expr()->ilike('u.displayname', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%')),
$query->expr()->ilike('p.configvalue', $query->createNamedParameter('%' . $this->dbConn->escapeLikeParameter($search) . '%'))
)
)
->orderBy('u.uid_lower', 'ASC');
}
if ($limit !== -1) {
$query->setMaxResults($limit);
}
if ($offset !== 0) {
$query->setFirstResult($offset);
}
$result = $query->executeQuery();
$users = [];
$userManager = \OCP\Server::get(IUserManager::class);
while ($row = $result->fetch()) {
$users[$row['uid']] = new LazyUser($row['uid'], $userManager, $row['displayname'] ?? null);
}
$result->closeCursor();
return $users;
}
/**
* get the number of all users matching the search string in a group
* @param string $gid
* @param string $search
* @return int
*/
public function countUsersInGroup(string $gid, string $search = ''): int {
$this->fixDI();
$query = $this->dbConn->getQueryBuilder();
$query->select($query->func()->count('*', 'num_users'))
->from('group_user')
->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
if ($search !== '') {
$query->andWhere($query->expr()->like('uid', $query->createNamedParameter(
'%' . $this->dbConn->escapeLikeParameter($search) . '%'
)));
}
$result = $query->execute();
$count = $result->fetchOne();
$result->closeCursor();
if ($count !== false) {
$count = (int)$count;
} else {
$count = 0;
}
return $count;
}
/**
* get the number of disabled users in a group
*
* @param string $search
*
* @return int
*/
public function countDisabledInGroup(string $gid): int {
$this->fixDI();
$query = $this->dbConn->getQueryBuilder();
$query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')'))
->from('preferences', 'p')
->innerJoin('p', 'group_user', 'g', $query->expr()->eq('p.userid', 'g.uid'))
->where($query->expr()->eq('appid', $query->createNamedParameter('core')))
->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('enabled')))
->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('false'), IQueryBuilder::PARAM_STR))
->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR));
$result = $query->execute();
$count = $result->fetchOne();
$result->closeCursor();
if ($count !== false) {
$count = (int)$count;
} else {
$count = 0;
}
return $count;
}
public function getDisplayName(string $gid): string {
if (isset($this->groupCache[$gid])) {
$displayName = $this->groupCache[$gid]['displayname'];
if (isset($displayName) && trim($displayName) !== '') {
return $displayName;
}
}
$this->fixDI();
$query = $this->dbConn->getQueryBuilder();
$query->select('displayname')
->from('groups')
->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
$result = $query->execute();
$displayName = $result->fetchOne();
$result->closeCursor();
return (string) $displayName;
}
public function getGroupDetails(string $gid): array {
$displayName = $this->getDisplayName($gid);
if ($displayName !== '') {
return ['displayName' => $displayName];
}
return [];
}
/**
* {@inheritdoc}
*/
public function getGroupsDetails(array $gids): array {
$notFoundGids = [];
$details = [];
// In case the data is already locally accessible, not need to do SQL query
// or do a SQL query but with a smaller in clause
foreach ($gids as $gid) {
if (isset($this->groupCache[$gid])) {
$details[$gid] = ['displayName' => $this->groupCache[$gid]['displayname']];
} else {
$notFoundGids[] = $gid;
}
}
foreach (array_chunk($notFoundGids, 1000) as $chunk) {
$query = $this->dbConn->getQueryBuilder();
$query->select('gid', 'displayname')
->from('groups')
->where($query->expr()->in('gid', $query->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY)));
$result = $query->executeQuery();
while ($row = $result->fetch()) {
$details[(string)$row['gid']] = ['displayName' => (string)$row['displayname']];
$this->groupCache[(string)$row['gid']] = [
'displayname' => (string)$row['displayname'],
'gid' => (string)$row['gid'],
];
}
$result->closeCursor();
}
return $details;
}
public function setDisplayName(string $gid, string $displayName): bool {
if (!$this->groupExists($gid)) {
return false;
}
$this->fixDI();
$displayName = trim($displayName);
if ($displayName === '') {
$displayName = $gid;
}
$query = $this->dbConn->getQueryBuilder();
$query->update('groups')
->set('displayname', $query->createNamedParameter($displayName))
->where($query->expr()->eq('gid', $query->createNamedParameter($gid)));
$query->execute();
return true;
}
/**
* Backend name to be shown in group management
* @return string the name of the backend to be shown
* @since 21.0.0
*/
public function getBackendName(): string {
return 'Database';
}
}
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/**
* @copyright 2022 Anna Larch <anna.larch@gmx.net>
* @author Anna Larch <anna.larch@gmx.net>
*
* @license AGPL-3.0-or-later
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Group;
use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Group\Events\GroupChangedEvent;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IGroupManager;
/**
* Class that cache the relation Group ID -> Display name
*
* This saves fetching the group from the backend for "just" the display name
*/
class DisplayNameCache implements IEventListener {
private CappedMemoryCache $cache;
private ICache $memCache;
private IGroupManager $groupManager;
public function __construct(ICacheFactory $cacheFactory, IGroupManager $groupManager) {
$this->cache = new CappedMemoryCache();
$this->memCache = $cacheFactory->createDistributed('groupDisplayNameMappingCache');
$this->groupManager = $groupManager;
}
public function getDisplayName(string $groupId): ?string {
if (isset($this->cache[$groupId])) {
return $this->cache[$groupId];
}
$displayName = $this->memCache->get($groupId);
if ($displayName) {
$this->cache[$groupId] = $displayName;
return $displayName;
}
$group = $this->groupManager->get($groupId);
if ($group) {
$displayName = $group->getDisplayName();
} else {
$displayName = null;
}
$this->cache[$groupId] = $displayName;
$this->memCache->set($groupId, $displayName, 60 * 10); // 10 minutes
return $displayName;
}
public function clear(): void {
$this->cache = new CappedMemoryCache();
$this->memCache->clear();
}
public function handle(Event $event): void {
if ($event instanceof GroupChangedEvent && $event->getFeature() === 'displayName') {
$groupId = $event->getGroup()->getGID();
$newDisplayName = $event->getValue();
$this->cache[$groupId] = $newDisplayName;
$this->memCache->set($groupId, $newDisplayName, 60 * 10); // 10 minutes
}
if ($event instanceof GroupDeletedEvent) {
$groupId = $event->getGroup()->getGID();
unset($this->cache[$groupId]);
$this->memCache->remove($groupId);
}
}
}
+410
View File
@@ -0,0 +1,410 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Bart Visscher <bartv@thisnet.nl>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Johannes Leuker <j.leuker@hosting.de>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Vincent Petry <vincent@nextcloud.com>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Group;
use OC\Hooks\PublicEmitter;
use OC\User\LazyUser;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Backend\ICountDisabledInGroup;
use OCP\Group\Backend\IGetDisplayNameBackend;
use OCP\Group\Backend\IHideFromCollaborationBackend;
use OCP\Group\Backend\INamedBackend;
use OCP\Group\Backend\ISearchableGroupBackend;
use OCP\Group\Backend\ISetDisplayNameBackend;
use OCP\Group\Events\BeforeGroupChangedEvent;
use OCP\Group\Events\BeforeGroupDeletedEvent;
use OCP\Group\Events\BeforeUserAddedEvent;
use OCP\Group\Events\BeforeUserRemovedEvent;
use OCP\Group\Events\GroupChangedEvent;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\Group\Events\UserAddedEvent;
use OCP\Group\Events\UserRemovedEvent;
use OCP\GroupInterface;
use OCP\IGroup;
use OCP\IUser;
use OCP\IUserManager;
class Group implements IGroup {
/** @var null|string */
protected $displayName;
/** @var string */
private $gid;
/** @var \OC\User\User[] */
private $users = [];
/** @var bool */
private $usersLoaded;
/** @var Backend[] */
private $backends;
/** @var IEventDispatcher */
private $dispatcher;
/** @var \OC\User\Manager|IUserManager */
private $userManager;
/** @var PublicEmitter */
private $emitter;
public function __construct(string $gid, array $backends, IEventDispatcher $dispatcher, IUserManager $userManager, PublicEmitter $emitter = null, ?string $displayName = null) {
$this->gid = $gid;
$this->backends = $backends;
$this->dispatcher = $dispatcher;
$this->userManager = $userManager;
$this->emitter = $emitter;
$this->displayName = $displayName;
}
public function getGID(): string {
return $this->gid;
}
public function getDisplayName(): string {
if (is_null($this->displayName)) {
foreach ($this->backends as $backend) {
if ($backend instanceof IGetDisplayNameBackend) {
$displayName = $backend->getDisplayName($this->gid);
if (trim($displayName) !== '') {
$this->displayName = $displayName;
return $this->displayName;
}
}
}
return $this->gid;
}
return $this->displayName;
}
public function setDisplayName(string $displayName): bool {
$displayName = trim($displayName);
if ($displayName !== '') {
$this->dispatcher->dispatchTyped(new BeforeGroupChangedEvent($this, 'displayName', $displayName, $this->displayName));
foreach ($this->backends as $backend) {
if (($backend instanceof ISetDisplayNameBackend)
&& $backend->setDisplayName($this->gid, $displayName)) {
$this->displayName = $displayName;
$this->dispatcher->dispatchTyped(new GroupChangedEvent($this, 'displayName', $displayName, ''));
return true;
}
}
}
return false;
}
/**
* get all users in the group
*
* @return \OC\User\User[]
*/
public function getUsers(): array {
if ($this->usersLoaded) {
return $this->users;
}
$userIds = [];
foreach ($this->backends as $backend) {
$diff = array_diff(
$backend->usersInGroup($this->gid),
$userIds
);
if ($diff) {
$userIds = array_merge($userIds, $diff);
}
}
$this->users = $this->getVerifiedUsers($userIds);
$this->usersLoaded = true;
return $this->users;
}
/**
* check if a user is in the group
*
* @param IUser $user
* @return bool
*/
public function inGroup(IUser $user): bool {
if (isset($this->users[$user->getUID()])) {
return true;
}
foreach ($this->backends as $backend) {
if ($backend->inGroup($user->getUID(), $this->gid)) {
$this->users[$user->getUID()] = $user;
return true;
}
}
return false;
}
/**
* add a user to the group
*
* @param IUser $user
*/
public function addUser(IUser $user): void {
if ($this->inGroup($user)) {
return;
}
$this->dispatcher->dispatchTyped(new BeforeUserAddedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'preAddUser', [$this, $user]);
}
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC\Group\Backend::ADD_TO_GROUP)) {
$backend->addToGroup($user->getUID(), $this->gid);
if ($this->users) {
$this->users[$user->getUID()] = $user;
}
$this->dispatcher->dispatchTyped(new UserAddedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'postAddUser', [$this, $user]);
}
return;
}
}
}
/**
* remove a user from the group
*/
public function removeUser(IUser $user): void {
$result = false;
$this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'preRemoveUser', [$this, $user]);
}
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC\Group\Backend::REMOVE_FROM_GOUP) and $backend->inGroup($user->getUID(), $this->gid)) {
$backend->removeFromGroup($user->getUID(), $this->gid);
$result = true;
}
}
if ($result) {
$this->dispatcher->dispatchTyped(new UserRemovedEvent($this, $user));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'postRemoveUser', [$this, $user]);
}
if ($this->users) {
foreach ($this->users as $index => $groupUser) {
if ($groupUser->getUID() === $user->getUID()) {
unset($this->users[$index]);
return;
}
}
}
}
}
/**
* Search for users in the group by userid or display name
* @return IUser[]
*/
public function searchUsers(string $search, ?int $limit = null, ?int $offset = null): array {
$users = [];
foreach ($this->backends as $backend) {
if ($backend instanceof ISearchableGroupBackend) {
$users += $backend->searchInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0);
} else {
$userIds = $backend->usersInGroup($this->gid, $search, $limit ?? -1, $offset ?? 0);
$userManager = \OCP\Server::get(IUserManager::class);
foreach ($userIds as $userId) {
if (!isset($users[$userId])) {
$users[$userId] = new LazyUser($userId, $userManager);
}
}
}
if (!is_null($limit) and $limit <= 0) {
return $users;
}
}
return $users;
}
/**
* returns the number of users matching the search string
*
* @param string $search
* @return int|bool
*/
public function count($search = ''): int|bool {
$users = false;
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC\Group\Backend::COUNT_USERS)) {
if ($users === false) {
//we could directly add to a bool variable, but this would
//be ugly
$users = 0;
}
$users += $backend->countUsersInGroup($this->gid, $search);
}
}
return $users;
}
/**
* returns the number of disabled users
*
* @return int|bool
*/
public function countDisabled(): int|bool {
$users = false;
foreach ($this->backends as $backend) {
if ($backend instanceof ICountDisabledInGroup) {
if ($users === false) {
//we could directly add to a bool variable, but this would
//be ugly
$users = 0;
}
$users += $backend->countDisabledInGroup($this->gid);
}
}
return $users;
}
/**
* search for users in the group by displayname
*
* @param string $search
* @param int $limit
* @param int $offset
* @return IUser[]
* @deprecated 27.0.0 Use searchUsers instead (same implementation)
*/
public function searchDisplayName(string $search, int $limit = null, int $offset = null): array {
return $this->searchUsers($search, $limit, $offset);
}
/**
* Get the names of the backend classes the group is connected to
*
* @return string[]
*/
public function getBackendNames(): array {
$backends = [];
foreach ($this->backends as $backend) {
if ($backend instanceof INamedBackend) {
$backends[] = $backend->getBackendName();
} else {
$backends[] = get_class($backend);
}
}
return $backends;
}
/**
* Delete the group
*
* @return bool
*/
public function delete(): bool {
// Prevent users from deleting group admin
if ($this->getGID() === 'admin') {
return false;
}
$result = false;
$this->dispatcher->dispatchTyped(new BeforeGroupDeletedEvent($this));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'preDelete', [$this]);
}
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC\Group\Backend::DELETE_GROUP)) {
$result = $result || $backend->deleteGroup($this->gid);
}
}
if ($result) {
$this->dispatcher->dispatchTyped(new GroupDeletedEvent($this));
if ($this->emitter) {
$this->emitter->emit('\OC\Group', 'postDelete', [$this]);
}
}
return $result;
}
/**
* returns all the Users from an array that really exists
* @param string[] $userIds an array containing user IDs
* @return \OC\User\User[] an Array with the userId as Key and \OC\User\User as value
*/
private function getVerifiedUsers(array $userIds): array {
$users = [];
foreach ($userIds as $userId) {
$user = $this->userManager->get($userId);
if (!is_null($user)) {
$users[$userId] = $user;
}
}
return $users;
}
/**
* @return bool
* @since 14.0.0
*/
public function canRemoveUser(): bool {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(GroupInterface::REMOVE_FROM_GOUP)) {
return true;
}
}
return false;
}
/**
* @return bool
* @since 14.0.0
*/
public function canAddUser(): bool {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(GroupInterface::ADD_TO_GROUP)) {
return true;
}
}
return false;
}
/**
* @return bool
* @since 16.0.0
*/
public function hideFromCollaboration(): bool {
return array_reduce($this->backends, function (bool $hide, GroupInterface $backend) {
return $hide | ($backend instanceof IHideFromCollaborationBackend && $backend->hideGroup($this->gid));
}, false);
}
}
+492
View File
@@ -0,0 +1,492 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Bart Visscher <bartv@thisnet.nl>
* @author Bernhard Posselt <dev@bernhard-posselt.com>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Knut Ahlers <knut@ahlers.me>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author macjohnny <estebanmarin@gmx.ch>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
* @author Robin McCorkell <robin@mccorkell.me.uk>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Roman Kreisel <mail@romankreisel.de>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Vincent Petry <vincent@nextcloud.com>
* @author Vinicius Cubas Brand <vinicius@eita.org.br>
* @author voxsim "Simon Vocella"
* @author Carl Schwan <carl@carlschwan.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Group;
use OC\Hooks\PublicEmitter;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Backend\IBatchMethodsBackend;
use OCP\Group\Backend\IGroupDetailsBackend;
use OCP\Group\Events\BeforeGroupCreatedEvent;
use OCP\Group\Events\GroupCreatedEvent;
use OCP\GroupInterface;
use OCP\ICacheFactory;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
use Psr\Log\LoggerInterface;
/**
* Class Manager
*
* Hooks available in scope \OC\Group:
* - preAddUser(\OC\Group\Group $group, \OC\User\User $user)
* - postAddUser(\OC\Group\Group $group, \OC\User\User $user)
* - preRemoveUser(\OC\Group\Group $group, \OC\User\User $user)
* - postRemoveUser(\OC\Group\Group $group, \OC\User\User $user)
* - preDelete(\OC\Group\Group $group)
* - postDelete(\OC\Group\Group $group)
* - preCreate(string $groupId)
* - postCreate(\OC\Group\Group $group)
*
* @package OC\Group
*/
class Manager extends PublicEmitter implements IGroupManager {
/** @var GroupInterface[] */
private $backends = [];
/** @var \OC\User\Manager */
private $userManager;
private IEventDispatcher $dispatcher;
private LoggerInterface $logger;
/** @var array<string, IGroup> */
private $cachedGroups = [];
/** @var array<string, list<string>> */
private $cachedUserGroups = [];
/** @var \OC\SubAdmin */
private $subAdmin = null;
private DisplayNameCache $displayNameCache;
public function __construct(\OC\User\Manager $userManager,
IEventDispatcher $dispatcher,
LoggerInterface $logger,
ICacheFactory $cacheFactory) {
$this->userManager = $userManager;
$this->dispatcher = $dispatcher;
$this->logger = $logger;
$this->displayNameCache = new DisplayNameCache($cacheFactory, $this);
$cachedGroups = &$this->cachedGroups;
$cachedUserGroups = &$this->cachedUserGroups;
$this->listen('\OC\Group', 'postDelete', function ($group) use (&$cachedGroups, &$cachedUserGroups) {
/**
* @var \OC\Group\Group $group
*/
unset($cachedGroups[$group->getGID()]);
$cachedUserGroups = [];
});
$this->listen('\OC\Group', 'postAddUser', function ($group) use (&$cachedUserGroups) {
/**
* @var \OC\Group\Group $group
*/
$cachedUserGroups = [];
});
$this->listen('\OC\Group', 'postRemoveUser', function ($group) use (&$cachedUserGroups) {
/**
* @var \OC\Group\Group $group
*/
$cachedUserGroups = [];
});
}
/**
* Checks whether a given backend is used
*
* @param string $backendClass Full classname including complete namespace
* @return bool
*/
public function isBackendUsed($backendClass) {
$backendClass = strtolower(ltrim($backendClass, '\\'));
foreach ($this->backends as $backend) {
if (strtolower(get_class($backend)) === $backendClass) {
return true;
}
}
return false;
}
/**
* @param \OCP\GroupInterface $backend
*/
public function addBackend($backend) {
$this->backends[] = $backend;
$this->clearCaches();
}
public function clearBackends() {
$this->backends = [];
$this->clearCaches();
}
/**
* Get the active backends
*
* @return \OCP\GroupInterface[]
*/
public function getBackends() {
return $this->backends;
}
protected function clearCaches() {
$this->cachedGroups = [];
$this->cachedUserGroups = [];
}
/**
* @param string $gid
* @return IGroup|null
*/
public function get($gid) {
if (isset($this->cachedGroups[$gid])) {
return $this->cachedGroups[$gid];
}
return $this->getGroupObject($gid);
}
/**
* @param string $gid
* @param string $displayName
* @return \OCP\IGroup|null
*/
protected function getGroupObject($gid, $displayName = null) {
$backends = [];
foreach ($this->backends as $backend) {
if ($backend->implementsActions(Backend::GROUP_DETAILS)) {
$groupData = $backend->getGroupDetails($gid);
if (is_array($groupData) && !empty($groupData)) {
// take the display name from the last backend that has a non-null one
if (is_null($displayName) && isset($groupData['displayName'])) {
$displayName = $groupData['displayName'];
}
$backends[] = $backend;
}
} elseif ($backend->groupExists($gid)) {
$backends[] = $backend;
}
}
if (count($backends) === 0) {
return null;
}
/** @var GroupInterface[] $backends */
$this->cachedGroups[$gid] = new Group($gid, $backends, $this->dispatcher, $this->userManager, $this, $displayName);
return $this->cachedGroups[$gid];
}
/**
* @brief Batch method to create group objects
*
* @param list<string> $gids List of groupIds for which we want to create a IGroup object
* @param array<string, string> $displayNames Array containing already know display name for a groupId
* @return array<string, IGroup>
*/
protected function getGroupsObjects(array $gids, array $displayNames = []): array {
$backends = [];
$groups = [];
foreach ($gids as $gid) {
$backends[$gid] = [];
if (!isset($displayNames[$gid])) {
$displayNames[$gid] = null;
}
}
foreach ($this->backends as $backend) {
if ($backend instanceof IGroupDetailsBackend || $backend->implementsActions(GroupInterface::GROUP_DETAILS)) {
/** @var IGroupDetailsBackend $backend */
if ($backend instanceof IBatchMethodsBackend) {
$groupDatas = $backend->getGroupsDetails($gids);
} else {
$groupDatas = [];
foreach ($gids as $gid) {
$groupDatas[$gid] = $backend->getGroupDetails($gid);
}
}
foreach ($groupDatas as $gid => $groupData) {
if (!empty($groupData)) {
// take the display name from the last backend that has a non-null one
if (isset($groupData['displayName'])) {
$displayNames[$gid] = $groupData['displayName'];
}
$backends[$gid][] = $backend;
}
}
} else {
if ($backend instanceof IBatchMethodsBackend) {
$existingGroups = $backend->groupsExists($gids);
} else {
$existingGroups = array_filter($gids, fn (string $gid): bool => $backend->groupExists($gid));
}
foreach ($existingGroups as $group) {
$backends[$group][] = $backend;
}
}
}
foreach ($gids as $gid) {
if (count($backends[$gid]) === 0) {
continue;
}
$this->cachedGroups[$gid] = new Group($gid, $backends[$gid], $this->dispatcher, $this->userManager, $this, $displayNames[$gid]);
$groups[$gid] = $this->cachedGroups[$gid];
}
return $groups;
}
/**
* @param string $gid
* @return bool
*/
public function groupExists($gid) {
return $this->get($gid) instanceof IGroup;
}
/**
* @param string $gid
* @return IGroup|null
*/
public function createGroup($gid) {
if ($gid === '' || $gid === null) {
return null;
} elseif ($group = $this->get($gid)) {
return $group;
} else {
$this->dispatcher->dispatchTyped(new BeforeGroupCreatedEvent($gid));
$this->emit('\OC\Group', 'preCreate', [$gid]);
foreach ($this->backends as $backend) {
if ($backend->implementsActions(Backend::CREATE_GROUP)) {
if ($backend->createGroup($gid)) {
$group = $this->getGroupObject($gid);
$this->dispatcher->dispatchTyped(new GroupCreatedEvent($group));
$this->emit('\OC\Group', 'postCreate', [$group]);
return $group;
}
}
}
return null;
}
}
/**
* @param string $search
* @param ?int $limit
* @param ?int $offset
* @return \OC\Group\Group[]
*/
public function search(string $search, ?int $limit = null, ?int $offset = 0) {
$groups = [];
foreach ($this->backends as $backend) {
$groupIds = $backend->getGroups($search, $limit ?? -1, $offset ?? 0);
$newGroups = $this->getGroupsObjects($groupIds);
foreach ($newGroups as $groupId => $group) {
$groups[$groupId] = $group;
}
if (!is_null($limit) and $limit <= 0) {
return array_values($groups);
}
}
return array_values($groups);
}
/**
* @param IUser|null $user
* @return \OC\Group\Group[]
*/
public function getUserGroups(IUser $user = null) {
if (!$user instanceof IUser) {
return [];
}
return $this->getUserIdGroups($user->getUID());
}
/**
* @param string $uid the user id
* @return \OC\Group\Group[]
*/
public function getUserIdGroups(string $uid): array {
$groups = [];
foreach ($this->getUserIdGroupIds($uid) as $groupId) {
$aGroup = $this->get($groupId);
if ($aGroup instanceof IGroup) {
$groups[$groupId] = $aGroup;
} else {
$this->logger->debug('User "' . $uid . '" belongs to deleted group: "' . $groupId . '"', ['app' => 'core']);
}
}
return $groups;
}
/**
* Checks if a userId is in the admin group
*
* @param string $userId
* @return bool if admin
*/
public function isAdmin($userId) {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(Backend::IS_ADMIN) && $backend->isAdmin($userId)) {
return true;
}
}
return $this->isInGroup($userId, 'admin');
}
/**
* Checks if a userId is in a group
*
* @param string $userId
* @param string $group
* @return bool if in group
*/
public function isInGroup($userId, $group) {
return in_array($group, $this->getUserIdGroupIds($userId));
}
/**
* get a list of group ids for a user
*
* @param IUser $user
* @return string[] with group ids
*/
public function getUserGroupIds(IUser $user): array {
return $this->getUserIdGroupIds($user->getUID());
}
/**
* @param string $uid the user id
* @return string[]
*/
private function getUserIdGroupIds(string $uid): array {
if (!isset($this->cachedUserGroups[$uid])) {
$groups = [];
foreach ($this->backends as $backend) {
if ($groupIds = $backend->getUserGroups($uid)) {
$groups = array_merge($groups, $groupIds);
}
}
$this->cachedUserGroups[$uid] = $groups;
}
return $this->cachedUserGroups[$uid];
}
/**
* @param string $groupId
* @return ?string
*/
public function getDisplayName(string $groupId): ?string {
return $this->displayNameCache->getDisplayName($groupId);
}
/**
* get an array of groupid and displayName for a user
*
* @param IUser $user
* @return array ['displayName' => displayname]
*/
public function getUserGroupNames(IUser $user) {
return array_map(function ($group) {
return ['displayName' => $this->displayNameCache->getDisplayName($group->getGID())];
}, $this->getUserGroups($user));
}
/**
* get a list of all display names in a group
*
* @param string $gid
* @param string $search
* @param int $limit
* @param int $offset
* @return array an array of display names (value) and user ids (key)
*/
public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) {
$group = $this->get($gid);
if (is_null($group)) {
return [];
}
$search = trim($search);
$groupUsers = [];
if (!empty($search)) {
// only user backends have the capability to do a complex search for users
$searchOffset = 0;
$searchLimit = $limit * 100;
if ($limit === -1) {
$searchLimit = 500;
}
do {
$filteredUsers = $this->userManager->searchDisplayName($search, $searchLimit, $searchOffset);
foreach ($filteredUsers as $filteredUser) {
if ($group->inGroup($filteredUser)) {
$groupUsers[] = $filteredUser;
}
}
$searchOffset += $searchLimit;
} while (count($groupUsers) < $searchLimit + $offset && count($filteredUsers) >= $searchLimit);
if ($limit === -1) {
$groupUsers = array_slice($groupUsers, $offset);
} else {
$groupUsers = array_slice($groupUsers, $offset, $limit);
}
} else {
$groupUsers = $group->searchUsers('', $limit, $offset);
}
$matchingUsers = [];
foreach ($groupUsers as $groupUser) {
$matchingUsers[(string) $groupUser->getUID()] = $groupUser->getDisplayName();
}
return $matchingUsers;
}
/**
* @return \OC\SubAdmin
*/
public function getSubAdmin() {
if (!$this->subAdmin) {
$this->subAdmin = new \OC\SubAdmin(
$this->userManager,
$this,
\OC::$server->getDatabaseConnection(),
$this->dispatcher
);
}
return $this->subAdmin;
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Daniel Kesselberg <mail@danielkesselberg.de>
* @author Joas Schilling <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Lukas Reschke <lukas@statuscode.ch>
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Stephan Peijnik <speijnik@anexia-it.com>
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Group;
use OC\Group\Manager as GroupManager;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUserSession;
class MetaData {
public const SORT_NONE = 0;
public const SORT_USERCOUNT = 1; // May have performance issues on LDAP backends
public const SORT_GROUPNAME = 2;
/** @var string */
protected $user;
/** @var bool */
protected $isAdmin;
/** @var array */
protected $metaData = [];
/** @var GroupManager */
protected $groupManager;
/** @var int */
protected $sorting = self::SORT_NONE;
/** @var IUserSession */
protected $userSession;
/**
* @param string $user the uid of the current user
* @param bool $isAdmin whether the current users is an admin
*/
public function __construct(
string $user,
bool $isAdmin,
IGroupManager $groupManager,
IUserSession $userSession
) {
$this->user = $user;
$this->isAdmin = $isAdmin;
$this->groupManager = $groupManager;
$this->userSession = $userSession;
}
/**
* returns an array with meta data about all available groups
* the array is structured as follows:
* [0] array containing meta data about admin groups
* [1] array containing meta data about unprivileged groups
* @param string $groupSearch only effective when instance was created with
* isAdmin being true
* @param string $userSearch the pattern users are search for
*/
public function get(string $groupSearch = '', string $userSearch = ''): array {
$key = $groupSearch . '::' . $userSearch;
if (isset($this->metaData[$key])) {
return $this->metaData[$key];
}
$adminGroups = [];
$groups = [];
$sortGroupsIndex = 0;
$sortGroupsKeys = [];
$sortAdminGroupsIndex = 0;
$sortAdminGroupsKeys = [];
foreach ($this->getGroups($groupSearch) as $group) {
$groupMetaData = $this->generateGroupMetaData($group, $userSearch);
if (strtolower($group->getGID()) !== 'admin') {
$this->addEntry(
$groups,
$sortGroupsKeys,
$sortGroupsIndex,
$groupMetaData);
} else {
//admin group is hard coded to 'admin' for now. In future,
//backends may define admin groups too. Then the if statement
//has to be adjusted accordingly.
$this->addEntry(
$adminGroups,
$sortAdminGroupsKeys,
$sortAdminGroupsIndex,
$groupMetaData);
}
}
//whether sorting is necessary is will be checked in sort()
$this->sort($groups, $sortGroupsKeys);
$this->sort($adminGroups, $sortAdminGroupsKeys);
$this->metaData[$key] = [$adminGroups, $groups];
return $this->metaData[$key];
}
/**
* sets the sort mode, see SORT_* constants for supported modes
*/
public function setSorting(int $sortMode): void {
switch ($sortMode) {
case self::SORT_USERCOUNT:
case self::SORT_GROUPNAME:
$this->sorting = $sortMode;
break;
default:
$this->sorting = self::SORT_NONE;
}
}
/**
* adds an group entry to the resulting array
* @param array $entries the resulting array, by reference
* @param array $sortKeys the sort key array, by reference
* @param int $sortIndex the sort key index, by reference
* @param array $data the group's meta data as returned by generateGroupMetaData()
*/
private function addEntry(array &$entries, array &$sortKeys, int &$sortIndex, array $data): void {
$entries[] = $data;
if ($this->sorting === self::SORT_USERCOUNT) {
$sortKeys[$sortIndex] = $data['usercount'];
$sortIndex++;
} elseif ($this->sorting === self::SORT_GROUPNAME) {
$sortKeys[$sortIndex] = $data['name'];
$sortIndex++;
}
}
/**
* creates an array containing the group meta data
* @return array with the keys 'id', 'name', 'usercount' and 'disabled'
*/
private function generateGroupMetaData(IGroup $group, string $userSearch): array {
return [
'id' => $group->getGID(),
'name' => $group->getDisplayName(),
'usercount' => $this->sorting === self::SORT_USERCOUNT ? $group->count($userSearch) : 0,
'disabled' => $group->countDisabled(),
'canAdd' => $group->canAddUser(),
'canRemove' => $group->canRemoveUser(),
];
}
/**
* sorts the result array, if applicable
* @param array $entries the result array, by reference
* @param array $sortKeys the array containing the sort keys
*/
private function sort(array &$entries, array $sortKeys): void {
if ($this->sorting === self::SORT_USERCOUNT) {
array_multisort($sortKeys, SORT_DESC, $entries);
} elseif ($this->sorting === self::SORT_GROUPNAME) {
array_multisort($sortKeys, SORT_ASC, $entries);
}
}
/**
* returns the available groups
* @return IGroup[]
*/
public function getGroups(string $search = ''): array {
if ($this->isAdmin) {
return $this->groupManager->search($search);
} else {
$userObject = $this->userSession->getUser();
if ($userObject !== null) {
$groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($userObject);
} else {
$groups = [];
}
return $groups;
}
}
}