You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

405 lines
10 KiB

3 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Bart Visscher <bartv@thisnet.nl>
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
  10. * @author Lukas Reschke <lukas@statuscode.ch>
  11. * @author Morris Jobke <hey@morrisjobke.de>
  12. * @author Robin Appelman <robin@icewind.nl>
  13. * @author Robin McCorkell <robin@mccorkell.me.uk>
  14. * @author Roeland Jago Douma <roeland@famdouma.nl>
  15. * @author Vincent Petry <pvince81@owncloud.com>
  16. *
  17. * @license AGPL-3.0
  18. *
  19. * This code is free software: you can redistribute it and/or modify
  20. * it under the terms of the GNU Affero General Public License, version 3,
  21. * as published by the Free Software Foundation.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License, version 3,
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>
  30. *
  31. */
  32. namespace OC\Group;
  33. use OC\Hooks\PublicEmitter;
  34. use OCP\Group\Backend\ICountDisabledInGroup;
  35. use OCP\Group\Backend\IGetDisplayNameBackend;
  36. use OCP\Group\Backend\IHideFromCollaborationBackend;
  37. use OCP\Group\Backend\ISetDisplayNameBackend;
  38. use OCP\GroupInterface;
  39. use OCP\IGroup;
  40. use OCP\IUser;
  41. use OCP\IUserManager;
  42. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  43. use Symfony\Component\EventDispatcher\GenericEvent;
  44. class Group implements IGroup {
  45. /** @var null|string */
  46. protected $displayName;
  47. /** @var string */
  48. private $gid;
  49. /** @var \OC\User\User[] */
  50. private $users = [];
  51. /** @var bool */
  52. private $usersLoaded;
  53. /** @var Backend[] */
  54. private $backends;
  55. /** @var EventDispatcherInterface */
  56. private $dispatcher;
  57. /** @var \OC\User\Manager|IUserManager */
  58. private $userManager;
  59. /** @var PublicEmitter */
  60. private $emitter;
  61. /**
  62. * @param string $gid
  63. * @param Backend[] $backends
  64. * @param EventDispatcherInterface $dispatcher
  65. * @param IUserManager $userManager
  66. * @param PublicEmitter $emitter
  67. * @param string $displayName
  68. */
  69. public function __construct(string $gid, array $backends, EventDispatcherInterface $dispatcher, IUserManager $userManager, PublicEmitter $emitter = null, ?string $displayName = null) {
  70. $this->gid = $gid;
  71. $this->backends = $backends;
  72. $this->dispatcher = $dispatcher;
  73. $this->userManager = $userManager;
  74. $this->emitter = $emitter;
  75. $this->displayName = $displayName;
  76. }
  77. public function getGID() {
  78. return $this->gid;
  79. }
  80. public function getDisplayName() {
  81. if (is_null($this->displayName)) {
  82. foreach ($this->backends as $backend) {
  83. if ($backend instanceof IGetDisplayNameBackend) {
  84. $displayName = $backend->getDisplayName($this->gid);
  85. if (trim($displayName) !== '') {
  86. $this->displayName = $displayName;
  87. return $this->displayName;
  88. }
  89. }
  90. }
  91. return $this->gid;
  92. }
  93. return $this->displayName;
  94. }
  95. public function setDisplayName(string $displayName): bool {
  96. $displayName = trim($displayName);
  97. if ($displayName !== '') {
  98. foreach ($this->backends as $backend) {
  99. if (($backend instanceof ISetDisplayNameBackend)
  100. && $backend->setDisplayName($this->gid, $displayName)) {
  101. $this->displayName = $displayName;
  102. return true;
  103. }
  104. }
  105. }
  106. return false;
  107. }
  108. /**
  109. * get all users in the group
  110. *
  111. * @return \OC\User\User[]
  112. */
  113. public function getUsers() {
  114. if ($this->usersLoaded) {
  115. return $this->users;
  116. }
  117. $userIds = [];
  118. foreach ($this->backends as $backend) {
  119. $diff = array_diff(
  120. $backend->usersInGroup($this->gid),
  121. $userIds
  122. );
  123. if ($diff) {
  124. $userIds = array_merge($userIds, $diff);
  125. }
  126. }
  127. $this->users = $this->getVerifiedUsers($userIds);
  128. $this->usersLoaded = true;
  129. return $this->users;
  130. }
  131. /**
  132. * check if a user is in the group
  133. *
  134. * @param IUser $user
  135. * @return bool
  136. */
  137. public function inGroup(IUser $user) {
  138. if (isset($this->users[$user->getUID()])) {
  139. return true;
  140. }
  141. foreach ($this->backends as $backend) {
  142. if ($backend->inGroup($user->getUID(), $this->gid)) {
  143. $this->users[$user->getUID()] = $user;
  144. return true;
  145. }
  146. }
  147. return false;
  148. }
  149. /**
  150. * add a user to the group
  151. *
  152. * @param IUser $user
  153. */
  154. public function addUser(IUser $user) {
  155. if ($this->inGroup($user)) {
  156. return;
  157. }
  158. $this->dispatcher->dispatch(IGroup::class . '::preAddUser', new GenericEvent($this, [
  159. 'user' => $user,
  160. ]));
  161. if ($this->emitter) {
  162. $this->emitter->emit('\OC\Group', 'preAddUser', [$this, $user]);
  163. }
  164. foreach ($this->backends as $backend) {
  165. if ($backend->implementsActions(\OC\Group\Backend::ADD_TO_GROUP)) {
  166. $backend->addToGroup($user->getUID(), $this->gid);
  167. if ($this->users) {
  168. $this->users[$user->getUID()] = $user;
  169. }
  170. $this->dispatcher->dispatch(IGroup::class . '::postAddUser', new GenericEvent($this, [
  171. 'user' => $user,
  172. ]));
  173. if ($this->emitter) {
  174. $this->emitter->emit('\OC\Group', 'postAddUser', [$this, $user]);
  175. }
  176. return;
  177. }
  178. }
  179. }
  180. /**
  181. * remove a user from the group
  182. *
  183. * @param \OC\User\User $user
  184. */
  185. public function removeUser($user) {
  186. $result = false;
  187. $this->dispatcher->dispatch(IGroup::class . '::preRemoveUser', new GenericEvent($this, [
  188. 'user' => $user,
  189. ]));
  190. if ($this->emitter) {
  191. $this->emitter->emit('\OC\Group', 'preRemoveUser', [$this, $user]);
  192. }
  193. foreach ($this->backends as $backend) {
  194. if ($backend->implementsActions(\OC\Group\Backend::REMOVE_FROM_GOUP) and $backend->inGroup($user->getUID(), $this->gid)) {
  195. $backend->removeFromGroup($user->getUID(), $this->gid);
  196. $result = true;
  197. }
  198. }
  199. if ($result) {
  200. $this->dispatcher->dispatch(IGroup::class . '::postRemoveUser', new GenericEvent($this, [
  201. 'user' => $user,
  202. ]));
  203. if ($this->emitter) {
  204. $this->emitter->emit('\OC\Group', 'postRemoveUser', [$this, $user]);
  205. }
  206. if ($this->users) {
  207. foreach ($this->users as $index => $groupUser) {
  208. if ($groupUser->getUID() === $user->getUID()) {
  209. unset($this->users[$index]);
  210. return;
  211. }
  212. }
  213. }
  214. }
  215. }
  216. /**
  217. * search for users in the group by userid
  218. *
  219. * @param string $search
  220. * @param int $limit
  221. * @param int $offset
  222. * @return \OC\User\User[]
  223. */
  224. public function searchUsers($search, $limit = null, $offset = null) {
  225. $users = [];
  226. foreach ($this->backends as $backend) {
  227. $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
  228. $users += $this->getVerifiedUsers($userIds);
  229. if (!is_null($limit) and $limit <= 0) {
  230. return $users;
  231. }
  232. }
  233. return $users;
  234. }
  235. /**
  236. * returns the number of users matching the search string
  237. *
  238. * @param string $search
  239. * @return int|bool
  240. */
  241. public function count($search = '') {
  242. $users = false;
  243. foreach ($this->backends as $backend) {
  244. if ($backend->implementsActions(\OC\Group\Backend::COUNT_USERS)) {
  245. if ($users === false) {
  246. //we could directly add to a bool variable, but this would
  247. //be ugly
  248. $users = 0;
  249. }
  250. $users += $backend->countUsersInGroup($this->gid, $search);
  251. }
  252. }
  253. return $users;
  254. }
  255. /**
  256. * returns the number of disabled users
  257. *
  258. * @return int|bool
  259. */
  260. public function countDisabled() {
  261. $users = false;
  262. foreach ($this->backends as $backend) {
  263. if ($backend instanceof ICountDisabledInGroup) {
  264. if ($users === false) {
  265. //we could directly add to a bool variable, but this would
  266. //be ugly
  267. $users = 0;
  268. }
  269. $users += $backend->countDisabledInGroup($this->gid);
  270. }
  271. }
  272. return $users;
  273. }
  274. /**
  275. * search for users in the group by displayname
  276. *
  277. * @param string $search
  278. * @param int $limit
  279. * @param int $offset
  280. * @return \OC\User\User[]
  281. */
  282. public function searchDisplayName($search, $limit = null, $offset = null) {
  283. $users = [];
  284. foreach ($this->backends as $backend) {
  285. $userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
  286. $users = $this->getVerifiedUsers($userIds);
  287. if (!is_null($limit) and $limit <= 0) {
  288. return array_values($users);
  289. }
  290. }
  291. return array_values($users);
  292. }
  293. /**
  294. * delete the group
  295. *
  296. * @return bool
  297. */
  298. public function delete() {
  299. // Prevent users from deleting group admin
  300. if ($this->getGID() === 'admin') {
  301. return false;
  302. }
  303. $result = false;
  304. $this->dispatcher->dispatch(IGroup::class . '::preDelete', new GenericEvent($this));
  305. if ($this->emitter) {
  306. $this->emitter->emit('\OC\Group', 'preDelete', [$this]);
  307. }
  308. foreach ($this->backends as $backend) {
  309. if ($backend->implementsActions(\OC\Group\Backend::DELETE_GROUP)) {
  310. $result = true;
  311. $backend->deleteGroup($this->gid);
  312. }
  313. }
  314. if ($result) {
  315. $this->dispatcher->dispatch(IGroup::class . '::postDelete', new GenericEvent($this));
  316. if ($this->emitter) {
  317. $this->emitter->emit('\OC\Group', 'postDelete', [$this]);
  318. }
  319. }
  320. return $result;
  321. }
  322. /**
  323. * returns all the Users from an array that really exists
  324. * @param string[] $userIds an array containing user IDs
  325. * @return \OC\User\User[] an Array with the userId as Key and \OC\User\User as value
  326. */
  327. private function getVerifiedUsers($userIds) {
  328. if (!is_array($userIds)) {
  329. return [];
  330. }
  331. $users = [];
  332. foreach ($userIds as $userId) {
  333. $user = $this->userManager->get($userId);
  334. if (!is_null($user)) {
  335. $users[$userId] = $user;
  336. }
  337. }
  338. return $users;
  339. }
  340. /**
  341. * @return bool
  342. * @since 14.0.0
  343. */
  344. public function canRemoveUser() {
  345. foreach ($this->backends as $backend) {
  346. if ($backend->implementsActions(GroupInterface::REMOVE_FROM_GOUP)) {
  347. return true;
  348. }
  349. }
  350. return false;
  351. }
  352. /**
  353. * @return bool
  354. * @since 14.0.0
  355. */
  356. public function canAddUser() {
  357. foreach ($this->backends as $backend) {
  358. if ($backend->implementsActions(GroupInterface::ADD_TO_GROUP)) {
  359. return true;
  360. }
  361. }
  362. return false;
  363. }
  364. /**
  365. * @return bool
  366. * @since 16.0.0
  367. */
  368. public function hideFromCollaboration(): bool {
  369. return array_reduce($this->backends, function (bool $hide, GroupInterface $backend) {
  370. return $hide | ($backend instanceof IHideFromCollaborationBackend && $backend->hideGroup($this->gid));
  371. }, false);
  372. }
  373. }