Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
workgroup.php
1<?php
2
4
5use Bitrix\Intranet\Integration\Templates\Bitrix24\ThemePicker;
6use Bitrix\Intranet\Internals\ThemeTable;
10use Bitrix\Main\Entity\Query;
18use Bitrix\Socialnetwork\EO_UserToGroup;
38use CExtranet;
39use Exception;
40
41class Workgroup extends Base
42{
43 private static function getAllowedSelectFields(): array
44 {
45 return [
46 'ID', 'ACTIVE', 'SUBJECT_ID', 'NAME', 'DESCRIPTION', 'KEYWORDS',
47 'CLOSED', 'VISIBLE', 'OPENED', 'PROJECT', 'LANDING',
48 'DATE_CREATE', 'DATE_UPDATE', 'DATE_ACTIVITY',
49 'IMAGE_ID', 'AVATAR_TYPE',
50 'OWNER_ID',
51 'NUMBER_OF_MEMBERS', 'NUMBER_OF_MODERATORS',
52 'INITIATE_PERMS',
53 'PROJECT_DATE_START', 'PROJECT_DATE_FINISH',
54 'SCRUM_OWNER_ID', 'SCRUM_MASTER_ID', 'SCRUM_SPRINT_DURATION', 'SCRUM_TASK_RESPONSIBLE',
55 ];
56 }
57
58 public function getAction(array $params = []): ?array
59 {
60 $groupId = (int)($params['groupId'] ?? 0);
61
62 if ($groupId <= 0)
63 {
64 $this->addEmptyGroupIdError();
65 return null;
66 }
67
68 $select = ($params['select'] ?? []);
69 $filter = ($params['filter'] ?? []);
70 $filter['ID'] = $groupId;
71
72 if (!\CSocNetUser::isCurrentUserModuleAdmin(SITE_ID, false))
73 {
74 $filter['CHECK_PERMISSIONS'] = $this->getCurrentUser()->getId();
75 }
76
77 $result = \CSocNetGroup::getList([], $filter, false, false, ['ID']);
78 if ($group = $result->fetch())
79 {
80 $groupItem = \Bitrix\Socialnetwork\Item\Workgroup::getById($group['ID']);
81 $groupFields = $groupItem->getFields();
82
83 if (in_array('AVATAR', $select, true))
84 {
85 $groupFields['AVATAR'] = File::getFileSource((int)$groupFields['IMAGE_ID'], 100, 100);
86 }
87 if (in_array('AVATAR_TYPES', $select, true))
88 {
89 $groupFields['AVATAR_TYPES'] = Helper\Workgroup::getAvatarTypes();
90 }
91 if (in_array('AVATAR_DATA', $select, true))
92 {
93 $imageId = (int) $groupFields['IMAGE_ID'];
94 $avatarType = $groupFields['AVATAR_TYPE'] ?? '';
95 $groupFields['AVATAR_DATA'] = $this->getAvatarData($imageId, $avatarType);
96 }
97 if (in_array('OWNER_DATA', $select, true))
98 {
99 $groupFields['OWNER_DATA'] = $this->getOwnerData($groupFields['OWNER_ID']);
100 }
101 if (in_array('SUBJECT_DATA', $select, true))
102 {
103 $groupFields['SUBJECT_DATA'] = $this->getSubjectData($groupFields['SUBJECT_ID']);
104 }
105 if (in_array('TAGS', $select, true))
106 {
107 $groupFields['TAGS'] = $this->getTags($groupId);
108 }
109 if (in_array('THEME_DATA', $select, true))
110 {
111 $groupFields['THEME_DATA'] = $this->getThemeData($groupId);
112 }
113 if (in_array('ACTIONS', $select, true))
114 {
115 $groupFields['ACTIONS'] = $this->getActions($groupId);
116 }
117 if (in_array('USER_DATA', $select, true))
118 {
119 $groupFields['USER_DATA'] = $this->getUserData($groupId);
120 }
121 if (in_array('DEPARTMENTS', $select, true))
122 {
123 $groupFields['DEPARTMENTS'] = $this->getDepartments($groupFields['UF_SG_DEPT']['VALUE']);
124 }
125 if (in_array('PIN', $select, true))
126 {
127 $groupFields['IS_PIN'] = $this->isPin($groupId, $this->getCurrentUser()->getId());
128 }
129 if (in_array('PRIVACY_TYPE', $select, true))
130 {
131 $groupFields['PRIVACY_CODE'] = Helper\Workgroup::getConfidentialityTypeCodeByParams([
132 'fields' => [
133 'OPENED' => $groupFields['OPENED'],
134 'VISIBLE' => $groupFields['VISIBLE'],
135 ],
136 ]);
137 }
138 if (in_array('LIST_OF_MEMBERS', $select, true))
139 {
140 $groupFields['LIST_OF_MEMBERS'] = $this->getListOfMembers(
141 $groupId,
142 $groupItem->getScrumMaster()
143 );
144 }
145
146 $needListOfAwaiting = in_array('LIST_OF_MEMBERS_AWAITING_INVITE', $select, true);
147 $needMembersList = in_array('GROUP_MEMBERS_LIST', $select, true);
148 if ($needListOfAwaiting || $needMembersList)
149 {
150 $permissions = Helper\Workgroup::getPermissions(
151 ['groupId' => $groupId],
152 );
153 }
154 if ($needListOfAwaiting)
155 {
156 $groupFields['LIST_OF_MEMBERS_AWAITING_INVITE'] = [];
157 if ($permissions['UserCanModifyGroup'] || $permissions['UserCanInitiate'])
158 {
159 $groupFields['LIST_OF_MEMBERS_AWAITING_INVITE'] = $this->getListOfAwaitingMembers($groupId);
160 }
161 }
162 if ($needMembersList)
163 {
164 $groupFields['GROUP_MEMBERS_LIST'] = [];
165 if ($permissions['UserCanModifyGroup'] || $permissions['UserCanInitiate'])
166 {
167 $membersManager = new MembersManager();
168 $groupFields['GROUP_MEMBERS_LIST'] = $membersManager->getGroupMembersList($groupId);
169 }
170 }
171
172 if (in_array('COUNTERS', $select, true))
173 {
174 $groupFields['COUNTERS'] = $this->getCounters($groupId);
175 }
176
177 if ($groupFields['NUMBER_OF_MEMBERS'])
178 {
179 $groupFields['NUMBER_OF_MEMBERS_PLURAL'] = Loc::getPluralForm($groupFields['NUMBER_OF_MEMBERS']);
180 }
181 if ($groupFields['PROJECT_DATE_START'] || $groupFields['PROJECT_DATE_FINISH'])
182 {
183 $culture = Context::getCurrent()->getCulture();
184 $format = $culture->getDayMonthFormat();
185
187 $dateStart = $groupFields['PROJECT_DATE_START'];
189 $dateFinish = $groupFields['PROJECT_DATE_FINISH'];
190
191 if ($dateStart)
192 {
193 $groupFields['FORMATTED_PROJECT_DATE_START'] = FormatDate(
194 $format,
195 MakeTimeStamp(DateTime::createFromTimestamp($dateStart->getTimestamp()))
196 );
197 }
198 if ($dateFinish)
199 {
200 $groupFields['FORMATTED_PROJECT_DATE_FINISH'] = FormatDate(
201 $format,
202 MakeTimeStamp(DateTime::createFromTimestamp($dateFinish->getTimestamp()))
203 );
204 }
205 }
206
207 if (
208 isset($params['mode'])
209 && $params['mode'] === 'mobile'
210 )
211 {
212 $additionalData = Helper\Workgroup::getAdditionalData([
213 'ids' => [ $groupId ],
214 'features' => ($params['features'] ?? []),
215 'mandatoryFeatures' => ($params['mandatoryFeatures'] ?? []),
216 'currentUserId' => (int)$this->getCurrentUser()->getId(),
217 ]);
218
219 $groupFields['ADDITIONAL_DATA'] = ($additionalData[$groupId] ?? []) ;
220 }
221
222 return $groupFields;
223 }
224
225 $this->addError(
226 new Error(
227 Loc::getMessage('SONET_CONTROLLER_WORKGROUP_NOT_FOUND'),
228 'SONET_CONTROLLER_WORKGROUP_NOT_FOUND'
229 )
230 );
231
232 return null;
233 }
234
235 public function listAction(
236 PageNavigation $pageNavigation,
237 array $filter = [],
238 array $select = [],
239 array $order = [],
240 array $params = []
241 )
242 {
243 if (
244 empty($select)
245 || !is_array($select)
246 )
247 {
248 $select = [ 'ID' ];
249 }
250
251 if (!in_array('ID', $select, true))
252 {
253 $select[] = 'ID';
254 }
255
256 $originalSelect = $select;
257
258 if (
259 $params['IS_ADMIN'] === 'Y'
260 && !\CSocNetUser::isCurrentUserModuleAdmin(SITE_ID, false)
261 )
262 {
263 unset($params['IS_ADMIN']);
264 }
265
266 if ($params['IS_ADMIN'] !== 'Y')
267 {
268 $filter['CHECK_PERMISSIONS'] = $this->getCurrentUser()->getId();
269 }
270
271 $extranetSiteId = \CSocNetLogRestService::getExtranetSiteId();
272
273 if (
274 $extranetSiteId
275 && $params['IS_ADMIN'] !== 'Y'
276 && \CSocNetLogRestService::getCurrentUserType() === 'extranet'
277 )
278 {
279 $filter['SITE_ID'] = $extranetSiteId;
280 }
281 else
282 {
283 $filter['SITE_ID'] = (string)($params['siteId'] ?? SITE_ID);
284 }
285
286 if (($key = array_search('AVATAR', $select, true)) !== false)
287 {
288 $select[] = 'IMAGE_ID';
289 $select[] = 'AVATAR_TYPE';
290 unset($select[$key]);
291 }
292
293 $workgroups = [];
294 $count = 0;
295
296 $queryIdFilter = [];
297
298 $res = \CSocNetGroup::getList([], $filter, false, false, [ 'ID' ]);
299 while ($groupFields = $res->fetch())
300 {
301 $queryIdFilter[] = (int)$groupFields['ID'];
302 }
303
304 if (!empty($queryIdFilter))
305 {
306 $select = $this->prepareSelect($select);
307
308 $query = WorkgroupTable::query();
309 $query
310 ->setSelect($select)
311 ->setOrder($order)
312 ->setOffset($pageNavigation->getOffset())
313 ->setLimit(($pageNavigation->getLimit()))
314 ->setFilter([
315 'ID' => $queryIdFilter,
316 ])
317 ->countTotal(true);
318
319 $res = $query->exec();
320
321 $avatarTypes = Helper\Workgroup::getAvatarTypes();
322
323 while ($groupFields = $res->fetch())
324 {
325 if (in_array('AVATAR', $originalSelect, true))
326 {
327 if ((int)$groupFields['IMAGE_ID'] > 0)
328 {
329 $groupFields['AVATAR'] = File::getFileSource((int)$groupFields['IMAGE_ID'], 100, 100);
330 }
331 elseif (
332 !empty($groupFields['AVATAR_TYPE'])
333 && isset($params['mode'])
334 && $params['mode'] === 'mobile'
335 )
336 {
337 $groupFields['AVATAR'] = $avatarTypes[$groupFields['AVATAR_TYPE']]['mobileUrl'];
338 }
339 else
340 {
341 $groupFields['AVATAR'] = '';
342 }
343 }
344
345 $workgroups[(int)$groupFields['ID']] = $groupFields;
346 }
347
348 $count = $res->getCount();
349 }
350
351 $ids = array_keys($workgroups);
352
353 if (
354 isset($params['mode'])
355 && $params['mode'] === 'mobile'
356 )
357 {
358 $additionalData = Helper\Workgroup::getAdditionalData([
359 'ids' => $ids,
360 'features' => ($params['features'] ?? []),
361 'mandatoryFeatures' => ($params['mandatoryFeatures'] ?? []),
362 'currentUserId' => (int)$this->getCurrentUser()->getId(),
363 ]);
364
365 foreach (array_keys($workgroups) as $id)
366 {
367 if (!isset($additionalData[$id]))
368 {
369 continue;
370 }
371
372 $workgroups[$id]['ADDITIONAL_DATA'] = ($additionalData[$id] ?? []) ;
373 }
374 }
375
376 $workgroups = $this->convertKeysToCamelCase($workgroups);
377
378 return new Engine\Response\DataType\Page('workgroups', array_values($workgroups), $count);
379 }
380
381 private function prepareSelect(array $select = []): array
382 {
383 return array_filter($select, static function ($key) {
384 return in_array(mb_strtoupper($key), static::getAllowedSelectFields(), true);
385 });
386 }
387
388 private function getAvatarData(int $imageId, string $avatarType): array
389 {
390 $avatarManager = new AvatarManager();
391
392 if ($imageId)
393 {
394 $avatarData = $avatarManager->getImageAvatar($imageId)->toArray();
395 }
396 else
397 {
398 $avatarData = $avatarManager->getIconAvatar($avatarType)->toArray();
399 }
400
401 return $avatarData;
402 }
403
404 private function getOwnerData(int $ownerId): array
405 {
406 $owner = UserTable::getList([
407 'select' => ['NAME', 'LAST_NAME', 'SECOND_NAME', 'LOGIN', 'PERSONAL_PHOTO'],
408 'filter' => ['ID' => $ownerId],
409 ])->fetch();
410
411 return [
412 'ID' => $ownerId,
413 'PHOTO' => ($owner['PERSONAL_PHOTO'] ? File::getFileSource($owner['PERSONAL_PHOTO']) : null),
414 'FORMATTED_NAME' => htmlspecialcharsback(
415 \CUser::FormatName(
416 \CSite::getNameFormat(),
417 [
418 'NAME' => $owner['NAME'],
419 'LAST_NAME' => $owner['LAST_NAME'],
420 'SECOND_NAME' => $owner['SECOND_NAME'],
421 'LOGIN' => $owner['LOGIN'],
422 ],
423 true
424 )
425 ),
426 ];
427 }
428
429 private function getSubjectData(int $subjectId): array
430 {
431 $subject = WorkgroupSubjectTable::getList([
432 'select' => ['NAME'],
433 'filter' => ['ID' => $subjectId],
434 ])->fetch();
435
436 return [
437 'ID' => $subjectId,
438 'NAME' => $subject['NAME'],
439 ];
440 }
441
442 private function getTags(int $groupId): array
443 {
444 $tags = WorkgroupTagTable::getList([
445 'select' => ['NAME'],
446 'filter' => ['GROUP_ID' => $groupId],
447 ])->fetchAll();
448
449 return array_map(
450 static function($tag) {
451 return htmlspecialcharsback($tag);
452 },
453 array_column($tags, 'NAME')
454 );
455 }
456
457 private function getThemeData(int $groupId): ?array
458 {
459 if (!Loader::includeModule('intranet'))
460 {
461 return [];
462 }
463
464 $themePicker = new ThemePicker(
465 SITE_TEMPLATE_ID,
466 false,
467 $this->getCurrentUser()->getId(),
468 ThemePicker::ENTITY_TYPE_SONET_GROUP,
469 $groupId
470 );
471
472 $themeUserId = false;
473 $themeId = $themePicker->getCurrentThemeId();
474 if ($themeId)
475 {
476 $res = ThemeTable::getList([
477 'select' => ['USER_ID'],
478 'filter' => [
479 '=ENTITY_TYPE' => $themePicker->getEntityType(),
480 'ENTITY_ID' => $themePicker->getEntityId(),
481 '=CONTEXT' => $themePicker->getContext(),
482 ],
483 ]);
484 if (($themeFields = $res->fetch()) && (int)$themeFields['USER_ID'] > 0)
485 {
486 $themeUserId = (int)$themeFields['USER_ID'];
487 }
488 }
489
490 return $themePicker->getTheme($themeId, $themeUserId);
491 }
492
493 private function getActions(int $groupId): array
494 {
495 $permissions = Helper\Workgroup::getPermissions(['groupId' => $groupId]);
496
497 return [
498 'EDIT' => $permissions['UserCanModifyGroup'],
499 'DELETE' => $permissions['UserCanModifyGroup'],
500 'INVITE' => $permissions['UserCanInitiate'],
501 'JOIN' => (
502 !$permissions['UserIsMember']
503 && !$permissions['UserRole']
504 ),
505 'LEAVE' => (
506 $permissions['UserIsMember']
507 && !$permissions['UserIsAutoMember']
508 && !$permissions['UserIsOwner']
509 && !$permissions['UserIsScrumMaster']
510 ),
511 ];
512 }
513
514 private function getUserData(int $groupId): array
515 {
516 $permissions = Helper\Workgroup::getPermissions(['groupId' => $groupId]);
517
518 return [
519 'ROLE' => $permissions['UserRole'],
520 'INITIATED_BY_TYPE' => $permissions['InitiatedByType'],
521 'IS_SUBSCRIBED' => (
522 in_array($permissions['UserRole'], UserToGroupTable::getRolesMember(), true)
523 && \CSocNetSubscription::isUserSubscribed($this->getCurrentUser()->getId(), 'SG' . $groupId)
524 ),
525 ];
526 }
527
528 private function getDepartments($ufDepartments): array
529 {
530 $departments = [];
531
532 if (
533 empty($ufDepartments)
534 || !is_array($ufDepartments)
535 || !Loader::includeModule('intranet')
536 )
537 {
538 return $departments;
539 }
540
541 $departmentsList = \CIntranetUtils::getDepartmentsData($ufDepartments);
542 if (empty($departmentsList))
543 {
544 return $departments;
545 }
546
547 foreach ($departmentsList as $id => $name)
548 {
549 if (($id = (int)$id) <= 0)
550 {
551 continue;
552 }
553
554 $departments[] = [
555 'ID' => $id,
556 'NAME' => $name,
557 ];
558 }
559
560 return $departments;
561 }
562
563 private function isPin(int $groupId, int $currentUserId, string $context = ''): bool
564 {
565 $query = new Query(WorkgroupPinTable::getEntity());
566
567 $query = $query
568 ->setSelect([
569 'ID',
570 'GROUP_ID',
571 'USER_ID',
572 ])
573 ->where('GROUP_ID', $groupId)
574 ->where('USER_ID', $currentUserId)
575 ;
576 if ($context === '')
577 {
578 $query = $query->where(Query::filter()
579 ->logic('or')
580 ->whereNull('CONTEXT')
581 ->where('CONTEXT', '')
582 );
583 }
584 else
585 {
586 $query = $query->where('CONTEXT', $context);
587 }
588
589 $pin = $query->setLimit(1)->exec()->fetchObject();
590
591 return (bool) $pin;
592 }
593
594 private function getListOfMembers(int $groupId, int $scrumMasterId): array
595 {
596 $list = [];
597
598 $records = UserToGroupTable::query()
599 ->setSelect([
600 'GROUP_ID',
601 'USER_ID',
602 'ROLE',
603 'INITIATED_BY_TYPE',
604 'AUTO_MEMBER',
605 'NAME' => 'USER.NAME',
606 'LAST_NAME' => 'USER.LAST_NAME',
607 'SECOND_NAME' => 'USER.SECOND_NAME',
608 'LOGIN' => 'USER.LOGIN',
609 'PERSONAL_PHOTO' => 'USER.PERSONAL_PHOTO',
610 ])
611 ->whereIn('GROUP_ID', $groupId)
612 ->whereIn('ROLE', UserToGroupTable::getRolesMember())
613 ->exec()->fetchCollection()
614 ;
615
616 $members = [];
617 $imageIdList = [];
618 foreach ($records as $record)
619 {
620 $user = $record->get('USER');
621 $imageIdList[$record->get('USER_ID')] = $user->get('PERSONAL_PHOTO');
622 $members[] = $record;
623 }
624 $imageIdList = array_filter(
625 $imageIdList,
626 static function ($id) {
627 return (int) $id > 0;
628 }
629 );
630 $avatars = $this->getUserAvatars($imageIdList);
631
632 foreach ($members as $member)
633 {
634 $memberId = (int) $member['USER_ID'];
635
636 $isOwner = ($member['ROLE'] === UserToGroupTable::ROLE_OWNER);
637 $isModerator = ($member['ROLE'] === UserToGroupTable::ROLE_MODERATOR);
638 $isScrumMaster = ($scrumMasterId === $memberId);
639
640 $list[] = [
641 'id' => $memberId,
642 'isOwner' => $isOwner,
643 'isModerator' => $isModerator,
644 'isScrumMaster' => $isScrumMaster,
645 'isAutoMember' => $member['AUTO_MEMBER'],
646 'photo' => ($avatars[($imageIdList[$memberId] ?? '')] ?? ''),
647 ];
648 }
649
650 return $list;
651 }
652
653 private function getListOfAwaitingMembers(int $groupId, int $limit = 10, int $offset = 0): array
654 {
655 $list = [];
656
657 $records = UserToGroupTable::query()
658 ->setSelect([
659 'GROUP_ID',
660 'USER_ID',
661 'ROLE',
662 'INITIATED_BY_TYPE',
663 'NAME' => 'USER.NAME',
664 'LAST_NAME' => 'USER.LAST_NAME',
665 'SECOND_NAME' => 'USER.SECOND_NAME',
666 'LOGIN' => 'USER.LOGIN',
667 'PERSONAL_PHOTO' => 'USER.PERSONAL_PHOTO',
668 ])
669 ->whereIn('GROUP_ID', $groupId)
670 ->where('INITIATED_BY_TYPE', UserToGroupTable::INITIATED_BY_USER)
671 ->where('ROLE', UserToGroupTable::ROLE_REQUEST)
672 ->setLimit($limit)
673 ->setOffset($offset)
674 ->exec()->fetchCollection()
675 ;
676
677 $members = [];
678 $imageIdList = [];
679 foreach ($records as $record)
680 {
681 $user = $record->get('USER');
682 $imageIdList[$record->get('USER_ID')] = $user->get('PERSONAL_PHOTO');
683
684 $members[] = $record;
685 }
686 $imageIdList = array_filter(
687 $imageIdList,
688 static function ($id) {
689 return (int) $id > 0;
690 }
691 );
692 $avatars = $this->getUserAvatars($imageIdList);
693
694 foreach ($members as $member)
695 {
696 $memberId = (int) $member['USER_ID'];
697
698 $userNameFormatted = \CUser::formatName(\CSite::getNameFormat(), [
699 'NAME' => $member->get('USER')->get('NAME'),
700 'LAST_NAME' => $member->get('USER')->get('LAST_NAME'),
701 'SECOND_NAME' => $member->get('USER')->get('SECOND_NAME'),
702 'LOGIN' => $member->get('USER')->get('LOGIN'),
703 ], ModuleManager::isModuleInstalled('intranet'));
704
705 $list[] = [
706 'id' => $memberId,
707 'name' => $userNameFormatted,
708 'photo' => ($avatars[($imageIdList[$memberId] ?? '')] ?? ''),
709 ];
710 }
711
712 return $list;
713 }
714
715 private function getCounters(int $groupId): array
716 {
717 $counters = [];
718
719 $counterProvider = Counter::getInstance($this->getCurrentUser()->getId());
720
721 $availableCounters = [
722 CounterDictionary::COUNTER_WORKGROUP_REQUESTS_OUT,
723 CounterDictionary::COUNTER_WORKGROUP_REQUESTS_IN,
724 ];
725 foreach ($availableCounters as $counter)
726 {
727 $counters[$counter] = $counterProvider->get($counter, $groupId)['all'];
728 }
729
730 return $counters;
731 }
732
733 public function updateAction(int $groupId, array $fields = []): ?bool
734 {
736 'groupId' => $groupId,
737 'checkAdminSession' => ($this->getScope() !== Controller::SCOPE_REST),
738 ]))
739 {
740 $this->addEmptyGroupIdError();
741 return null;
742 }
743
744 $whiteList = [
745 'NAME',
746 'KEYWORDS',
747 'VISIBLE',
748 'OPENED',
749 'EXTERNAL',
750 ];
751
752 foreach ($fields as $key => $value)
753 {
754 if (!in_array($key, $whiteList, true))
755 {
756 unset($fields[$key]);
757 }
758 }
759
760 if (
761 empty($fields)
762 )
763 {
764 $this->addError(new Error(
765 Loc::getMessage('SONET_CONTROLLER_WORKGROUP_ACTION_FAILED'),
766 'SONET_CONTROLLER_WORKGROUP_ACTION_FAILED')
767 );
768 return null;
769 }
770
771 try
772 {
773 $result = \CSocNetGroup::update($groupId, $fields);
774 }
775 catch (Exception $e)
776 {
777 $this->addError(new Error($e->getMessage(), $e->getCode()));
778 return null;
779 }
780
781 if (!$result)
782 {
783 $this->addError(new Error(
784 Loc::getMessage('SONET_CONTROLLER_WORKGROUP_ACTION_FAILED'),
785 'SONET_CONTROLLER_WORKGROUP_ACTION_FAILED')
786 );
787 return null;
788 }
789
790 return true;
791 }
792
793 public function deleteAction(int $groupId)
794 {
795 if (
797 'groupId' => $groupId,
798 'checkAdminSession' => ($this->getScope() !== Controller::SCOPE_REST),
799 ])
800 )
801 {
802 $this->addEmptyGroupIdError();
803
804 return null;
805 }
806
807 global $APPLICATION;
808
809 $deleteResult = \CSocNetGroup::Delete($groupId);
810 if (!$deleteResult && ($e = $APPLICATION->GetException()))
811 {
812 return $e->GetString();
813 }
814
815 return true;
816 }
817
818 public function getAvatarTypesAction(): array
819 {
820 return Helper\Workgroup::getAvatarTypes();
821 }
822
823 public function disconnectDepartmentsAction(int $groupId, array $departmentIds)
824 {
825 foreach ($departmentIds as $id)
826 {
827 Helper\Workgroup::disconnectDepartment([
828 'groupId' => $groupId,
829 'departmentId' => $id,
830 ]);
831 }
832 }
833
834 public function setFavoritesAction(array $params = []): ?array
835 {
836 $groupId = (int)($params['groupId'] ?? 0);
837 $getAdditionalResultData = (bool)($params['getAdditionalResultData'] ?? false);
838
839 if ($groupId <= 0)
840 {
841 $this->addEmptyGroupIdError();
842 return null;
843 }
844
845 if (!in_array($params['value'] ?? null, WorkgroupFavorites::AVAILABLE_VALUES, true))
846 {
847 $this->addIncorrectValueError();
848 return null;
849 }
850
851 try
852 {
854 'GROUP_ID' => $groupId,
855 'USER_ID' => $this->getCurrentUser()->getId(),
856 'VALUE' => $params['value'],
857 ]);
858 }
859 catch (Exception $e)
860 {
861 $this->addError(new Error($e->getMessage(), $e->getCode()));
862 return null;
863 }
864
865 if (!$res)
866 {
867 $this->addError(new Error(Loc::getMessage('SONET_CONTROLLER_WORKGROUP_ACTION_FAILED'), 'SONET_CONTROLLER_WORKGROUP_ACTION_FAILED'));
868 return null;
869 }
870
871 $result = [
872 'ID' => $groupId,
873 'RESULT' => $params['value'],
874 ];
875
876 if ($getAdditionalResultData)
877 {
878 $groupItem = \Bitrix\Socialnetwork\Item\Workgroup::getById($groupId);
879 $groupFields = $groupItem->getFields();
880 $groupUrlData = $groupItem->getGroupUrlData([
881 'USER_ID' => $this->getCurrentUser()->getId(),
882 ]);
883
884 $groupSiteList = [];
885 $resSite = WorkgroupSiteTable::getList([
886 'filter' => [
887 '=GROUP_ID' => $groupId
888 ],
889 'select' => [ 'SITE_ID' ],
890 ]);
891 while ($groupSite = $resSite->fetch())
892 {
893 $groupSiteList[] = $groupSite['SITE_ID'];
894 }
895
896 $result['NAME'] = $groupFields['NAME'];
897 $result['URL'] = $groupUrlData["URL"];
898 $result['EXTRANET'] = (
899 Loader::includeModule('extranet')
900 && CExtranet::isIntranetUser()
901 && in_array(CExtranet::getExtranetSiteId(), $groupSiteList, true)
902 ? 'Y'
903 : 'N'
904 );
905 }
906
907 $this->sendPush(PushEventDictionary::EVENT_WORKGROUP_FAVORITES_CHANGED, ['GROUP_ID' => $groupId]);
908
909 return $result;
910 }
911
912 public function setSubscriptionAction(array $params = []): ?array
913 {
914 $groupId = (int)($params['groupId'] ?? 0);
915 if ($groupId <= 0)
916 {
917 $this->addEmptyGroupIdError();
918 return null;
919 }
920
921 if (!in_array($params['value'] ?? null, Subscription::AVAILABLE_VALUES, true))
922 {
923 $this->addIncorrectValueError();
924 return null;
925 }
926
927 try
928 {
929 $result = Subscription::set([
930 'GROUP_ID' => $groupId,
931 'USER_ID' => $this->getCurrentUser()->getId(),
932 'VALUE' => $params['value'],
933 ]);
934 }
935 catch (Exception $e)
936 {
937 $this->addError(new Error($e->getMessage(), $e->getCode()));
938 return null;
939 }
940
941 $this->sendPush(PushEventDictionary::EVENT_WORKGROUP_SUBSCRIBE_CHANGED, ['GROUP_ID' => $groupId]);
942
943 return [
944 'RESULT' => $result ? 'Y' : 'N',
945 ];
946 }
947
948 public function createGroupAction(): array
949 {
950 $groupName = $this->getRequest()->get('groupName');
951 $viewMode = $this->getRequest()->get('viewMode');
952 $groupImage = $this->getRequest()->getFile('groupImage');
953 $avatarColor = $this->getRequest()->get('avatarColor');
954
956 {
957 return ['groupId' => 0];
958 }
959
960 $ownerId = $this->getCurrentUser()->getId();
961
962 $groupParams = [
963 'SITE_ID' => [SITE_ID],
964 'NAME' => $groupName,
965 'SUBJECT_ID' => $this->getDefaultSubjectId(),
966 'INITIATE_PERMS' => SONET_ROLES_USER,
967 'SPAM_PERMS' => SONET_ROLES_USER,
968 'VISIBLE' => $viewMode !== 'secret' ? 'Y' : 'N',
969 'OPENED' => $viewMode === 'open' ? 'Y' : 'N',
970 ];
971
972 if (is_array($groupImage))
973 {
974 try
975 {
976 $avatarManager = new AvatarManager();
977 $result = $avatarManager->loadAvatar($groupImage);
978 $groupParams['IMAGE_ID'] = $avatarManager->getAvatar($result['fileId']);
979 }
980 catch (\RuntimeException)
981 {
982 unset($groupParams['IMAGE_ID']);
983 }
984 }
985
986 if (!isset($groupParams['IMAGE_ID']))
987 {
988 $groupParams['AVATAR_TYPE'] = $this->getColoredDefaultAvatar($avatarColor);
989 }
990
991 $groupId = (int)\CSocNetGroup::createGroup($ownerId, $groupParams);
992
993 global $APPLICATION;
994 if ($e = $APPLICATION->GetException())
995 {
996 $this->addError(new Error($e->msg, $e->id));
997 return [];
998 }
999
1000 $this->setDefaultGroupFeatures($groupId);
1001
1002 return [
1003 'groupId' => $groupId,
1004 ];
1005 }
1006
1007 private function getColoredDefaultAvatar(string $color): string
1008 {
1009 if (in_array($color, Helper\Workgroup::getAvatarColors(), true))
1010 {
1011 return "space_$color";
1012 }
1013
1014 return array_rand(Helper\Workgroup::getColoredAvatarTypes());
1015 }
1016
1017 private function setDefaultGroupFeatures(int $groupId): void
1018 {
1019 $allowedFeatures = array_keys(\CSocNetAllowed::getAllowedFeatures());
1020 $inactiveFeaturesList = ['forum', 'photo', 'search', 'group_lists', 'wiki'];
1021
1022 $features = [];
1023 foreach ($allowedFeatures as $featureName)
1024 {
1025 $features[$featureName] = !in_array($featureName, $inactiveFeaturesList, true);
1026 }
1027
1028 foreach ($features as $featureName => $isActive)
1029 {
1030 \CSocNetFeatures::setFeature(
1031 SONET_ENTITY_GROUP,
1032 $groupId,
1033 $featureName,
1034 $isActive,
1035 );
1036 }
1037 }
1038
1039 private function getDefaultSubjectId(): int
1040 {
1041 $subject = \CSocNetGroupSubject::GetList(
1042 ["SORT"=>"ASC", "NAME" => "ASC"],
1043 ["SITE_ID" => SITE_ID],
1044 false,
1045 false,
1046 ["ID", "NAME"],
1047 )->fetch();
1048
1049 return (int)($subject['ID'] ?? 0);
1050 }
1051
1052 public function getCanCreateAction(): bool
1053 {
1054 return Helper\Workgroup\Access::canCreate([
1055 'checkAdminSession' => ($this->getScope() !== Controller::SCOPE_REST),
1056 ]);
1057 }
1058
1059 public function updateInvitedUsersAction(int $spaceId, array $users): void
1060 {
1061 $membersManager = new MembersManager();
1062
1063 if (!$membersManager->canInviteUsers($spaceId))
1064 {
1065 return;
1066 }
1067
1068 $membersManager->updateInvitedUsers($spaceId, array_map(static fn($userId) => (int)$userId, $users));
1069 }
1070
1072 int $groupId,
1073 string $type,
1074 int $page,
1075 string $componentName = '',
1076 string $signedParameters = ''
1077 ): ?array
1078 {
1079 if (
1080 $groupId <= 0
1082 'groupId' => $groupId,
1083 'checkAdminSession' => ($this->getScope() !== Controller::SCOPE_REST),
1084 ])
1085 )
1086 {
1087 $this->addEmptyGroupIdError();
1088 return null;
1089 }
1090
1091 $unsignedParameters = [];
1092 if (
1093 $componentName !== ''
1094 && $signedParameters !== ''
1095 )
1096 {
1097 $unsignedParameters = \Bitrix\Main\Component\ParameterSigner::unsignParameters(
1098 $componentName,
1099 $signedParameters
1100 );
1101 if (!is_array($unsignedParameters))
1102 {
1103 $unsignedParameters = [];
1104 }
1105 }
1106
1107 $rolesMap = [
1108 'all' => [
1112 ],
1113 'heads' => [
1116 ],
1117 'members' => [
1119 ],
1120 'scrumTeam' => [
1123 ],
1124 ];
1125 $limit = 10;
1126
1127 $query = UserToGroupTable::query();
1128 $records = $query
1129 ->setSelect([
1130 'GROUP_ID',
1131 'GROUP',
1132 'USER_ID',
1133 'ROLE',
1134 'INITIATED_BY_TYPE',
1135 'AUTO_MEMBER',
1136 'NAME' => 'USER.NAME',
1137 'LAST_NAME' => 'USER.LAST_NAME',
1138 'SECOND_NAME' => 'USER.SECOND_NAME',
1139 'LOGIN' => 'USER.LOGIN',
1140 'PERSONAL_PHOTO' => 'USER.PERSONAL_PHOTO',
1141 ])
1142 ->where('GROUP_ID', $groupId)
1143 ->whereIn('ROLE', $rolesMap[$type])
1144 ->setLimit($limit)
1145 ->setOffset(($page - 1) * $limit)
1146 ->exec()->fetchCollection();
1147
1148 $isScrumMembers = ($type === 'scrumTeam');
1149 if ($isScrumMembers)
1150 {
1151 $query->addSelect('GROUP.SCRUM_MASTER_ID', 'SCRUM_MASTER_ID');
1152 }
1153
1154 $imageIds = [];
1155 $resultMembers = [];
1156
1157 foreach ($records as $member)
1158 {
1159 $imageIds[$member->get('USER_ID')] = $member->get('USER')->get('PERSONAL_PHOTO');
1160 $resultMembers[] = $member;
1161 }
1162
1163 $imageIds = array_filter(
1164 $imageIds,
1165 static function ($id) { return (int)$id > 0; }
1166 );
1167 $avatars = Helper\UI\Grid\Workgroup\Members::getUserAvatars($imageIds);
1168 $pathToUser = ($unsignedParameters['PATH_TO_USER'] ?? Helper\Path::get('user_profile'));
1169 $userNameTemplate = ($unsignedParameters['NAME_TEMPLATE'] ?? \CSite::getNameFormat());
1170 $isIntranetInstalled = ModuleManager::isModuleInstalled('intranet');
1171
1172 $members = [];
1173
1174 foreach ($resultMembers as $member)
1175 {
1176 $id = $member->get('USER_ID');
1177 $userNameFormatted = \CUser::formatName($userNameTemplate, [
1178 'NAME' => $member->get('USER')->get('NAME'),
1179 'LAST_NAME' => $member->get('USER')->get('LAST_NAME'),
1180 'SECOND_NAME' => $member->get('USER')->get('SECOND_NAME'),
1181 'LOGIN' => $member->get('USER')->get('LOGIN'),
1182 ], $isIntranetInstalled);
1183
1184 $userUrl = \CComponentEngine::makePathFromTemplate($pathToUser, [
1185 'user_id' => $id,
1186 'id' => $id,
1187 ]);
1188
1189 $members[] = [
1190 'ID' => $id,
1191 'PHOTO' => $avatars[$imageIds[$id] ?? null] ?? '',
1192 'HREF' => $userUrl,
1193 'FORMATTED_NAME' => $userNameFormatted,
1194 'ROLE' => ($isScrumMembers ? $this->getScrumRole($member) : $member->get('ROLE')),
1195 ];
1196
1197 if ($isScrumMembers)
1198 {
1199 if (
1200 $member->get('ROLE') === UserToGroupTable::ROLE_OWNER
1201 && $member->get('USER_ID') === $member->get('GROUP')->get('SCRUM_MASTER_ID')
1202 )
1203 {
1204 $members[] = [
1205 'ID' => $id,
1206 'PHOTO' => $avatars[$imageIds[$id] ?? null] ?? '',
1207 'HREF' => $userUrl,
1208 'FORMATTED_NAME' => $userNameFormatted,
1209 'ROLE' => 'M',
1210 ];
1211 }
1212 }
1213 }
1214
1215 return $members;
1216 }
1217
1218 private function getScrumRole(EO_UserToGroup $member): string
1219 {
1220 if (
1221 $member->get('USER_ID') === $member->get('GROUP')->get('SCRUM_MASTER_ID')
1222 && $member->get('ROLE') !== UserToGroupTable::ROLE_OWNER
1223 )
1224 {
1225 return 'M';
1226 }
1227
1228 return $member->get('ROLE');
1229 }
1230
1234 public function changePinAction(
1235 array $groupIdList,
1236 string $action,
1237 string $componentName = '',
1238 string $signedParameters = ''
1239 ): ?bool
1240 {
1241 $unsignedParameters = [];
1242 if (
1243 $componentName !== ''
1244 && $signedParameters !== ''
1245 )
1246 {
1247 $unsignedParameters = \Bitrix\Main\Component\ParameterSigner::unsignParameters($componentName, $signedParameters);
1248 if (!is_array($unsignedParameters))
1249 {
1250 $unsignedParameters = [];
1251 }
1252 }
1253
1254 $mode = ($unsignedParameters['MODE'] ?? '');
1255
1256 $counter = 0;
1257
1258 foreach ($groupIdList as $groupId)
1259 {
1260 $groupId = (int)$groupId;
1261
1262 if (
1263 $groupId <= 0
1265 'groupId' => $groupId,
1266 ])
1267 )
1268 {
1269 continue;
1270 }
1271
1272 $counter++;
1273
1274 $pin = new Pin($this->userId, $groupId, $mode);
1275 $result = $pin->switch();
1276
1277 if (!$result->isSuccess())
1278 {
1279 $this->addErrors($result->getErrors());
1280 return null;
1281 }
1282
1283 $this->sendPush(PushEventDictionary::EVENT_WORKGROUP_PIN_CHANGED, [
1284 'GROUP_ID' => $groupId,
1285 'ACTION' => $action,
1286 ]
1287 );
1288 }
1289
1290 if ($counter <= 0)
1291 {
1292 $this->addEmptyGroupIdError();
1293 return null;
1294 }
1295
1296 return true;
1297 }
1298
1299 public function acceptIncomingRequestAction(int $groupId, array $userIds): ?array
1300 {
1301 try
1302 {
1303 $result = [];
1304
1305 foreach ($userIds as $userId)
1306 {
1307 $result[$userId] = Helper\Workgroup::acceptIncomingRequest([
1308 'groupId' => $groupId,
1309 'userId' => $userId,
1310 ]);
1311 }
1312
1313 // re-calculte counters for the group moderators
1314 $moderators = UserToGroupTable::getGroupModerators($groupId);
1315 Service::addEvent(
1316 EventDictionary::EVENT_WORKGROUP_MEMBER_REQUEST_CONFIRM,
1317 [
1318 'GROUP_ID' => $groupId,
1319 'RECEPIENTS' => array_map(function ($row) { return $row['USER_ID']; }, $moderators),
1320 ]
1321 );
1322
1323 return $result;
1324 }
1325 catch (Exception $e)
1326 {
1327 $this->addError(new Error($e->getMessage()));
1328
1329 return null;
1330 }
1331 }
1332
1333 public function rejectIncomingRequestAction(int $groupId, array $userIds): ?array
1334 {
1335 try
1336 {
1337 $result = [];
1338
1339 foreach ($userIds as $userId)
1340 {
1341 $result[$userId] = Helper\Workgroup::rejectIncomingRequest([
1342 'groupId' => $groupId,
1343 'userId' => $userId,
1344 ]);
1345 }
1346
1347 // re-calculte counters for the group moderators
1348 $moderators = UserToGroupTable::getGroupModerators($groupId);
1349 Service::addEvent(
1350 EventDictionary::EVENT_WORKGROUP_MEMBER_REQUEST_CONFIRM,
1351 [
1352 'GROUP_ID' => $groupId,
1353 'RECEPIENTS' => array_map(function ($row) { return $row['USER_ID']; }, $moderators),
1354 ]
1355 );
1356
1357 return $result;
1358 }
1359 catch (Exception $e)
1360 {
1361 $this->addError(new Error($e->getMessage()));
1362
1363 return null;
1364 }
1365 }
1366
1367 public function getListIncomingUsersAction(int $groupId, int $pageNum): ?array
1368 {
1369 $permissions = Helper\Workgroup::getPermissions(
1370 ['groupId' => $groupId]
1371 );
1372 if (!$permissions['UserCanModifyGroup'] && !$permissions['UserCanInitiate'])
1373 {
1374 $this->addError(new Error('Access denied'));
1375
1376 return null;
1377 }
1378
1379 $limit = 10;
1380 $offset = ($pageNum - 1) * $limit;
1381
1382 return $this->getListOfAwaitingMembers($groupId, $limit, $offset);
1383 }
1384
1385 public function getChatIdAction(int $groupId): ?string
1386 {
1387 $chatId = '';
1388
1389 if (!Loader::includeModule('im'))
1390 {
1391 return $chatId;
1392 }
1393
1394 $chatData = \Bitrix\Socialnetwork\Integration\Im\Chat\Workgroup::getChatData(
1395 [
1396 'group_id' => $groupId,
1397 'skipAvailabilityCheck' => true,
1398 ]
1399 );
1400 if (!empty($chatData[$groupId]) && intval($chatData[$groupId]) > 0)
1401 {
1402 $chatId = $chatData[$groupId];
1403 }
1404 else
1405 {
1406 $chatId = \Bitrix\Socialnetwork\Integration\Im\Chat\Workgroup::createChat(
1407 [
1408 'group_id' => $groupId,
1409 ]
1410 );
1411 }
1412
1413 return $chatId;
1414 }
1415
1416 private function getUserAvatars(array $imageIds): array
1417 {
1418 $result = [];
1419 if (empty($imageIds))
1420 {
1421 return $result;
1422 }
1423
1424 $result = array_fill_keys($imageIds, '');
1425
1426 $res = \CFile::getList([], ['@ID' => implode(',', $imageIds)]);
1427 while ($file = $res->fetch())
1428 {
1429 $file['SRC'] = \CFile::getFileSRC($file);
1430 $fileInfo = \CFile::resizeImageGet(
1431 $file,
1432 [
1433 'width' => 100,
1434 'height' => 100,
1435 ],
1436 BX_RESIZE_IMAGE_EXACT,
1437 false,
1438 false,
1439 true,
1440 );
1441
1442 $result[$file['ID']] = $fileInfo['src'];
1443 }
1444
1445 return $result;
1446 }
1447
1448 private function sendPush(string $command, array $parameters = []): void
1449 {
1450 $parameters['USER_ID'] = $this->userId;
1451 PushService::addEvent(
1452 [$this->userId],
1453 [
1454 'module_id' => 'socialnetwork',
1455 'command' => $command,
1456 'params' => $parameters,
1457 ]
1458 );
1459 }
1460
1461 private function addEmptyGroupIdError(): void
1462 {
1463 $this->addError(
1464 new Error(
1465 Loc::getMessage('SONET_CONTROLLER_WORKGROUP_EMPTY'),
1466 'SONET_CONTROLLER_WORKGROUP_EMPTY'
1467 )
1468 );
1469 }
1470
1471 private function addIncorrectValueError(): void
1472 {
1473 $this->addError(new Error(
1474 'SONET_CONTROLLER_WORKGROUP_INCORRECT_VALUE',
1475 'SONET_CONTROLLER_WORKGROUP_INCORRECT_VALUE'
1476 ));
1477 }
1478}
static getCurrent()
Definition context.php:241
static getPluralForm($value, $language='')
Definition loc.php:528
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static createFromTimestamp($timestamp)
Definition datetime.php:246
getListIncomingUsersAction(int $groupId, int $pageNum)
changePinAction(array $groupIdList, string $action, string $componentName='', string $signedParameters='')
getGridPopupMembersAction(int $groupId, string $type, int $page, string $componentName='', string $signedParameters='')
listAction(PageNavigation $pageNavigation, array $filter=[], array $select=[], array $order=[], array $params=[])
updateAction(int $groupId, array $fields=[])
disconnectDepartmentsAction(int $groupId, array $departmentIds)
rejectIncomingRequestAction(int $groupId, array $userIds)
acceptIncomingRequestAction(int $groupId, array $userIds)
updateInvitedUsersAction(int $spaceId, array $users)
static canCreate(array $params=[])
Definition access.php:18
static canModify(array $params=[])
Definition access.php:77
static getGroupModerators(int $groupId)