1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
calendarentryajax.php
См. документацию.
1<?php
2namespace Bitrix\Calendar\Controller;
3
4use Bitrix\Calendar\Access\ActionDictionary;
5use Bitrix\Calendar\Access\EventAccessController;
6use Bitrix\Calendar\Access\Model\EventModel;
7use Bitrix\Calendar\Application\Command\CreateEventCommand;
8use Bitrix\Calendar\Application\Command\CreateEventHandler;
9use Bitrix\Calendar\Application\Command\UpdateEventCommand;
10use Bitrix\Calendar\Application\Command\UpdateEventHandler;
11use Bitrix\Calendar\Core\Event\Tools\Dictionary;
12use Bitrix\Calendar\Core\Managers\Accessibility;
13use Bitrix\Calendar\Core\Mappers;
14use Bitrix\Calendar\FileUploader\EventController;
15use Bitrix\Calendar\ICal\IcalIcsBuilder;
16use Bitrix\Calendar\Integration\Disk\File;
17use Bitrix\Calendar\Integration\Disk\FileUploader;
18use Bitrix\Calendar\OpenEvents\Exception\MaxAttendeesReachedException;
19use Bitrix\Calendar\Relation\Item\Relation;
20use Bitrix\Calendar\Relation\RelationProvider;
21use Bitrix\Calendar\Integration\Tasks\TaskQueryParameter;
22use Bitrix\Calendar\Internals\Exception\InvalidDate;
23use Bitrix\Calendar\Rooms;
24use Bitrix\Calendar\Internals;
25use Bitrix\Calendar\Ui\CalendarFilter;
26use Bitrix\Calendar\UserSettings;
27use Bitrix\Calendar\Util;
28use Bitrix\Main\Engine\ActionFilter;
29use Bitrix\Main\Engine\CurrentUser;
30use Bitrix\Main\Error;
31use Bitrix\Main\HttpApplication;
32use Bitrix\Main\HttpRequest;
33use Bitrix\Main\HttpResponse;
34use Bitrix\Main\Localization\Loc;
35use Bitrix\Calendar\Integration\Bitrix24Manager;
36use Bitrix\Intranet;
37use Bitrix\Main\Loader;
38use Bitrix\Main\Web\MimeType;
39use Bitrix\UI\FileUploader\Uploader;
40
41Loc::loadMessages($_SERVER['DOCUMENT_ROOT'].BX_ROOT.'/modules/calendar/lib/controller/calendarajax.php');
42
47{
48 protected const DIRECTION_PREVIOUS = 'previous';
49 protected const DIRECTION_NEXT = 'next';
50 protected const DIRECTION_BOTH = 'both';
51
52 public function configureActions(): array
53 {
54 return [
55 'getIcsFile' => [
56 '-prefilters' => [
57 ActionFilter\Csrf::class,
58 ],
59 '+postfilters' => [
61 ],
62 ],
63 'getIcsFileMobile' => [
64 '-prefilters' => [
65 ActionFilter\Authentication::class,
66 ActionFilter\Csrf::class,
67 ],
68 '+postfilters' => [
70 ],
71 ],
72 ];
73 }
74
75 public function getNearestEventsAction()
76 {
77 if (Loader::includeModule('intranet') && !\Bitrix\Intranet\Util::isIntranetUser())
78 {
79 return [];
80 }
81
82 $request = $this->getRequest();
83 $calendarType = $request->getPost('type');
84 $futureDaysAmount = (int)$request->getPost('futureDaysAmount');
85 $maxEntryAmount = (int)$request->getPost('maxEntryAmount');
86
87 $entries = \CCalendar::getNearestEventsList([
88 'bCurUserList' => true,
89 'fromLimit' => \CCalendar::Date(time(), false),
90 'toLimit' => \CCalendar::Date(time() + \CCalendar::DAY_LENGTH * $futureDaysAmount, false),
91 'type' => $calendarType,
92 'maxAmount' => $maxEntryAmount,
93 ]
94 );
95
96 return [
97 'entries' => $entries,
98 ];
99 }
100
101 public function loadEntriesAction()
102 {
103 $request = $this->getRequest();
104 $monthFrom = (int)$request->getPost('month_from');
105 $yearFrom = (int)$request->getPost('year_from');
106 $monthTo = (int)$request->getPost('month_to');
107 $yearTo = (int)$request->getPost('year_to');
108 $ownerId = (int)$request->getPost('ownerId');
109 $calendarType = $request->getPost('type');
110
111 $direction = $request->getPost('direction');
112 if (!in_array($direction, [self::DIRECTION_PREVIOUS, self::DIRECTION_NEXT, self::DIRECTION_BOTH], true))
113 {
114 $direction = null;
115 }
116
117 $activeSectionIds = is_array($request->getPost('active_sect'))
118 ? $request->getPost('active_sect')
119 : [];
120 $additionalSectionIds = is_array($request->getPost('sup_sect'))
121 ? $request->getPost('sup_sect')
122 : [];
123
124 $sections = [];
125 $limits = \CCalendarEvent::getLimitDates($yearFrom, $monthFrom, $yearTo, $monthTo);
126
127 $connections = false;
128 $fetchTasks = false;
129 $sectionIdList = [];
130
131 foreach(array_unique(array_merge($activeSectionIds, $additionalSectionIds)) as $sectId)
132 {
133 if ($sectId === 'tasks')
134 {
135 $fetchTasks = true;
136 }
137 elseif ((int)$sectId > 0)
138 {
139 $sectionIdList[] = (int)$sectId;
140 }
141 }
142
143 $userId = \CCalendar::GetUserId();
144
145 $isExtranetUser = Loader::includeModule('intranet') && !Intranet\Util::isIntranetUser($userId);
146
147 if (!empty($sectionIdList))
148 {
149 $sect = \CCalendarSect::GetList([
150 'arFilter' => [
151 'ID' => $sectionIdList,
152 'ACTIVE' => 'Y',
153 ],
154 'checkPermissions' => true,
155 ]);
156 foreach($sect as $section)
157 {
158 if ($isExtranetUser && $section['CAL_TYPE'] === Dictionary::CALENDAR_TYPE['location'])
159 {
160 continue;
161 }
162 $sections[] = (int)$section['ID'];
163 }
164 }
165
166 $isBoundaryOfPastReached = false;
167 $isBoundaryOfFutureReached = false;
168 $entries = [];
169 if (!empty($sections))
170 {
171 $entries = $this->getEntries($sections, $limits);
172
173 if (
174 $direction === self::DIRECTION_BOTH
175 && count($this->getShownEntries($entries)) < 5
176 )
177 {
178 $isBoundaryOfPastReached = true;
179 $isBoundaryOfFutureReached = true;
180 //Load all events
181 $limits = [
182 'from' => false,
183 'to' => false,
184 ];
185 $entries = $this->getEntries($sections, $limits);
186
187 if (!empty($entries))
188 {
189 $earliestEvent = $this->getEarliestEvent($entries);
190 $timestamp = strtotime($earliestEvent['DATE_FROM']);
191 if($timestamp < strtotime("01.$monthFrom.$yearFrom"))
192 {
193 $yearFrom = (int)date('Y', $timestamp);
194 $monthFrom = (int)date('m', $timestamp);
195 }
196
197 $latestEvent = $this->getLatestEvent($entries);
198 $timestamp = strtotime($latestEvent['DATE_FROM']);
199 if($timestamp > strtotime("01.$monthTo.$yearTo"))
200 {
201 $yearTo = (int)date('Y', $timestamp);
202 $monthTo = (int)date('m', $timestamp);
203 [$yearTo, $monthTo] = $this->getValidYearAndMonth($yearTo, $monthTo + 1);
204 }
205 }
206 }
207
208 if (
209 ($direction === self::DIRECTION_PREVIOUS)
210 && !$this->hasArrayEntriesInMonth($entries, $yearFrom, $monthFrom)
211 )
212 {
213 //Load one month further
214 [$yearFrom, $monthFrom] = $this->getValidYearAndMonth($yearFrom, $monthFrom - 1);
215 $entries = $this->getEntries($sections, \CCalendarEvent::getLimitDates($yearFrom, $monthFrom, $yearTo, $monthTo));
216
217 if (!$this->hasArrayEntriesInMonth($entries, $yearFrom, $monthFrom))
218 {
219 //Load half year further
220 [$yearFrom, $monthFrom] = $this->getValidYearAndMonth($yearFrom, $monthFrom - 5);
221 $limits = \CCalendarEvent::getLimitDates($yearFrom, $monthFrom, $yearTo, $monthTo);
222 $entries = $this->getEntries($sections, $limits);
223
224 if (!$this->hasArrayEntriesInRange($entries, $yearFrom, $monthFrom, (int)$request->getPost('year_from'), (int)$request->getPost('month_from')))
225 {
226 $isBoundaryOfPastReached = true;
227 //Load all events
228 $limits['from'] = false;
229 $entries = $this->getEntries($sections, $limits);
230
231 if (!empty($entries))
232 {
233 $earliestEvent = $this->getEarliestEvent($entries);
234 $timestamp = strtotime($earliestEvent['DATE_FROM']);
235 $yearFrom = (int)date('Y', $timestamp);
236 $monthFrom = (int)date('m', $timestamp);
237 }
238 }
239 }
240 }
241
242 if (
243 ($direction === self::DIRECTION_NEXT)
244 && !$this->hasArrayEntriesInMonth($entries, $yearTo, $monthTo - 1)
245 )
246 {
247 //Load one month further
248 [$yearTo, $monthTo] = $this->getValidYearAndMonth($yearTo, $monthTo + 1);
249 $entries = $this->getEntries($sections, \CCalendarEvent::getLimitDates($yearFrom, $monthFrom, $yearTo, $monthTo));
250
251 if (!$this->hasArrayEntriesInMonth($entries, $yearTo, $monthTo - 1))
252 {
253 //Load half year further
254 [$yearTo, $monthTo] = $this->getValidYearAndMonth($yearTo, $monthTo + 5);
255 $limits = \CCalendarEvent::getLimitDates($yearFrom, $monthFrom, $yearTo, $monthTo);
256 $entries = $this->getEntries($sections, $limits);
257
258 if (!$this->hasArrayEntriesInRange($entries, (int)$request->getPost('year_to'), (int)$request->getPost('month_to') - 1, $yearTo, $monthTo - 1))
259 {
260 $isBoundaryOfFutureReached = true;
261 //Load all events
262 $limits['to'] = false;
263 $entries = $this->getEntries($sections, $limits);
264
265 if (!empty($entries))
266 {
267 $latestEvent = $this->getLatestEvent($entries);
268 $timestamp = strtotime($latestEvent['DATE_FROM']);
269 $yearTo = (int)date('Y', $timestamp);
270 $monthTo = (int)date('m', $timestamp);
271 [$yearTo, $monthTo] = $this->getValidYearAndMonth($yearTo, $monthTo + 1);
272 }
273 }
274 }
275 }
276 }
277
279 foreach ($entries as $key => $entry)
280 {
281 $eventModel = EventModel::createFromArray($entry);
282 $canEditEventInParentSection = $accessController->check(ActionDictionary::ACTION_EVENT_EDIT, $eventModel);
283 $canEditEventInCurrentSection = $accessController->check(ActionDictionary::ACTION_EVENT_EDIT, $eventModel, [
284 'checkCurrentEvent' => 'Y',
285 ]);
286 $entries[$key]['permissions'] = [
287 'edit' => $canEditEventInParentSection && $canEditEventInCurrentSection,
288 'edit_attendees' => $accessController->check(ActionDictionary::ACTION_EVENT_EDIT_ATTENDEES, $eventModel),
289 'edit_location' => $accessController->check(ActionDictionary::ACTION_EVENT_EDIT_LOCATION, $eventModel),
290 ];
291 }
292
293 // **** GET TASKS ****
294 if ($fetchTasks)
295 {
296 $tasksEntries = \CCalendar::getTaskList(
297 (new TaskQueryParameter($this->getCurrentUser()->getId()))
298 ->setType($calendarType)
299 ->setOwnerId($ownerId)
300 );
301
302 if (!empty($tasksEntries))
303 {
304 $entries = array_merge($entries, $tasksEntries);
305 }
306 }
307
308 $response = [
309 'entries' => $entries,
310 'userIndex' => \CCalendarEvent::getUserIndex(),
311 'isBoundaryOfPastReached' => $isBoundaryOfPastReached,
312 'isBoundaryOfFutureReached' => $isBoundaryOfFutureReached,
313 ];
314 if (is_array($connections))
315 {
316 $response['connections'] = $connections;
317 }
318
319 if (
320 (int)$request->getPost('month_from') !== $monthFrom
321 || (int)$request->getPost('year_from') !== $yearFrom
322 )
323 {
324 $response['newYearFrom'] = $yearFrom;
325 $response['newMonthFrom'] = $monthFrom;
326 }
327
328 if (
329 (int)$request->getPost('month_to') !== $monthTo
330 || (int)$request->getPost('year_to') !== $yearTo
331 )
332 {
333 $response['newYearTo'] = $yearTo;
334 $response['newMonthTo'] = $monthTo;
335 }
336
337 return $response;
338 }
339
340 protected function getShownEntries(array $entries): array
341 {
342 return CalendarFilter::filterByShowDeclined($entries);
343 }
344
345 protected function getEntries(array $sections, array $limits): array
346 {
347 return \CCalendarEvent::GetList(
348 [
349 'arFilter' => [
350 'SECTION' => $sections,
351 'FROM_LIMIT' => $limits['from'],
352 'TO_LIMIT' => $limits['to'],
353 ],
354 'parseRecursion' => true,
355 'fetchAttendees' => true,
356 'userId' => \CCalendar::GetCurUserId(),
357 'setDefaultLimit' => false,
358 ]
359 );
360 }
361
362 protected function getValidYearAndMonth(int $year, int $month): array
363 {
364 if ($month <= 0)
365 {
366 return [$year - 1, $month + 12];
367 }
368
369 if ($month > 12)
370 {
371 return [$year + 1, $month - 12];
372 }
373
374 return [$year, $month];
375 }
376
377 protected function hasArrayEntriesInMonth(array $entries, int $yearFrom, int $monthFrom): bool
378 {
379 return $this->hasArrayEntriesInRange($entries, $yearFrom, $monthFrom, $yearFrom, $monthFrom);
380 }
381
382 protected function hasArrayEntriesInRange(array $entries, int $yearFrom, int $monthFrom, int $yearTo, int $monthTo): bool
383 {
384 $monthsFrom = $yearFrom * 12 + $monthFrom;
385 $monthsTo = $yearTo * 12 + $monthTo;
387 $showDeclined = $settings['showDeclined'];
388 foreach ($entries as $entry)
389 {
390 if (!$showDeclined && $entry['MEETING_STATUS'] === 'N')
391 {
392 continue;
393 }
394
395 $timestamp = strtotime($entry['DATE_FROM']);
396 $entryYear = (int)date('Y', $timestamp);
397 $entryMonth = (int)date('m', $timestamp);
398 $entryMonths = $entryYear * 12 + $entryMonth;
399
400 if ($entryMonths >= $monthsFrom && $entryMonths <= $monthsTo)
401 {
402 return true;
403 }
404 }
405 return false;
406 }
407
408 protected function getEarliestEvent(array $entries): array
409 {
410 return array_reduce($entries, static function($firstEntry, $entry) {
411 if (!$firstEntry)
412 {
413 return $entry;
414 }
415 if (strtotime($entry['DATE_FROM']) < strtotime($firstEntry['DATE_FROM']))
416 {
417 return $entry;
418 }
419 return $firstEntry;
420 });
421 }
422
423 protected function getLatestEvent(array $entries): array
424 {
425 return array_reduce($entries, static function($lastEntry, $entry) {
426 if (!$lastEntry)
427 {
428 return $entry;
429 }
430 if (strtotime($entry['DATE_FROM']) > strtotime($lastEntry['DATE_FROM']))
431 {
432 return $entry;
433 }
434 return $lastEntry;
435 });
436 }
437
438 public function moveEventAction()
439 {
440 $request = $this->getRequest();
441 $userId = \CCalendar::getCurUserId();
442 $id = (int)$request->getPost('id');
443 $sectionId = (int)$request->getPost('section');
444
445 if ($id)
446 {
447 $eventModel = \CCalendarEvent::getEventModelForPermissionCheck(
448 eventId: $id,
449 userId: $userId
450 );
451 }
452 else
453 {
454 $section = \CCalendarSect::GetById($sectionId);
455
456 $eventModel =
457 EventModel::createNew()
458 ->setOwnerId((int)($section['OWNER_ID'] ?? 0))
459 ->setSectionId($sectionId ?? 0)
460 ->setSectionType($section['TYPE'] ?? '')
461 ;
462 }
464 if (
465 (!$id && !$accessController->check(ActionDictionary::ACTION_EVENT_ADD, $eventModel))
466 || ($id && !$accessController->check(ActionDictionary::ACTION_EVENT_EDIT, $eventModel))
467 )
468 {
469 $this->addError(new Error(Loc::getMessage('EC_ACCESS_DENIED'), 'move_entry_access_denied'));
470 }
471
472 $sectionList = Internals\SectionTable::getList([
473 'filter' => [
474 '=ACTIVE' => 'Y',
475 '=ID' => $sectionId,
476 ],
477 'select' => [
478 'ID',
479 'CAL_TYPE',
480 'OWNER_ID',
481 'NAME',
482 ],
483 ]
484 );
485
486 if (!($section = $sectionList->fetch()))
487 {
488 $this->addError(new Error(Loc::getMessage('EC_SECTION_NOT_FOUND'), 'edit_entry_section_not_found'));
489 }
490
491 if (
492 $section['CAL_TYPE'] !== 'group'
493 && Loader::includeModule('intranet') && !Intranet\Util::isIntranetUser($userId)
494 )
495 {
496 $this->addError(new Error(Loc::getMessage('EC_ACCESS_DENIED'), 'edit_entry_extranet_access_denied'));
497 }
498
499
500 if (empty($this->getErrors()))
501 {
502 $entry = Internals\EventTable::getList(
503 [
504 "filter" => [
505 "=ID" => $id,
506 "=DELETED" => 'N',
507 "=SECTION_ID" => $sectionId,
508 ],
509 "select" => ["ID", "CAL_TYPE"],
510 ]
511 )->fetch();
512
513 if (Loader::includeModule('intranet'))
514 {
515 if ($entry['CAL_TYPE'] !== 'group' && !Intranet\Util::isIntranetUser($userId))
516 {
517 $this->addError(new Error(Loc::getMessage('EC_ACCESS_DENIED'), 'edit_entry_extranet_access_denied'));
518 }
519 }
520 }
521
522 $requestUid = (int)$request->getPost('requestUid');
523 $reload = $request->getPost('recursive') === 'Y';
524 $sendInvitesToDeclined = $request->getPost('sendInvitesAgain') === 'Y';
525 $skipTime = $request->getPost('skip_time') === 'Y';
526 $dateFrom = $request->getPost('date_from');
527 $dateTo = $request->getPost('date_to');
528 $timezone = $request->getPost('timezone');
529 $attendees = $request->getPost('attendees');
530 $location = trim((string) $request->getPost('location'));
531 $isPlannerFeatureEnabled = Bitrix24Manager::isPlannerFeatureEnabled();
532
533 $locationBusyWarning = false;
534 $busyWarning = false;
535
536 if (empty($this->getErrors()))
537 {
538 $arFields = [
539 "ID" => $id,
540 "DATE_FROM" => \CCalendar::Date(\CCalendar::Timestamp($dateFrom), !$skipTime),
541 "SKIP_TIME" => $skipTime,
542 ];
543
544 if (!empty($dateTo))
545 {
546 $arFields["DATE_TO"] = \CCalendar::Date(\CCalendar::Timestamp($dateTo), !$skipTime);
547 }
548
549 if (!$skipTime && $request->getPost('set_timezone') === 'Y' && $timezone)
550 {
551 $arFields["TZ_FROM"] = $timezone;
552 $arFields["TZ_TO"] = $timezone;
553 }
554
555 if (
556 $isPlannerFeatureEnabled
557 && !empty($location)
559 )
560 {
561 $locationBusyWarning = true;
562 $reload = true;
563 }
564
565 if (
566 $isPlannerFeatureEnabled
567 && is_array($attendees)
568 && $request->getPost('is_meeting') === 'Y'
569 )
570 {
571 $timezoneName = \CCalendar::GetUserTimezoneName(\CCalendar::GetUserId());
572 $timezoneOffset = Util::getTimezoneOffsetUTC($timezoneName);
573 $timestampFrom = \CCalendar::TimestampUTC($arFields["DATE_FROM"]) - $timezoneOffset;
574 $timestampTo = \CCalendar::TimestampUTC($arFields["DATE_TO"]) - $timezoneOffset;
575 if (!empty($this->getBusyUsersIds($attendees, $id, $timestampFrom, $timestampTo)))
576 {
577 $busyWarning = true;
578 $reload = true;
579 }
580 }
581
582 if (!$busyWarning && !$locationBusyWarning)
583 {
584 if ($request->getPost('recursive') === 'Y')
585 {
586 \CCalendar::SaveEventEx(
587 [
588 'arFields' => $arFields,
589 'silentErrorMode' => false,
590 'recursionEditMode' => 'this',
591 'currentEventDateFrom' => \CCalendar::Date(
592 \CCalendar::Timestamp($request->getPost('current_date_from')),
593 false
594 ),
595 'sendInvitesToDeclined' => $sendInvitesToDeclined,
596 'requestUid' => $requestUid,
597 ]
598 );
599 }
600 else
601 {
602 $id = \CCalendar::SaveEvent(
603 [
604 'arFields' => $arFields,
605 'silentErrorMode' => false,
606 'sendInvitesToDeclined' => $sendInvitesToDeclined,
607 'requestUid' => $requestUid,
608 ]
609 );
610 }
611 }
612 }
613
614 return [
615 'id' => $id,
616 'reload' => $reload,
617 'busy_warning' => $busyWarning,
618 'location_busy_warning' => $locationBusyWarning,
619 ];
620 }
621
622 public function editEntryAction()
623 {
624 $response = [];
625 $request = $this->getRequest();
626 $id = (int)($request['id'] ?? null);
627 $userId = \CCalendar::getCurUserId();
628 $dates = $this->getDates($request);
629 $reminderList = \CCalendarReminder::prepareReminder($request['reminder']);
630 $newId = null;
631
632 try
633 {
634 if ($id)
635 {
636 $command = $this->getUpdateCommand($request);
637 $event = (new UpdateEventHandler())($command);
638 }
639 else
640 {
641 $command = $this->getCreateCommand($request);
642 $event = (new CreateEventHandler())($command);
643 }
644 $newId = $event->getId();
645 $response['reload'] = true;
646 }
647 catch (Internals\Exception\PermissionDenied)
648 {
649 $this->addError(new Error(Loc::getMessage('EC_ACCESS_DENIED'), 'edit_entry_access_denied'));
650 }
651 catch (Internals\Exception\SectionNotFound)
652 {
653 $this->addError(new Error(Loc::getMessage('EC_SECTION_NOT_FOUND'), 'edit_entry_section_not_found'));
654 }
655 catch (Internals\Exception\ExtranetPermissionDenied)
656 {
657 $this->addError(new Error(Loc::getMessage('EC_ACCESS_DENIED'), 'edit_entry_extranet_access_denied'));
658 }
660 {
661 $this->addError(new Error(Loc::getMessage('EC_JS_EV_FROM_ERR')));
662 }
663 catch (Internals\Exception\LocationBusy)
664 {
665 $this->addError(new Error(Loc::getMessage('EC_LOCATION_BUSY'), 'edit_entry_location_busy'));
666 }
667 catch (Internals\Exception\AttendeeBusy $e)
668 {
669 $this->addError(
670 new Error(Loc::getMessage('EC_USER_BUSY', ['#USER#' => $e->getAttendeeName()]), 'edit_entry_user_busy')
671 );
672 $response['busyUsersList'] = $e->getBusyUsersList();
673 }
674 catch (Internals\Exception\EditException)
675 {
676 $this->addError(new Error(Loc::getMessage('EC_JS_EV_SAVE_ERR'), 'edit_entry_save'));
677 }
678 catch (Internals\Exception\EventNotFound)
679 {
680 $this->addError(new Error('Event not found', 'edit_entry_event_not_found'));
681 }
683 {
684 $this->addError(new Error(Loc::getMessage('EC_LOCATION_BUSY_RECURRENCE'), 'edit_entry_location_busy_recurrence'));
685 $this->addError(new Error($e->getMessage(), 'edit_entry_location_repeat_busy'));
686 }
688 {
689 $this->addError(new Error('MAX ATTENDEES_REACHED', 'edit_entry_max_attendees_reached'));
690 }
691
692 // Exit if any error
693 if (!empty($this->getErrors()))
694 {
695 return $response;
696 }
697
698 $eventList = [];
699 $eventIdList = [$newId];
700
701 if ($newId)
702 {
703 $response['entryId'] = $newId;
704
705 $filter = [
706 'ID' => $newId,
707 'FROM_LIMIT' => \CCalendar::Date(
708 \CCalendar::Timestamp($dates['date_from']) -
709 \CCalendar::DAY_LENGTH * 10, false
710 ),
711 'TO_LIMIT' => \CCalendar::Date(
712 \CCalendar::Timestamp($dates['date_to']) +
713 \CCalendar::DAY_LENGTH * 90, false
714 ),
715 ];
716
717 $eventList = \CCalendarEvent::GetList(
718 [
719 'arFilter' => $filter,
720 'parseRecursion' => true,
721 'fetchAttendees' => true,
722 'userId' => \CCalendar::GetUserId(),
723 ]
724 );
725
726 if (isset($_REQUEST['rec_edit_mode']) && in_array($_REQUEST['rec_edit_mode'], ['this', 'next']))
727 {
728 unset($filter['ID']);
729 $filter['RECURRENCE_ID'] = ($eventList && $eventList[0] && $eventList[0]['RECURRENCE_ID']) ? $eventList[0]['RECURRENCE_ID'] : $newId;
730
731 $resRelatedEvents = \CCalendarEvent::GetList(
732 [
733 'arFilter' => $filter,
734 'parseRecursion' => true,
735 'fetchAttendees' => true,
736 'userId' => \CCalendar::GetUserId(),
737 ]
738 );
739
740 foreach($resRelatedEvents as $ev)
741 {
742 $eventIdList[] = $ev['ID'];
743 }
744 $eventList = array_merge($eventList, $resRelatedEvents);
745 }
746 else if ($id && $eventList && $eventList[0] && \CCalendarEvent::CheckRecurcion($eventList[0]))
747 {
748 $recId = $eventList[0]['RECURRENCE_ID'] ?? $eventList[0]['ID'];
749
750 if ($eventList[0]['RECURRENCE_ID'] && $eventList[0]['RECURRENCE_ID'] !== $eventList[0]['ID'])
751 {
752 unset($filter['RECURRENCE_ID']);
753 $filter['ID'] = $eventList[0]['RECURRENCE_ID'];
754 $resRelatedEvents = \CCalendarEvent::GetList(
755 [
756 'arFilter' => $filter,
757 'parseRecursion' => true,
758 'fetchAttendees' => true,
759 'userId' => \CCalendar::GetUserId(),
760 ]
761 );
762 $eventIdList[] = $eventList[0]['RECURRENCE_ID'];
763 $eventList = array_merge($eventList, $resRelatedEvents);
764 }
765
766 if ($recId)
767 {
768 unset($filter['ID']);
769 $filter['RECURRENCE_ID'] = $recId;
770 $resRelatedEvents = \CCalendarEvent::GetList(
771 [
772 'arFilter' => $filter,
773 'parseRecursion' => true,
774 'fetchAttendees' => true,
775 'userId' => \CCalendar::GetUserId(),
776 ]
777 );
778
779 foreach($resRelatedEvents as $ev)
780 {
781 $eventIdList[] = $ev['ID'];
782 }
783 $eventList = array_merge($eventList, $resRelatedEvents);
784 }
785 }
786 }
787
788 $pathToCalendar = \CCalendar::GetPathForCalendarEx($userId);
789 foreach($eventList as $ind => $event)
790 {
791 $eventList[$ind]['~URL'] = \CHTTP::urlAddParams($pathToCalendar, ['EVENT_ID' => $event['ID']]);
792 }
793
794 $response['eventList'] = $eventList;
795 $response['eventIdList'] = $eventIdList;
796
797 $userSettings = UserSettings::get($userId);
798 $userSettings['defaultReminders'][$dates['skip_time'] ? 'fullDay' : 'withTime'] = $reminderList;
799 UserSettings::set($userSettings, $userId);
800
801 return $response;
802 }
803
807 private function getCreateCommand(HttpRequest $request): CreateEventCommand
808 {
809 return new CreateEventCommand(...$this->getCommandFields($request));
810 }
811
815 private function getUpdateCommand(HttpRequest $request): UpdateEventCommand
816 {
817 $id = (int)($request['id'] ?? null);
818
819 return new UpdateEventCommand($id, ...$this->getCommandFields($request));
820 }
821
825 private function getCommandFields(HttpRequest $request): array
826 {
827 $id = (int)($request['id'] ?? null);
828 $sectionId = (int)($request['section'] ?? null);
829 $requestUid = (int)($request['requestUid'] ?? null);
830 $userId = \CCalendar::getCurUserId();
831 $dates = $this->getDates($request);
832 $timezone = $this->getTimeZones($request);
833 $reminderList = \CCalendarReminder::prepareReminder($request['reminder']);
834
835 return [
836 $userId,
837 $sectionId,
838 $dates['date_from'],
839 $dates['date_to'],
840 $dates['skip_time'],
841 $timezone['timezone_from'],
842 $timezone['timezone_to'],
843 $this->getName($request),
844 trim($request['desc'] ?? ''),
845 $request['color'] ?? null,
846 $request['accessibility'] ?? null,
847 $request['importance'] ?? 'normal',
848 ($request['private_event'] ?? 'N') === 'Y',
849 $this->prepareRecurringRule($request['EVENT_RRULE'] ?? null, $request['rrule_endson'] ?? null),
850 $request['location'] ?? '',
851 $reminderList,
852 (int)$request['meeting_host'] ?: $userId,
853 (int)($request['chat_id'] ?? null),
854 $request['attendeesEntityList'] ?? null,
855 $request['exclude_users'] ?? null,
856 ($request['meeting_notify'] ?? 'N') === 'Y',
857 ($request['meeting_reinvite'] ?? 'N') === 'Y',
858 ($request['allow_invite'] ?? 'N') === 'Y',
859 ($request['hide_guests'] ?? 'N') === 'Y',
860 Bitrix24Manager::isPlannerFeatureEnabled(),
861 !$id || $request->getPost('checkCurrentUsersAccessibility') !== 'N',
862 $request['newAttendeesList'] ?? null,
863 !empty($request['rec_edit_mode']) ? $request['rec_edit_mode'] : null,
864 $this->getCurrentDateFrom($request),
865 $this->getUFfields($id, $request),
866 ($request['sendInvitesAgain'] ?? 'N') === 'Y',
867 $requestUid,
868 ($request['doCheckOccupancy'] ?? 'N') === 'Y',
869 (int)($request['max_attendees'] ?? 0),
870 $request['category'] !== null ? (int)$request['category'] : null,
871 $request['analyticsSubSection'] ?? null,
872 (int)($request['analyticsChatId'] ?? 0),
873 ];
874 }
875
876 private function getUFfields(int $eventId, HttpRequest $request): array
877 {
878 $arUFFields = [];
879 foreach($request as $field => $value)
880 {
881 if (mb_strpos($field, 'UF_') === 0)
882 {
883 $arUFFields[$field] = $value;
884 }
885 }
886
887 $uploadedFiles = $this->handleUploadingFiles($eventId, $request);
888 if (!empty($uploadedFiles))
889 {
890 if (isset($arUFFields['UF_WEBDAV_CAL_EVENT']) && is_array($arUFFields['UF_WEBDAV_CAL_EVENT']))
891 {
892 $arUFFields['UF_WEBDAV_CAL_EVENT'] = [...$arUFFields['UF_WEBDAV_CAL_EVENT'], ...$uploadedFiles];
893 }
894 else
895 {
896 $arUFFields['UF_WEBDAV_CAL_EVENT'] = ['', ...$uploadedFiles];
897 }
898 }
899
900 return $arUFFields;
901 }
902
903 private function handleUploadingFiles(int $eventId, HttpRequest $request): array
904 {
905 $result = [];
906
907 if (empty($request['uploaded_files']) || !is_array($request['uploaded_files']))
908 {
909 return $result;
910 }
911
912 $controller = new EventController(['eventId' => $eventId]);
913 $uploader = new Uploader($controller);
914 $fileUploader = new FileUploader(\CCalendar::getCurUserId());
915
916 $pendingFiles = $uploader->getPendingFiles($request['uploaded_files']);
917
918 foreach ($pendingFiles->getFileIds() as $fileId)
919 {
920 $addingResult = $fileUploader->addFile($fileId);
921 if ($addingResult->isSuccess())
922 {
923 $result[] = $addingResult->getData()['ATTACHMENT_ID'];
924 }
925 }
926 $pendingFiles->makePersistent();
927
928 return $result;
929 }
930
931 private function getCurrentDateFrom(HttpRequest $request): ?string
932 {
933 return !empty($request['current_date_from'])
934 ? \CCalendar::Date(\CCalendar::Timestamp($request['current_date_from']), false)
935 : null;
936 }
937
938 private function getBusyUsersIds(array $attendees, int $curEventId, int $fromTs, int $toTs): array
939 {
940 $usersToCheck = $this->getUsersToCheck($attendees);
941 if (empty($usersToCheck))
942 {
943 return [];
944 }
945
946 return (new Accessibility())
947 ->setSkipEventId($curEventId)
948 ->getBusyUsersIds($usersToCheck, $fromTs, $toTs);
949 }
950
951 private function getUsersToCheck(array $attendees): array
952 {
953 $usersToCheck = [];
954 foreach ($attendees as $attId)
955 {
956 if ((int)$attId !== \CCalendar::GetUserId())
957 {
958 $userSettings = UserSettings::get((int)$attId);
959 if ($userSettings && $userSettings['denyBusyInvitation'])
960 {
961 $usersToCheck[] = (int)$attId;
962 }
963 }
964 }
965 return $usersToCheck;
966 }
967
968 private function prepareRecurringRule(array $rrule = null, ?string $endMode = 'never'): ?array
969 {
970 if (empty($rrule) || !is_array($rrule))
971 {
972 return null;
973 }
974 if (!isset($rrule['INTERVAL']) && $rrule['FREQ'] !== 'NONE')
975 {
976 $rrule['INTERVAL'] = 1;
977 }
978 if ($endMode === 'never')
979 {
980 unset($rrule['COUNT'], $rrule['UNTIL']);
981 }
982 elseif ($endMode === 'count')
983 {
984 if ((int)$rrule['COUNT'] <= 0)
985 {
986 $rrule['COUNT'] = 10;
987 }
988 unset($rrule['UNTIL']);
989 }
990 elseif ($endMode === 'until')
991 {
992 unset($rrule['COUNT']);
993 }
994
995 return $rrule;
996 }
997
998 private function getName(HttpRequest $request): string
999 {
1000 return (empty($request['name']))
1001 ? Loc::getMessage('EC_DEFAULT_EVENT_NAME_V2')
1002 : trim($request['name']);
1003 }
1004
1008 private function getDates(HttpRequest $request): array
1009 {
1010 // Date & Time
1011 $dateFrom = $request['date_from'] ?? '';
1012 $dateTo = $request['date_to'] ?? '';
1013 $skipTime = isset($request['skip_time']) && $request['skip_time'] === 'Y';
1014 if (!$skipTime)
1015 {
1016 $dateFrom .= ' ' . $request['time_from'] ?? '';
1017 $dateTo .= ' ' . $request['time_to'] ?? '';
1018 }
1019 $dateFrom = trim($dateFrom);
1020 $dateTo = trim($dateTo);
1021
1022 if (
1023 (int)(new \DateTime())->setTimestamp(\CCalendar::Timestamp($dateFrom))->format('Y') > 9999
1024 || (int)(new \DateTime())->setTimestamp(\CCalendar::Timestamp($dateTo))->format('Y') > 9999
1025 )
1026 {
1027 throw new Internals\Exception\InvalidDate();
1028 }
1029
1030 return [
1031 'date_from' => $dateFrom,
1032 'date_to' => $dateTo,
1033 'skip_time' => $skipTime,
1034 ];
1035 }
1036
1037 private function getTimeZones(HttpRequest $request): array
1038 {
1039 $tzFrom = $request['tz_from'] ?? '';
1040 $tzTo = $request['tz_to'] ?? '';
1041 if (!$tzFrom && isset($request['default_tz']))
1042 {
1043 $tzFrom = $request['default_tz'];
1044 }
1045 if (!$tzTo && isset($request['default_tz']))
1046 {
1047 $tzTo = $request['default_tz'];
1048 }
1049
1050 if (isset($request['default_tz']) && (string)$request['default_tz'] !== '')
1051 {
1052 \CCalendar::SaveUserTimezoneName(\CCalendar::GetUserId(), $request['default_tz']);
1053 }
1054
1055 return [
1056 'timezone_from' => $tzFrom,
1057 'timezone_to' => $tzTo,
1058 ];
1059 }
1060
1061 public function getEventEntityRelationAction(int $eventId): ?Relation
1062 {
1063 if (Loader::includeModule('intranet') && !\Bitrix\Intranet\Util::isIntranetUser())
1064 {
1065 return null;
1066 }
1067
1068 $userId = \CCalendar::GetUserId();
1070 $hasAccess = $accessController->check(
1071 ActionDictionary::ACTION_EVENT_VIEW_FULL,
1072 \CCalendarEvent::getEventModelForPermissionCheck(
1073 eventId: $eventId,
1074 userId: $userId
1075 )
1076 );
1077 if (!$hasAccess)
1078 {
1079 $this->addError(new Error('Event access denied'));
1080
1081 return null;
1082 }
1083
1084 $getRelationResult = (new RelationProvider())->getEventRelation($userId, $eventId);
1085 if (!empty($getRelationResult->getErrors()))
1086 {
1087 $this->addErrors($getRelationResult->getErrors());
1088
1089 return null;
1090 }
1091
1092 return $getRelationResult->getRelation();
1093 }
1094
1095 public function getIcsContentAction(CurrentUser $currentUser, int $eventId): ?string
1096 {
1097 $canAccess = EventAccessController::can(
1098 $currentUser->getId(),
1099 ActionDictionary::ACTION_EVENT_VIEW_FULL,
1100 $eventId
1101 );
1102
1103 if (!$canAccess)
1104 {
1105 $this->addError(new Error('Event access denied'));
1106
1107 return null;
1108 }
1109
1110 $event = (new Mappers\Event())->getById($eventId);
1111
1112 return IcalIcsBuilder::buildFromEvent($event)->getContent();
1113 }
1114
1115 public function getIcsFileAction(CurrentUser $currentUser, int $eventId): string|HttpResponse
1116 {
1117 return $this->prepareIcsResponse($currentUser, $eventId);
1118 }
1119
1120 public function getIcsFileMobileAction(CurrentUser $currentUser, string $hitHash, int $eventId): ?HttpResponse
1121 {
1122 $httpResponse = new HttpResponse();
1123 $httpResponse->addHeader('Location', 'bitrix24://');
1124
1125 if (empty($eventId) || empty($hitHash))
1126 {
1127 return $httpResponse;
1128 }
1129
1130 if (!$GLOBALS['USER']->LoginHitByHash($hitHash, false, true))
1131 {
1132 return $httpResponse;
1133 }
1134
1135 HttpApplication::getInstance()->getSession()->set('MOBILE_OAUTH', true);
1136
1137 return $this->prepareIcsResponse($currentUser, $eventId);
1138 }
1139
1140 private function prepareIcsResponse(
1141 CurrentUser $currentUser,
1142 int $eventId,
1143 ): ?HttpResponse
1144 {
1145 $canAccess = EventAccessController::can(
1146 $currentUser->getId(),
1147 ActionDictionary::ACTION_EVENT_VIEW_FULL,
1148 $eventId
1149 );
1150
1151 // TODO: response with error should be here
1152 if (!$canAccess)
1153 {
1154 $this->addError(new Error('Event access denied'));
1155
1156 return null;
1157 }
1158
1159 $event = (new Mappers\Event())->getById($eventId);
1160
1162
1163 $fileType = 'ics';
1164 $mimeType = MimeType::getByFileExtension($fileType);
1165
1166 $httpResponse = new HttpResponse();
1167 $httpResponse->addHeader('Content-Type', "{$mimeType}; charset=utf-8");
1168 $httpResponse->addHeader('Content-Disposition', "attachment;filename=event.{$fileType}");
1169 $httpResponse->setContent($fileContent);
1170
1171 return $httpResponse;
1172 }
1173}
const BX_ROOT
Определения bx_root.php:3
$accessController
Определения options.php:23
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
if(!is_object($USER)||! $USER->IsAuthorized()) $userId
Определения check_mail.php:18
Определения calendarentryajax.php:47
const DIRECTION_BOTH
Определения calendarentryajax.php:50
editEntryAction()
Определения calendarentryajax.php:622
getIcsFileMobileAction(CurrentUser $currentUser, string $hitHash, int $eventId)
Определения calendarentryajax.php:1120
getEntries(array $sections, array $limits)
Определения calendarentryajax.php:345
getShownEntries(array $entries)
Определения calendarentryajax.php:340
getValidYearAndMonth(int $year, int $month)
Определения calendarentryajax.php:362
loadEntriesAction()
Определения calendarentryajax.php:101
moveEventAction()
Определения calendarentryajax.php:438
hasArrayEntriesInMonth(array $entries, int $yearFrom, int $monthFrom)
Определения calendarentryajax.php:377
getIcsContentAction(CurrentUser $currentUser, int $eventId)
Определения calendarentryajax.php:1095
hasArrayEntriesInRange(array $entries, int $yearFrom, int $monthFrom, int $yearTo, int $monthTo)
Определения calendarentryajax.php:382
const DIRECTION_PREVIOUS
Определения calendarentryajax.php:48
configureActions()
Определения calendarentryajax.php:52
getNearestEventsAction()
Определения calendarentryajax.php:75
getEarliestEvent(array $entries)
Определения calendarentryajax.php:408
getEventEntityRelationAction(int $eventId)
Определения calendarentryajax.php:1061
const DIRECTION_NEXT
Определения calendarentryajax.php:49
getLatestEvent(array $entries)
Определения calendarentryajax.php:423
getIcsFileAction(CurrentUser $currentUser, int $eventId)
Определения calendarentryajax.php:1115
static buildFromEvent(Event $eventObject)
Определения icalicsbuilder.php:31
static checkAccessibility(string $locationId='', array $params=[])
Определения accessibilitymanager.php:93
static set($settings=[], $userId=false)
Определения usersettings.php:50
static get($userId=null)
Определения usersettings.php:86
static getTimezoneOffsetUTC(string $timezoneName)
Определения util.php:767
addError(Error $error)
Определения controller.php:1070
addErrors(array $errors)
Определения controller.php:1083
Определения error.php:15
static prepareReminder($reminder=[])
Определения calendar_reminder.php:431
static urlAddParams($url, $add_params, $options=[])
Определения http.php:521
$arFields
Определения dblapprove.php:5
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$fileContent
Определения file_property.php:47
$_REQUEST["admin_mnu_menu_id"]
Определения get_menu.php:8
$result
Определения get_property_values.php:14
$filter
Определения iblock_catalog_list.php:54
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
setTimestamp(int $timestamp=null)
Определения messagetrait.php:147
$GLOBALS['____1690880296']
Определения license.php:1
$year
Определения payment.php:9
$settings
Определения product_settings.php:43
$event
Определения prolog_after.php:141
$direction
Определения prolog_auth_admin.php:25
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
</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
$location
Определения options.php:2729
$response
Определения result.php:21