1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
projectprovider.php
См. документацию.
1<?php
2
3namespace Bitrix\Socialnetwork\Integration\UI\EntitySelector;
4
5use Bitrix\Intranet\Settings\Tools\ToolsManager;
6use Bitrix\Main\Application;
7use Bitrix\Main\Config\Option;
8use Bitrix\Main\ORM\Fields\BooleanField;
9use Bitrix\Main\Loader;
10use Bitrix\Main\Localization\Loc;
11use Bitrix\Main\ModuleManager;
12use Bitrix\Main\ORM\Fields\ExpressionField;
13use Bitrix\Main\ORM\Fields\IntegerField;
14use Bitrix\Main\ORM\Fields\Relations\Reference;
15use Bitrix\Main\ORM\Query\Filter;
16use Bitrix\Main\ORM\Query\Join;
17use Bitrix\Main\ORM\Query\Query;
18use Bitrix\Main\Search\Content;
19use Bitrix\Socialnetwork\Collab\CollabFeature;
20use Bitrix\Socialnetwork\Collab\Control\Option\Type\WhoCanInviteOption;
21use Bitrix\Socialnetwork\Collab\Integration\IM;
22use Bitrix\Socialnetwork\Collab\Internals\CollabOptionTable;
23use Bitrix\Socialnetwork\EO_Workgroup;
24use Bitrix\Socialnetwork\EO_Workgroup_Collection;
25use Bitrix\Socialnetwork\FeaturePermTable;
26use Bitrix\Socialnetwork\FeatureTable;
27use Bitrix\Socialnetwork\Helper\Feature;
28use Bitrix\Socialnetwork\Integration\Im\Chat\Workgroup;
29use Bitrix\Socialnetwork\UserToGroupTable;
30use Bitrix\Socialnetwork\WorkgroupSiteTable;
31use Bitrix\Socialnetwork\WorkgroupTable;
32use Bitrix\Socialnetwork\WorkgroupViewTable;
33use Bitrix\UI\EntitySelector\BaseProvider;
34use Bitrix\UI\EntitySelector\Dialog;
35use Bitrix\UI\EntitySelector\Item;
36use Bitrix\UI\EntitySelector\RecentItem;
37use Bitrix\UI\EntitySelector\SearchQuery;
38use Bitrix\UI\EntitySelector\Tab;
39use Bitrix\Socialnetwork\Item\Workgroup\Type;
40use Bitrix\Socialnetwork\Collab\User\User;
41
43{
44 protected const ENTITY_ID = 'project';
45 protected const MAX_PROJECTS_IN_RECENT_TAB = 30;
46 protected const SEARCH_LIMIT = 100;
47
48 private static array $groupDialogIds = [];
49
50 public function __construct(array $options = [])
51 {
52 parent::__construct();
53
54 if (isset($options['project']) && is_bool($options['project']))
55 {
56 $this->options['project'] = $options['project'];
57 }
58
59 if (!empty($options['!type']) && is_array($options['!type']))
60 {
61 $this->options['!type'] = $options['!type'];
62 }
63
64 if (!empty($options['type']) && is_array($options['type']))
65 {
66 $this->options['type'] = $options['type'];
67 }
68
69 if (isset($options['extranet']) && is_bool($options['extranet']))
70 {
71 $this->options['extranet'] = $options['extranet'];
72 }
73
74 if (isset($options['landing']) && is_bool($options['landing']))
75 {
76 $this->options['landing'] = $options['landing'];
77 }
78
79 if (isset($options['features']) && is_array($options['features']))
80 {
81 $this->options['features'] = $options['features'];
82 }
83
84 $this->options['checkFeatureForCreate'] = false;
85 if (isset($options['checkFeatureForCreate']) && is_bool($options['checkFeatureForCreate']))
86 {
87 $this->options['checkFeatureForCreate'] = $options['checkFeatureForCreate'];
88 }
89
90 $this->options['fillRecentTab'] = null; // auto
91 if (isset($options['fillRecentTab']) && is_bool($options['fillRecentTab']))
92 {
93 $this->options['fillRecentTab'] = $options['fillRecentTab'];
94 }
95
96 $this->options['createProjectLink'] = null; // auto
97 if (isset($options['createProjectLink']) && is_bool($options['createProjectLink']))
98 {
99 $this->options['createProjectLink'] = $options['createProjectLink'];
100 }
101
102 $this->options['lockProjectLinkFeatureId'] = '';
103 if (isset($options['lockProjectLinkFeatureId']) && is_string($options['lockProjectLinkFeatureId']))
104 {
105 $this->options['lockProjectLinkFeatureId'] = $options['lockProjectLinkFeatureId'];
106 }
107
108 $this->options['lockProjectLink'] = false;
109 if (isset($options['lockProjectLink']) && is_bool($options['lockProjectLink']))
110 {
111 $this->options['lockProjectLink'] = $options['lockProjectLink'];
112 }
113
114 if (isset($options['projectId']))
115 {
116 if (is_array($options['projectId']))
117 {
118 $this->options['projectId'] = $options['projectId'];
119 }
120 elseif (is_string($options['projectId']) || is_int($options['projectId']))
121 {
122 $this->options['projectId'] = (int)$options['projectId'];
123 }
124 }
125 elseif (isset($options['!projectId']))
126 {
127 if (is_array($options['!projectId']))
128 {
129 $this->options['!projectId'] = $options['!projectId'];
130 }
131 elseif (is_string($options['!projectId']) || is_int($options['!projectId']))
132 {
133 $this->options['!projectId'] = (int)$options['!projectId'];
134 }
135 }
136
137 $this->options['maxProjectsInRecentTab'] = static::MAX_PROJECTS_IN_RECENT_TAB;
138 if (isset($options['maxProjectsInRecentTab']) && is_int($options['maxProjectsInRecentTab']))
139 {
140 $this->options['maxProjectsInRecentTab'] = max(
141 1,
142 min($options['maxProjectsInRecentTab'], static::MAX_PROJECTS_IN_RECENT_TAB)
143 );
144 }
145
146 $this->options['searchLimit'] = static::SEARCH_LIMIT;
147 if (isset($options['searchLimit']) && is_int($options['searchLimit']))
148 {
149 $this->options['searchLimit'] = max(1, min($options['searchLimit'], static::SEARCH_LIMIT));
150 }
151
152 $this->options['shouldSelectProjectDates'] = false;
153 if (isset($options['shouldSelectProjectDates']) && is_bool($options['shouldSelectProjectDates']))
154 {
155 $this->options['shouldSelectProjectDates'] = (bool)$options['shouldSelectProjectDates'];
156 }
157
158 $this->options['shouldSelectDialogId'] = false;
159 if (isset($options['shouldSelectDialogId']) && is_bool($options['shouldSelectDialogId']))
160 {
161 $this->options['shouldSelectDialogId'] = $options['shouldSelectDialogId'];
162 }
163
164 $this->options['checkCollabInviteOption'] = false;
165 if (isset($options['checkCollabInviteOption']) && is_bool($options['checkCollabInviteOption']))
166 {
167 $this->options['checkCollabInviteOption'] = $options['checkCollabInviteOption'];
168 }
169
174 $this->options['addProjectMetaUsers'] = false;
175 if (isset($options['addProjectMetaUsers']) && is_bool($options['addProjectMetaUsers']))
176 {
177 $this->options['addProjectMetaUsers'] = (bool)$options['addProjectMetaUsers'];
178 }
179 }
180
181 public function isAvailable(): bool
182 {
183 return $GLOBALS['USER']->isAuthorized();
184 }
185
186 public function getItems(array $ids): array
187 {
188 return $this->getProjectItems([
189 'projectId' => $ids
190 ]);
191 }
192
193 public function getSelectedItems(array $ids): array
194 {
195 return $this->getProjectItems([
196 'projectId' => $ids
197 ]);
198 }
199
200 public function getPreselectedItems(array $ids): array
201 {
202 return $this->getProjectItems([
203 'projectId' => $ids,
204 'myProjectsOnly' => false,
205 ]);
206 }
207
208 public function fillDialog(Dialog $dialog): void
209 {
210 $limit = 100;
211 $projects = $this->getProjectCollection(['limit' => $limit]);
212 $dialog->addItems($this->makeProjectItems($projects, ['tabs' => 'projects']));
213 $currentUserId = UserProvider::getCurrentUserId();
214 $user = new User($currentUserId);
215 $isCollaber = $user->isCollaber();
216/*
217 if ($projects->count() < $limit)
218 {
219 $entity = $dialog->getEntity('project');
220 if ($entity)
221 {
222 $entity->setDynamicSearch(false);
223 }
224 }
225*/
226 if ($isCollaber)
227 {
228 $dialog->addTab(new Tab([
229 'id' => 'projects',
230 'title' => Loc::getMessage('SOCNET_ENTITY_SELECTOR_COLLAB_TAB_TITLE'),
231 'stub' => true,
232 'icon' => [
233 'default' => '/bitrix/js/socialnetwork/entity-selector/src/images/collab-tab-icon.svg',
234 'selected' => '/bitrix/js/socialnetwork/entity-selector/src/images/collab-tab-icon-selected.svg'
235 ]
236 ]));
237 }
238 else
239 {
240 $icon =
241 'data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2223%22%20height%3D%2223%22%20'.
242 'fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cpath%20d%3D%22M11'.
243 '.934%202.213a.719.719%200%2001.719%200l3.103%201.79c.222.13.36.367.36.623V8.21a.719.71'.
244 '9%200%2001-.36.623l-3.103%201.791a.72.72%200%2001-.719%200L8.831%208.832a.719.719%200%'.
245 '2001-.36-.623V4.627c0-.257.138-.495.36-.623l3.103-1.791zM7.038%2010.605a.719.719%200%2'.
246 '001.719%200l3.103%201.792a.72.72%200%2001.359.622v3.583a.72.72%200%2001-.36.622l-3.102'.
247 '%201.792a.719.719%200%2001-.72%200l-3.102-1.791a.72.72%200%2001-.36-.623v-3.583c0-.257'.
248 '.138-.494.36-.622l3.103-1.792zM20.829%2013.02a.719.719%200%2000-.36-.623l-3.102-1.792a'.
249 '.719.719%200%2000-.72%200l-3.102%201.792a.72.72%200%2000-.36.622v3.583a.72.72%200%2000'.
250 '.36.622l3.103%201.792a.719.719%200%2000.719%200l3.102-1.791a.719.719%200%2000.36-.623v'.
251 '-3.583z%22%20fill%3D%22%23ABB1B8%22/%3E%3C/svg%3E';
252
253 $dialog->addTab(new Tab([
254 'id' => 'projects',
255 'title' => Loc::getMessage('SOCNET_ENTITY_SELECTOR_PROJECTS_TAB_TITLE'),
256 'stub' => true,
257 'icon' => [
258 'default' => $icon,
259 'selected' => str_replace('ABB1B8', 'fff', $icon),
260 //'default' => '/bitrix/js/socialnetwork/entity-selector/images/project-tab-icon.svg',
261 //'selected' => '/bitrix/js/socialnetwork/entity-selector/images/project-tab-icon-selected.svg'
262 ]
263 ]));
264 }
265
266 $onlyProjectsMode = count($dialog->getEntities()) === 1;
267
268 $fillRecentTab = (
269 $this->options['fillRecentTab'] === true ||
270 ($this->options['fillRecentTab'] !== false && $onlyProjectsMode)
271 );
272
273 if ($fillRecentTab)
274 {
275 $this->fillRecentTab($dialog, $projects);
276 }
277
278 $createProjectLink =
279 $this->options['createProjectLink'] === true ||
280 ($this->options['createProjectLink'] !== false && $onlyProjectsMode)
281 ;
282
283 if (
284 $this->options['checkFeatureForCreate']
285 && !Feature::isFeatureEnabled(Feature::PROJECTS_GROUPS)
286 )
287 {
288 $createProjectLink = false;
289 }
290
291 if ($createProjectLink && self::canCreateProject())
292 {
293 $footerOptions = [];
294 if ($dialog->getFooter() === 'BX.SocialNetwork.EntitySelector.Footer')
295 {
296 // Footer could be set from UserProvider
297 $footerOptions = $dialog->getFooterOptions() ?? [];
298 }
299
300 if ($this->options['lockProjectLink'])
301 {
302 $footerOptions['lockProjectLink'] = true;
303 $footerOptions['lockProjectLinkFeatureId'] = $this->options['lockProjectLinkFeatureId'] ?? '';
304 }
305
306 $footerOptions['createProjectLink'] = self::getCreateProjectUrl(UserProvider::getCurrentUserId());
307 $dialog->setFooter('BX.SocialNetwork.EntitySelector.Footer', $footerOptions);
308 }
309 }
310
311 public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void
312 {
313 $dialog->addItems(
314 $this->getProjectItems(['searchQuery' => $searchQuery->getQuery()])
315 );
316 }
317
319 {
320 $options = array_merge($this->getOptions(), $options);
321
322 return static::getProjects($options);
323 }
324
325 public function getProjectItems(array $options = []): array
326 {
328 }
329
331 {
332 return self::makeItems($projects, array_merge($this->getOptions(), $options));
333 }
334
335 public static function getProjects(array $options = []): EO_Workgroup_Collection
336 {
337 $isProjectsEnabled = true;
338 $isScrumEnabled = true;
339 if (Loader::includeModule('intranet'))
340 {
341 $toolsManager = ToolsManager::getInstance();
342 $isProjectsEnabled = $toolsManager->checkAvailabilityByToolId('projects');
343 $isScrumEnabled = $toolsManager->checkAvailabilityByToolId('scrum');
344 if (!$isProjectsEnabled && !$isScrumEnabled)
345 {
346 return new EO_Workgroup_Collection();
347 }
348 }
349
350 $query = WorkgroupTable::query();
351 $selectFields = [
352 'ID',
353 'NAME',
354 'ACTIVE',
355 'PROJECT',
356 'CLOSED',
357 'VISIBLE',
358 'OPENED',
359 'IMAGE_ID',
360 'AVATAR_TYPE',
361 'LANDING',
362 'TYPE',
363 'SCRUM_MASTER_ID',
364 ];
365
366 if (
367 isset($options['shouldSelectProjectDates'])
368 && is_bool(isset($options['shouldSelectProjectDates']))
369 && $options['shouldSelectProjectDates']
370 )
371 {
372 $selectFields[] = 'PROJECT_DATE_START';
373 $selectFields[] = 'PROJECT_DATE_FINISH';
374 }
375 $query->setSelect($selectFields);
376
377 if (isset($options['visible']) && is_bool(isset($options['visible'])))
378 {
379 $query->where('VISIBLE', $options['visible'] ? 'Y' : 'N');
380 }
381
382 if (isset($options['open']) && is_bool(isset($options['open'])))
383 {
384 $query->where('OPENED', $options['open'] ? 'Y' : 'N');
385 }
386
387 if (isset($options['closed']) && is_bool(isset($options['closed'])))
388 {
389 $query->where('CLOSED', $options['closed'] ? 'Y' : 'N');
390 }
391
392 if (isset($options['landing']) && is_bool(isset($options['landing'])))
393 {
394 $query->where('LANDING', $options['landing'] ? 'Y' : 'N');
395 }
396
397 if (isset($options['active']) && is_bool(isset($options['active'])))
398 {
399 $query->where('ACTIVE', $options['active'] ? 'Y' : 'N');
400 }
401
402 if (isset($options['project']) && is_bool(isset($options['project'])))
403 {
404 $query->where('PROJECT', $options['project'] ? 'Y' : 'N');
405 }
406
407 if (!CollabFeature::isFeatureEnabledInPortalSettings())
408 {
409 $options['!type'] ??= [];
410
411 if (is_array($options['!type']) && !in_array(Type::Collab->value, $options['!type']))
412 {
413 $options['!type'][] = Type::Collab->value;
414 }
415 }
416
417 if (!empty($options['!type']) && is_array($options['!type']))
418 {
419 $filter = Query::filter()
420 ->logic('or')
421 ->whereNotIn('TYPE', $options['!type'])
422 ->whereNull('TYPE');
423
424 $query->where($filter);
425 }
426
427 if (!empty($options['type']) && is_array($options['type']))
428 {
429 $query->whereIn('TYPE', $options['type']);
430 }
431
432 if (!empty($options['searchQuery']) && is_string($options['searchQuery']))
433 {
434 $query->whereMatch(
435 'SEARCH_INDEX',
436 Filter\Helper::matchAgainstWildcard(
437 Content::prepareStringToken($options['searchQuery']),
438 '*',
439 1
440 )
441 );
442 }
443
444 $currentUserId = (!empty($options['currentUserId']) && is_int($options['currentUserId'])
445 ? $options['currentUserId'] : $GLOBALS['USER']->getId());
446
447 $query->registerRuntimeField(
448 new Reference(
449 'PROJECT_SITE',
450 WorkgroupSiteTable::class,
451 Join::on('this.ID', 'ref.GROUP_ID'),
452 ['join_type' => 'INNER']
453 )
454 );
455
456 $siteId = !empty($options['siteId']) && is_string($options['siteId']) ? $options['siteId'] : SITE_ID;
457 $query->where('PROJECT_SITE.SITE_ID', $siteId);
458
459 $options['myProjectsOnly'] ??= true;
460 $notOnlyMyProjects = ($options['myProjectsOnly'] === false);
461
462 $currentUserModuleAdmin = \CSocNetUser::isCurrentUserModuleAdmin();
463 if (!$currentUserModuleAdmin)
464 {
465 $query->registerRuntimeField(
466 new Reference(
467 'MY_PROJECT',
468 UserToGroupTable::class,
469 Join::on('this.ID', 'ref.GROUP_ID')
470 ->where('ref.USER_ID', $currentUserId)
471 ->where(
472 'ref.ROLE',
473 '<=',
475 ),
476 ['join_type' => $notOnlyMyProjects ? Join::TYPE_LEFT : Join::TYPE_INNER]
477 )
478 );
479 $query->addSelect('MY_PROJECT.ROLE', 'PROJECT_ROLE');
480 }
481
482 if (isset($options['viewed']) && is_bool(isset($options['viewed'])))
483 {
484 $query->registerRuntimeField(
485 new Reference(
486 'VIEWED_PROJECT',
487 WorkgroupViewTable::class,
488 Join::on('this.ID', 'ref.GROUP_ID')->where('ref.USER_ID', $currentUserId),
489 ['join_type' => 'INNER']
490 )
491 );
492 }
493
494 if ($options['checkCollabInviteOption'] ?? false)
495 {
496 if ($currentUserModuleAdmin)
497 {
498 // include UserToGroupTable for admin, but don't include it twice for anyone else
499 $query->registerRuntimeField(
500 new Reference(
501 'MY_PROJECT',
502 UserToGroupTable::class,
503 Join::on('this.ID', 'ref.GROUP_ID')
504 ->where('ref.USER_ID', $currentUserId),
505 ['join_type' => Join::TYPE_INNER]
506 )
507 );
508 }
509
510 $query->registerRuntimeField(
511 new Reference(
512 'COLLAB_OPTIONS',
513 CollabOptionTable::class,
514 Join::on('this.ID', 'ref.COLLAB_ID')
515 ->where('ref.NAME', WhoCanInviteOption::DB_NAME),
516 ['join_type' => Join::TYPE_INNER]
517 )
518 );
519
520 $ownerRole = UserToGroupTable::ROLE_OWNER;
522 $userRole = UserToGroupTable::ROLE_USER;
523 $query->registerRuntimeField((new ExpressionField(
524 'NEED_ROLE_WEIGHT',
525 "CASE
526 WHEN %s = '{$ownerRole}' THEN 2
527 WHEN %s = '{$managerRole}' THEN 1
528 ELSE 0
529 END",
530 ['COLLAB_OPTIONS.VALUE', 'COLLAB_OPTIONS.VALUE']
531 ))->configureValueType(IntegerField::class));
532
533 $query->registerRuntimeField((new ExpressionField(
534 'ROLE_WEIGHT',
535 "CASE
536 WHEN %s = '{$ownerRole}' THEN 2
537 WHEN %s = '{$managerRole}' THEN 1
538 WHEN %s = '{$userRole}' THEN 0
539 ELSE -1
540 END",
541 ['MY_PROJECT.ROLE', 'MY_PROJECT.ROLE', 'MY_PROJECT.ROLE']
542 ))->configureValueType(IntegerField::class));
543
544 $query->where((new ExpressionField(
545 'HAS_ACCESS_BY_ROLE',
546 '%s >= %s',
547 ['ROLE_WEIGHT', 'NEED_ROLE_WEIGHT']
548 ))->configureValueType(BooleanField::class), 'expr', true);
549 }
550
551 $extranetSiteId = Option::get('extranet', 'extranet_site');
552 $extranetSiteId = (
553 $extranetSiteId
554 && ModuleManager::isModuleInstalled('extranet') ? $extranetSiteId : false
555 );
556 if ($extranetSiteId)
557 {
558 $query->registerRuntimeField(
559 new Reference(
560 'EXTRANET_PROJECT',
561 WorkgroupSiteTable::class,
562 Join::on('this.ID', 'ref.GROUP_ID')->where('ref.SITE_ID', $extranetSiteId),
563 ['join_type' => 'LEFT']
564 )
565 );
566
567 $query->registerRuntimeField(
568 new ExpressionField(
569 'IS_EXTRANET', 'CASE WHEN %s IS NOT NULL THEN \'Y\' ELSE \'N\' END', ['EXTRANET_PROJECT.GROUP_ID']
570 )
571 );
572
573 $query->addSelect('IS_EXTRANET');
574
575 if (isset($options['extranet']) && is_bool($options['extranet']))
576 {
577 if ($options['extranet'])
578 {
579 $query->whereNotNull('EXTRANET_PROJECT.GROUP_ID');
580 }
581 else
582 {
583 $query->whereNull('EXTRANET_PROJECT.GROUP_ID');
584 }
585 }
586 }
587
588 $projectIds = [];
589 $projectFilter = (
590 isset($options['projectId'])
591 ? 'projectId'
592 : (isset($options['!projectId']) ? '!projectId' : null)
593 );
594
595 if (isset($options[$projectFilter]))
596 {
597 if (is_array($options[$projectFilter]) && !empty($options[$projectFilter]))
598 {
599 foreach ($options[$projectFilter] as $id)
600 {
601 $projectIds[] = (int)$id;
602 }
603
604 $projectIds = array_unique($projectIds);
605
606 if (!empty($projectIds))
607 {
608 if ($projectFilter === 'projectId')
609 {
610 $query->whereIn('ID', $projectIds);
611 }
612 else
613 {
614 $query->whereNotIn('ID', $projectIds);
615 }
616 }
617 }
618 else if (!is_array($options[$projectFilter]) && (int)$options[$projectFilter] > 0)
619 {
620 if ($projectFilter === 'projectId')
621 {
622 $query->where('ID', (int)$options[$projectFilter]);
623 }
624 else
625 {
626 $query->whereNot('ID', (int)$options[$projectFilter]);
627 }
628 }
629 }
630
631 if (
632 $projectFilter === 'projectId'
633 && empty($options['order'])
634 && ($projectsCount = count($projectIds)) > 1
635 )
636 {
637 $helper = Application::getConnection()->getSqlHelper();
638 $field = new ExpressionField(
639 'ID_SEQUENCE',
640 $helper->getOrderByIntField('%s', $projectIds, false),
641 array_fill(0, $projectsCount, 'ID')
642 );
643
644 $query
645 ->registerRuntimeField($field)
646 ->setOrder($field->getName());
647 }
648 elseif (!empty($options['order']) && is_array($options['order']))
649 {
650 $query->setOrder($options['order']);
651 }
652 else
653 {
654 $query->setOrder(['NAME' => 'asc']);
655 }
656
657 $isUserModuleAdmin = \CSocNetUser::isUserModuleAdmin($currentUserId, $siteId);
658
659 if (
660 isset($options['features'])
661 && is_array($options['features'])
662 && !empty($options['features'])
663 )
664 {
665 foreach (array_keys($options['features']) as $feature)
666 {
667 if (!self::isAllowedFeatures($feature))
668 {
669 return new EO_Workgroup_Collection();
670 }
671
672 $featureField = new Reference(
673 "BF_{$feature}",
674 FeatureTable::class,
675 Join::on('this.ID', 'ref.ENTITY_ID')
676 ->where('ref.ENTITY_TYPE', FeatureTable::FEATURE_ENTITY_TYPE_GROUP)
677 ->where('ref.FEATURE', $feature)
678 ->where('ref.ACTIVE', 'N'),
679 ['join_type' => 'LEFT']
680 );
681 $query->registerRuntimeField($featureField);
682
683 $query->whereNull("BF_{$feature}.ENTITY_ID");
684 }
685
686 if (!$isUserModuleAdmin)
687 {
688 $featuresPermissionsQuery = self::getFeaturesPermissionsQuery(
689 $currentUserId,
690 $options['features']
691 );
692 if ($featuresPermissionsQuery)
693 {
694 $query->whereIn('ID', $featuresPermissionsQuery);
695 }
696 }
697 }
698
699 if (!$isProjectsEnabled)
700 {
701 $query->whereNotNull('SCRUM_MASTER_ID');
702 }
703
704 if (!$isScrumEnabled)
705 {
706 $query->whereNull('SCRUM_MASTER_ID');
707 }
708
709 if (isset($options['limit']) && is_int($options['limit']))
710 {
711 $query->setLimit($options['limit']);
712 }
713 elseif ($projectFilter !== 'projectId' || empty($projectIds))
714 {
715 $query->setLimit($options['searchLimit'] ?? null);
716 }
717
718 $eoWorkgroups = $query->exec()->fetchCollection();
719
720 if ($currentUserModuleAdmin)
721 {
722 return $eoWorkgroups;
723 }
724
725 $workgroups = new EO_Workgroup_Collection();
726 foreach ($eoWorkgroups as $eoWorkgroup)
727 {
728 $isMember = $query->getEntity()->hasField('MY_PROJECT') && !empty($eoWorkgroup->get('MY_PROJECT'));
729 $notSecretGroup = $eoWorkgroup->getVisible() === true;
730 if ($isMember || ($notOnlyMyProjects && $notSecretGroup))
731 {
732 $workgroups->add($eoWorkgroup);
733 }
734 }
735
736 if ($options['shouldSelectDialogId'] ?? false)
737 {
738 $chatData = Workgroup::getChatData([
739 'group_id' => array_diff($workgroups->getIdList(), array_keys(static::$groupDialogIds)),
740 'skipAvailabilityCheck' => true,
741 ]);
742
743 foreach ($workgroups as $workgroup)
744 {
745 $id = $workgroup->getId();
746 static::$groupDialogIds[$id] = IM\Dialog::getDialogId($chatData[$id] ?? 0);
747 }
748 }
749
750 return $workgroups;
751 }
752
753 private static function isAllowedFeatures($feature = ''): bool
754 {
755 static $globalFeatures = null;
756
757 if ($globalFeatures === null)
758 {
759 $globalFeatures = \CSocNetAllowed::getAllowedFeatures();
760 }
761
762 if (
763 !isset($globalFeatures[$feature]['allowed'])
764 || !is_array($globalFeatures[$feature]['allowed'])
765 || !in_array(SONET_ENTITY_GROUP, $globalFeatures[$feature]['allowed'], true)
766 || mb_strlen($feature) <= 0
767 )
768 {
769 return false;
770 }
771
772 return true;
773 }
774
778 public static function getFeatureQuery($alias, $feature = '')
779 {
780 if (!self::isAllowedFeatures($feature))
781 {
782 return false;
783 }
784
785 $subQuery = FeatureTable::query();
786 $subQuery->where('ENTITY_TYPE', FeatureTable::FEATURE_ENTITY_TYPE_GROUP);
787 $subQuery->where('FEATURE', $feature);
788 $subQuery->where('ACTIVE', 'N');
789 $subQuery->registerRuntimeField(
790 new ExpressionField(
791 'IS_INACTIVE_IN_GROUP', "CASE WHEN {$alias}.ID = %s THEN 1 ELSE 0 END", 'ENTITY_ID'
792 )
793 );
794 $subQuery->where('IS_INACTIVE_IN_GROUP', 1);
795
796 return $subQuery;
797 }
798
802 public static function getFeaturesPermissionsQuery($currentUserId, $featuresList = [])
803 {
804 $helper = \Bitrix\Main\Application::getConnection()->getSqlHelper();
805 $globalFeatures = \CSocNetAllowed::getAllowedFeatures();
806
807 $workWithClosedGroups = (Option::get('socialnetwork', 'work_with_closed_groups', 'N') === 'Y');
808
809 $query = new \Bitrix\Main\Entity\Query(WorkgroupTable::getEntity());
810 $query->addSelect('ID');
811
812 if ($currentUserId > 0)
813 {
814 $query->registerRuntimeField(new Reference(
815 'UG',
816 UserToGroupTable::getEntity(),
817 Join::on('this.ID', 'ref.GROUP_ID')
818 ->where('ref.USER_ID', $currentUserId),
819 [ 'join_type' => 'INNER' ]
820 ));
821 }
822
823 $hasFilter = false;
824
825 $featureEntity = clone FeatureTable::getEntity();
826
827 foreach ($featuresList as $feature => $operationsList)
828 {
829 if (empty($operationsList))
830 {
831 continue;
832 }
833
834 $hasFilter = true;
835
836 $defaultPerm = 'A';
837 foreach ($globalFeatures[$feature]['operations'] as $operation => $perms)
838 {
839 if (
840 !in_array($operation, $operationsList, true)
842 )
843 {
844 continue;
845 }
846
847 if ($perms[FeatureTable::FEATURE_ENTITY_TYPE_GROUP] > $defaultPerm)
848 {
849 $defaultPerm = $perms[FeatureTable::FEATURE_ENTITY_TYPE_GROUP];
850 }
851 }
852
853 $query->registerRuntimeField(new Reference(
854 "F_{$feature}",
855 $featureEntity,
856 Join::on('this.ID', 'ref.ENTITY_ID')
857 ->where('ref.ENTITY_TYPE', FeatureTable::FEATURE_ENTITY_TYPE_GROUP)
858 ->where('ref.FEATURE', $feature),
859 [ 'join_type' => 'LEFT' ]
860 ));
861
862 $featureEntity->addField(new Reference(
863 "FP_{$feature}",
864 FeaturePermTable::class,
865 Join::on('this.ID', 'ref.FEATURE_ID'),
866 [ 'join_type' => 'LEFT' ]
867 ));
868
869 $query->where(\Bitrix\Main\Entity\Query::filter()
870 ->logic('or')
871 ->whereIn("F_{$feature}.FP_{$feature}.OPERATION_ID", $operationsList)
872 ->whereNull("F_{$feature}.FP_{$feature}.OPERATION_ID")
873 );
874
875 if ($currentUserId > 0)
876 {
877 $minOperationsList = ($globalFeatures[$feature]['minoperation'] ?? []);
878 if (!is_array($minOperationsList))
879 {
880 $minOperationsList = [ $minOperationsList ];
881 }
882
883 $conditionsList = [];
884 $substitutes = [];
885
886 if (!$workWithClosedGroups && !empty($minOperationsList))
887 {
888 $minOperations = implode(', ', array_map(static function($operation) use ($helper) { return "'" . $helper->forSql($operation) . "'"; }, $minOperationsList));
889 $conditionsList[] = "WHEN %s = 'Y' AND %s NOT IN ({$minOperations}) THEN 'A'";
890 $substitutes[] = 'CLOSED';
891 $substitutes[] = "F_{$feature}.FP_{$feature}.OPERATION_ID";
892 }
893
894 $conditionsList[] = "WHEN %s = 'N' AND %s IN ('N', 'L') THEN 'K'";
895 $substitutes[] = 'VISIBLE';
896 $substitutes[] = "F_{$feature}.FP_{$feature}.ROLE";
897
898 $conditionsList[] = 'WHEN %s IS NOT NULL THEN %s';
899 $substitutes[] = "F_{$feature}.FP_{$feature}.ROLE";
900 $substitutes[] = "F_{$feature}.FP_{$feature}.ROLE";
901
902 $conditions = implode(' ', $conditionsList);
903
904 $query->registerRuntimeField(new ExpressionField(
905 "MIN_PERMISSION_{$feature}",
906 "CASE {$conditions} ELSE '{$defaultPerm}' END",
907 $substitutes
908 ));
909
910 $query->registerRuntimeField(
911 new ExpressionField(
912 "HAS_ACCESS_{$feature}",
913 'CASE WHEN %s <= %s THEN 1 ELSE 0 END',
914 [
915 'UG.ROLE',
916 "MIN_PERMISSION_{$feature}",
917 ]
918 )
919 );
920 $query->where("HAS_ACCESS_{$feature}", 1);
921 }
922 else
923 {
924 $query->registerRuntimeField(new ExpressionField(
925 "MIN_PERMISSION_{$feature}",
926 "CASE WHEN %s IS NOT NULL THEN %s ELSE '{$defaultPerm}' END",
927 [
928 "F_{$feature}.FP_{$feature}.ROLE",
929 "F_{$feature}.FP_{$feature}.ROLE"
930 ]
931 ));
932
933 $query->where("MIN_PERMISSION_{$feature}", 'N');
934 }
935 }
936
937 return ($hasFilter ? $query : false);
938 }
939
940 public static function filterByFeatures(
941 EO_Workgroup_Collection $projects, array $features, int $userId, string $siteId
942 )
943 {
944 if (empty($features))
945 {
946 return $projects;
947 }
948
949 $projectIds = $projects->getIdList();
950 foreach ($features as $feature => $operations)
951 {
952 $availableIds = \CSocNetFeatures::isActiveFeature(SONET_ENTITY_GROUP, $projectIds, $feature);
953 if (!is_array($availableIds))
954 {
955 return new EO_Workgroup_Collection();
956 }
957
958 $hasUnavailableId = false;
959 foreach ($availableIds as $projectId => $isAvailable)
960 {
961 if (!$isAvailable)
962 {
963 $hasUnavailableId = true;
964 $projects->removeByPrimary($projectId);
965 }
966 }
967
968 if ($hasUnavailableId)
969 {
970 $projectIds = $projects->getIdList();
971 }
972 }
973
974 $isUserModuleAdmin = \CSocNetUser::isUserModuleAdmin($userId, $siteId);
975 $availableIdsByFeature = [];
976
977 // Features have logic 'AND', whereas operations have logic 'OR'.
978 foreach ($features as $feature => $operations)
979 {
980 if (!is_array($operations) || empty($operations))
981 {
982 $availableIdsByFeature[] = $projectIds;
983 continue;
984 }
985
986 $availableFeatureIds = [];
987 $ids = $projectIds;
988 foreach ($operations as $operation)
989 {
990 if (empty($ids))
991 {
992 break;
993 }
994
995 $availableIds = \CSocNetFeaturesPerms::canPerformOperation(
996 $userId,
998 $ids,
999 $feature,
1000 $operation,
1001 $isUserModuleAdmin
1002 );
1003
1004 if (!is_array($availableIds))
1005 {
1006 continue;
1007 }
1008
1009 foreach ($availableIds as $projectId => $isAvailable)
1010 {
1011 if ($isAvailable)
1012 {
1013 $availableFeatureIds[] = $projectId;
1014 }
1015 }
1016
1017 $ids = array_diff($ids, $availableFeatureIds);
1018 }
1019
1020 $availableIdsByFeature[] = $availableFeatureIds;
1021 }
1022
1023 $availableIds = [];
1024 if (!empty($availableIdsByFeature))
1025 {
1026 $availableIds = (
1027 count($availableIdsByFeature) > 1
1028 ? call_user_func_array('array_intersect', $availableIdsByFeature)
1029 : $availableIdsByFeature[0]
1030 );
1031 }
1032
1033 if (empty($availableIds))
1034 {
1035 return new EO_Workgroup_Collection();
1036 }
1037
1038 $wrongIds = array_diff($projects->getIdList(), $availableIds);
1039 foreach ($wrongIds as $wrongId)
1040 {
1041 $projects->removeByPrimary($wrongId);
1042 }
1043
1044 return $projects;
1045 }
1046
1047 public static function makeItems(EO_Workgroup_Collection $projects, $options = [])
1048 {
1049 $result = [];
1050 foreach ($projects as $project)
1051 {
1052 $result[] = static::makeItem($project, $options);
1053 }
1054
1055 return $result;
1056 }
1057
1064 public static function makeItem(EO_Workgroup $project, $options = []): Item
1065 {
1066 $extranetSiteId = Option::get('extranet', 'extranet_site');
1067 $extranetSiteId = ($extranetSiteId && ModuleManager::isModuleInstalled('extranet') ? $extranetSiteId : false);
1068 $isExtranet = ($extranetSiteId && $project->get('IS_EXTRANET') === 'Y');
1069 $isCollab = $project->getType() === Type::Collab->value;
1070
1071 $item = new Item([
1072 'id' => $project->getId(),
1073 'entityId' => static::ENTITY_ID,
1074 'entityType' => self::makeProjectEntityType($project),
1075 'title' => $project->getName(),
1076 'avatar' => self::makeProjectAvatar($project),
1077 'customData' => [
1078 'landing' => $project->getLanding(),
1079 'active' => $project->getActive(),
1080 'visible' => $project->getVisible(),
1081 'closed' => $project->getClosed(),
1082 'open' => $project->getOpened(),
1083 'project' => $project->getProject(),
1084 'isCollab' => $isCollab,
1085 'isExtranet' => $isExtranet,
1086 'datePlan' => [
1087 'dateStart' => $project->getProjectDateStart()?->getTimestamp(),
1088 'dateFinish' => $project->getProjectDateFinish()?->getTimestamp(),
1089 ],
1090 ],
1091 ]);
1092
1093 if ($options['shouldSelectDialogId'] ?? false)
1094 {
1095 $item->getCustomData()->set('dialogId', static::$groupDialogIds[$project->getId()]);
1096 }
1097
1098 if (!empty($options['tabs']))
1099 {
1100 $item->addTab($options['tabs']);
1101 }
1102
1103 if (isset($options['addProjectMetaUsers']) && $options['addProjectMetaUsers'] === true)
1104 {
1105 $item->addChild(new Item([
1106 'id' => $project->getId() . ':A',
1107 'title' => $project->getName() . '. ' . Loc::getMessage('SOCNET_ENTITY_SELECTOR_PROJECTS_METAUSER_OWNER'),
1108 'entityId' => static::ENTITY_ID,
1109 'entityType' => 'project_metauser',
1110 'nodeOptions' => [
1111 'title' => Loc::getMessage('SOCNET_ENTITY_SELECTOR_PROJECTS_METAUSER_OWNER'),
1112 'renderMode' => 'override',
1113 ],
1114 'customData' => [
1115 'projectId' => $project->getId(),
1116 'metauser' => 'owner'
1117 ]
1118 ]));
1119
1120 $item->addChild(new Item([
1121 'id' => $project->getId() . ':E',
1122 'title' => $project->getName() . '. ' . Loc::getMessage('SOCNET_ENTITY_SELECTOR_PROJECTS_METAUSER_MODERATOR'),
1123 'entityType' => 'project_metauser',
1124 'entityId' => static::ENTITY_ID,
1125 'nodeOptions' => [
1126 'title' => Loc::getMessage('SOCNET_ENTITY_SELECTOR_PROJECTS_METAUSER_MODERATOR'),
1127 'renderMode' => 'override',
1128 ],
1129 'customData' => [
1130 'projectId' => $project->getId(),
1131 'metauser' => 'moderator'
1132 ]
1133 ]));
1134
1135 $item->addChild(new Item([
1136 'id' => $project->getId() . ':K',
1137 'title' => $project->getName() . '. ' . Loc::getMessage('SOCNET_ENTITY_SELECTOR_PROJECTS_METAUSER_ALL'),
1138 'entityId' => static::ENTITY_ID,
1139 'entityType' => 'project_metauser',
1140 'nodeOptions' => [
1141 'title' => Loc::getMessage('SOCNET_ENTITY_SELECTOR_PROJECTS_METAUSER_ALL'),
1142 'renderMode' => 'override',
1143 ],
1144 'customData' => [
1145 'projectId' => $project->getId(),
1146 'metauser' => 'all'
1147 ]
1148 ]));
1149 }
1150
1151 return $item;
1152 }
1153
1154 public static function makeProjectAvatar(EO_Workgroup $project): ?string
1155 {
1156 if (!empty($project->getImageId()))
1157 {
1158 $avatar = \CFile::resizeImageGet(
1159 $project->getImageId(),
1160 ['width' => 100, 'height' => 100],
1162 false
1163 );
1164
1165 return !empty($avatar['src']) ? $avatar['src'] : null;
1166 }
1167
1168 if (!empty($project->getAvatarType()))
1169 {
1170 $url = \Bitrix\Socialnetwork\Helper\Workgroup::getAvatarEntitySelectorUrl($project->getAvatarType());
1171 return !empty($url) ? $url : null;
1172 }
1173
1174 return null;
1175 }
1176
1177 public static function makeProjectEntityType(EO_Workgroup $project): ?string
1178 {
1179 $extranetSiteId = Option::get('extranet', 'extranet_site');
1180 $extranetSiteId = ($extranetSiteId && ModuleManager::isModuleInstalled('extranet') ? $extranetSiteId : false);
1181 $isExtranet = ($extranetSiteId && $project->get('IS_EXTRANET') === 'Y');
1182 $isCollab = $project->getType() === Type::Collab->value;
1183
1184 return ($isExtranet && !$isCollab) ? 'extranet' : $project->getType();
1185 }
1186
1187 public static function getProjectUrl(?int $projectId = null, ?int $currentUserId = null): string
1188 {
1189 if (UserProvider::isExtranetUser($currentUserId))
1190 {
1191 $extranetSiteId = Option::get('extranet', 'extranet_site');
1192 $projectPage = Option::get('socialnetwork', 'workgroups_page', false, $extranetSiteId);
1193 if (!$projectPage)
1194 {
1195 $projectPage = '/extranet/workgroups/';
1196 }
1197 }
1198 else
1199 {
1200 $projectPage = Option::get('socialnetwork', 'workgroups_page', false, SITE_ID);
1201 if (!$projectPage)
1202 {
1203 $projectPage = SITE_DIR.'workgroups/';
1204 }
1205 }
1206
1207 return $projectPage.'group/'.($projectId !== null ? $projectId : '#id#').'/';
1208 }
1209
1210 public static function getCreateProjectUrl(?int $currentUserId = null): string
1211 {
1212 $userPage =
1213 UserProvider::isExtranetUser($currentUserId)
1214 ? UserProvider::getExtranetUserUrl($currentUserId)
1215 : UserProvider::getIntranetUserUrl($currentUserId)
1216 ;
1217
1218 return $userPage . 'groups/create/';
1219 }
1220
1221 public static function canCreateProject(): bool
1222 {
1223 return \Bitrix\Socialnetwork\Helper\Workgroup\Access::canCreate();
1224 }
1225
1226 private function fillRecentTab(Dialog $dialog, EO_Workgroup_Collection $projects): void
1227 {
1228 $recentItems = $dialog->getRecentItems()->getEntityItems(static::ENTITY_ID);
1229
1230 if (count($recentItems) < $this->options['maxProjectsInRecentTab'])
1231 {
1232 $limit = $this->options['maxProjectsInRecentTab'] - count($recentItems);
1233 $recentGlobalItems = $dialog->getGlobalRecentItems()->getEntityItems(static::ENTITY_ID);
1234 foreach ($recentGlobalItems as $recentGlobalItem)
1235 {
1236 if ($limit <= 0)
1237 {
1238 break;
1239 }
1240
1241 if (!isset($recentItems[$recentGlobalItem->getId()]) && $recentGlobalItem->isLoaded())
1242 {
1243 $dialog->getRecentItems()->add($recentGlobalItem);
1244 $limit--;
1245 }
1246 }
1247
1248 $recentItems = $dialog->getRecentItems()->getEntityItems(static::ENTITY_ID);
1249 }
1250
1251 if (count($recentItems) < $this->options['maxProjectsInRecentTab'])
1252 {
1253 $recentIds = array_map('intval', array_keys($recentItems));
1254
1255 $dialog->addRecentItems(
1256 $this->getProjectItems([
1257 '!projectId' => $recentIds,
1258 'viewed' => true,
1259 'order' => ['VIEWED_PROJECT.DATE_VIEW' => 'desc'],
1260 'limit' => $this->options['maxProjectsInRecentTab'] - count($recentItems)
1261 ])
1262 );
1263
1264 $recentItems = $dialog->getRecentItems()->getEntityItems(static::ENTITY_ID);
1265 }
1266
1267 if (count($recentItems) < $this->options['maxProjectsInRecentTab'])
1268 {
1269 $limit = $this->options['maxProjectsInRecentTab'] - count($recentItems);
1270 foreach ($projects as $project)
1271 {
1272 if ($limit <= 0)
1273 {
1274 break;
1275 }
1276
1277 if (isset($recentItems[$project->getId()]))
1278 {
1279 continue;
1280 }
1281
1282 $dialog->getRecentItems()->add(
1283 new RecentItem([
1284 'id' => $project->getId(),
1285 'entityId' => static::ENTITY_ID,
1286 'loaded' => true,
1287 ])
1288 );
1289
1290 $limit--;
1291 }
1292 }
1293 }
1294}
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
Определения dialog.php:11
static getDialogId(int $chatId, $userId=null)
Определения dialog.php:50
static getConnection($name="")
Определения application.php:638
const FEATURE_ENTITY_TYPE_GROUP
Определения feature.php:48
static makeItem(EO_Workgroup $project, $options=[])
Определения projectprovider.php:1064
static getProjectUrl(?int $projectId=null, ?int $currentUserId=null)
Определения projectprovider.php:1187
static makeItems(EO_Workgroup_Collection $projects, $options=[])
Определения projectprovider.php:1047
static getFeaturesPermissionsQuery($currentUserId, $featuresList=[])
Определения projectprovider.php:802
static getCreateProjectUrl(?int $currentUserId=null)
Определения projectprovider.php:1210
makeProjectItems(EO_Workgroup_Collection $projects, array $options=[])
Определения projectprovider.php:330
static filterByFeatures(EO_Workgroup_Collection $projects, array $features, int $userId, string $siteId)
Определения projectprovider.php:940
doSearch(SearchQuery $searchQuery, Dialog $dialog)
Определения projectprovider.php:311
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$query
Определения get_search.php:11
$isAvailable
Определения .description.php:12
$filter
Определения iblock_catalog_list.php:54
$selectFields
Определения iblock_catalog_list.php:160
const BX_RESIZE_IMAGE_EXACT
Определения constants.php:12
const SITE_DIR(!defined('LANG'))
Определения include.php:72
$siteId
Определения ajax.php:8
is_set($a, $k=false)
Определения tools.php:2133
Определения ufield.php:9
$user
Определения mysql_to_pgsql.php:33
$GLOBALS['____1690880296']
Определения license.php:1
return false
Определения prolog_main_admin.php:185
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
const SONET_ENTITY_GROUP
Определения include.php:117
const SITE_ID
Определения sonet_set_content_view.php:12
$url
Определения iframe.php:7