Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
eventmanager.php
1<?php
2
4
5use Bitrix\Calendar\Core;
27
29{
30 public const CREATE_PATH = '/calendars/%CALENDAR_ID%/events/';
31 public const EVENT_PATH = '/calendars/%CALENDAR_ID%/events/%EVENT_ID%';
32
41 public function create(Core\Event\Event $event, EventContext $context): Result
42 {
43 $result = new Result();
44
45 try
46 {
47 // TODO: Remake it: move this logic to parent::request().
48 // Or, better, in separate class.
49 $this->httpClient->post(
50 $this->prepareCreateUrl($context),
51 $this->encode((new EventConverter($event))->convertForCreate())
52 );
53
54 if ($this->isRequestSuccess())
55 {
56 $requestResult = $this->prepareResult($this->httpClient->getResult(), $event);
57
58 $result->setData($requestResult);
59 }
60 else
61 {
62 $response = Json::decode($this->httpClient->getResult());
63 if (!empty($response['error']))
64 {
65 $error = $response['error'];
66 switch ($error['code'])
67 {
68 case 409:
69 throw new ConflictException($error['message'], $error['code']);
70 case 401:
71 $this->handleUnauthorize($this->connection);
72 $result->addError(new Error($error['message'], $error['code']));
73 break;
74 default:
75 if (!empty($error['code']))
76 {
77 $result->addError(new Error($error['message'], $error['code']));
78 }
79 else
80 {
81 $result->addError(new Error('Uncknown Google API error', 400));
82 }
83 }
84 }
85 }
86 }
87 catch (ConflictException $e)
88 {
89 if ($event->getUid())
90 {
91 $event->setUid(null);
92 return $this->create($event, $context);
93 }
94 else
95 {
96 $result->addError(new Error($e->getMessage(), $e->getCode()));
97 }
98 }
99 catch (ArgumentException $e)
100 {
101 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
102 $result->addError(new Error('failed to create an event in google'));
103 }
104 catch (ObjectException $e)
105 {
106 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
107 $result->addError(new Error('failed to convert event'));
108 }
109
110 return $result;
111 }
112
123 public function update(Core\Event\Event $event, EventContext $context): Result
124 {
125 $result = new Result();
126
127 try
128 {
129 // TODO: Remake it: move this logic to parent::request().
130 // Or, better, in separate class.
131 $this->httpClient->query(
133 $this->prepareUpdateUrl($context),
134 $this->encode((new EventConverter($event, $context->getEventConnection()))->convertForUpdate())
135 );
136
137 if ($this->isRequestSuccess())
138 {
139 $requestResult = $this->prepareResult($this->httpClient->getResult(), $event);
140 // TODO: move it to core
141 if ($childList = ($context->sync['childList'] ?? null))
142 {
147 foreach ($childList as $eventId => $childEvent)
148 {
149 $childResult = $this->createInstance($childEvent, $context);
150 if ($childResult->isSuccess())
151 {
152 $data = $childResult->getData()['event'];
153 $mapper = new Core\Mappers\EventConnection();
155 $link = $mapper->getMap([
156 '=EVENT_ID' => $childEvent->getId(),
157 '=CONNECTION_ID' => $this->connection->getId(),
158 ])->fetch();
159 if ($link)
160 {
161 $link
162 ->setVendorEventId($data['id'])
163 ->setEntityTag($data['etag'])
164 ->setVendorVersionId($data['version'])
165 ->setRecurrenceId($requestResult['event']['id'])
166 ;
167 $mapper->update($link);
168 }
169 }
170 }
171 }
172 $result->setData($requestResult);
173 }
174 else
175 {
176 $response = Json::decode($this->httpClient->getResult());
177 if (!empty($response['error']) && $response['error']['code'] === 401)
178 {
179 $this->handleUnauthorize($this->connection);
180 $result->addError(new Error($response['error']['message'], $response['error']['code']));
181 }
182 else
183 {
184 $result->addError(new Error('error of updating event', $this->httpClient->getStatus()));
185 }
186 }
187 }
188 catch (ArgumentException $e)
189 {
190 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
191 $result->addError(new Error('failed to update an event in google'));
192 }
193 catch (ObjectException $e)
194 {
195 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
196 $result->addError(new Error('failed to convert event'));
197 }
198
199 return $result;
200 }
201
210 public function delete(Core\Event\Event $event, EventContext $context): Result
211 {
212 $result = new Result();
213
214 try
215 {
216 // TODO: Remake it: move this logic to parent::request().
217 // Or, better, in separate class.
218 $this->httpClient->query(
220 $this->prepareUpdateUrl($context),
221 $this->encode((new EventConverter($event))->convertForDelete())
222 );
223
224 if ($this->isRequestDeleteSuccess())
225 {
226 if ($response = $this->httpClient->getResult())
227 {
228 $requestResult = Json::decode($response);
229 $result->setData($requestResult);
230 }
231 }
232 else
233 {
234 $response = Json::decode($this->httpClient->getResult());
235 if (!empty($response['error']) && $response['error']['code'] === 401)
236 {
237 $this->handleUnauthorize($this->connection);
238 $result->addError(new Error($response['error']['message'], $response['error']['code']));
239 }
240 else
241 {
242 $result->addError(new Error('error of deleting event'));
243 }
244 }
245
246 }
247 catch (ArgumentException $e)
248 {
249 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
250 $result->addError(new Error('failed to delete an event in google'));
251 }
252 catch (ObjectException $e)
253 {
254 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
255 $result->addError(new Error('failed to convert event'));
256 }
257
258 return $result;
259 }
260
266 public function createInstance(Core\Event\Event $event, EventContext $context): Result
267 {
268 $result = new Result();
269
270 $instanceContext = $this->prepareContextForInstance($event, $context);
271 if ($instanceContext === null)
272 {
273 AddMessage2Log('failed to create instance. id='. $event->getId(), 'calendar', 2, true);
274 return $result->addError(new Error('failed to create an instance in google'));
275 }
276
277 try
278 {
279 // TODO: Remake it: move this logic to parent::request().
280 // Or, better, in separate class.
281 $this->httpClient->query(
283 $this->prepareUpdateUrl($instanceContext),
284 $this->encode((new EventConverter($event, $instanceContext->getEventConnection()))->convertForUpdate())
285 );
286
287 if ($this->isRequestSuccess())
288 {
289 $requestResult = $this->prepareResult($this->httpClient->getResult(), $event);
290
291 $result->setData($requestResult);
292 }
293 else
294 {
295 $response = Json::decode($this->httpClient->getResult());
296 if (!empty($response['error']) && $response['error']['code'] === 401)
297 {
298 $this->handleUnauthorize($this->connection);
299 $result->addError(new Error($response['error']['message'], $response['error']['code']));
300 }
301 else
302 {
303 $result->addError(new Error('error of creating instance'));
304 }
305 }
306 }
307 catch (ArgumentException $e)
308 {
309 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
310 $result->addError(new Error('failed to create an instance in google'));
311 }
312
313 return $result;
314 }
315
320 public function updateInstance(Event $event, EventContext $context): Result
321 {
322 $eventLink = $context->sync['instanceLink'];
323
324 if ($eventLink)
325 {
326 $eventContext = (new EventContext())
327 ->setSectionConnection($context->getSectionConnection())
328 ->setEventConnection($eventLink)
329 ;
330
331 $result = $this->update($event, $eventContext);
332 if (!$result->isSuccess() && $result->getErrorCollection()->getErrorByCode(403))
333 {
334 // TODO: implement it
335 }
336
337 return $result;
338 }
339
340 return (new Result())->addError(new Error('Not found link for instance'));
341 }
342
352 public function deleteInstance(Event $event, EventContext $context): Result
353 {
354 $result = new Result();
355 $excludeDate = $context->sync['excludeDate'] ?? null;
356 $originalDate = $context->sync['originalDate'] ?? null;
357
358 $instance = $this->getInstanceForDay($event, $excludeDate, $originalDate);
359 $instanceContext = $this->prepareContextForInstance($instance, $context);
360
361 if ($instanceContext === null)
362 {
363 AddMessage2Log('failed to create instance. id='. $event->getId(), 'calendar', 2, true);
364 return $result->addError(new Error('failed to delete an instance in google'));
365 }
366
367 try
368 {
369 $this->httpClient->query(
371 $this->prepareUpdateUrl($instanceContext),
372 $this->encode((new EventConverter($instance, $instanceContext->getEventConnection()))->convertForDeleteInstance())
373 );
374
375 if ($this->isRequestSuccess())
376 {
377 $requestResult = $this->prepareResult($this->httpClient->getResult(), $event);
378
379 $result->setData($requestResult);
380 }
381 else
382 {
383 $response = Json::decode($this->httpClient->getResult());
384 if (!empty($response['error']) && $response['error']['code'] === 401)
385 {
386 $this->handleUnauthorizeException();
387 }
388
389 $result->addError(new Error('error of delete instance'));
390 }
391 }
392
393 catch (ArgumentException $e)
394 {
395 AddMessage2Log($e->getMessage(), 'calendar', 2, true);
396 $result->addError(new Error('failed to delete an instance in google'));
397 }
398
399 return $result;
400 }
401
406 public function prepareCreateUrl(EventContext $context): string
407 {
408 return Server::mapUri(
409 $this->connection->getServer()->getFullPath() . self::CREATE_PATH,
410 [
411 '%CALENDAR_ID%' => Server::getEncodePath($context->getSectionConnection()->getVendorSectionId()),
412 ]
413 );
414 }
415
421 public function prepareUpdateUrl(EventContext $context): string
422 {
423 return Server::mapUri(
424 $this->connection->getServer()->getFullPath() . self::EVENT_PATH,
425 [
426 '%CALENDAR_ID%' => Server::getEncodePath($context->getSectionConnection()->getVendorSectionId()),
427 '%EVENT_ID%' => Server::getEncodePath($context->getEventConnection()->getVendorEventId()),
428 ]
429 );
430 }
431
442 public function saveRecurrence(
443 SyncEvent $recurrenceEvent,
444 SectionConnection $sectionConnection,
445 Context $context
446 ): Result
447 {
448 $result = new Result();
449
450 if ($recurrenceEvent->getEventConnection())
451 {
452 $masterResult = $this->updateRecurrenceEntity($recurrenceEvent, $context);
453 }
454 else
455 {
456 $masterResult = $this->createRecurrenceEntity($recurrenceEvent, $sectionConnection, $context);
457 }
458
459 if (!$masterResult->isSuccess())
460 {
461 $result->addErrors($masterResult->getErrors());
462 return $result;
463 }
464
465 if ($recurrenceEvent->getInstanceMap())
466 {
468 foreach ($recurrenceEvent->getInstanceMap()->getCollection() as $instance)
469 {
470 if ($instance->getEventConnection())
471 {
472 $instanceResult = $this->updateRecurrenceEntity($instance, $context, $recurrenceEvent->getEventConnection());
473 }
474 else
475 {
476 $instanceResult = $this->createRecurrenceEntity(
477 $instance,
478 $sectionConnection,
479 $context,
480 $recurrenceEvent->getEventConnection()
481 );
482 }
483
484 if (!$result->isSuccess())
485 {
486 $result->addErrors($instanceResult->getErrors());
487 }
488 }
489 }
490
491 return $result;
492 }
493
504 public function createRecurrence(
505 SyncEvent $recurrenceEvent,
506 SectionConnection $sectionConnection,
507 Context $context
508 ): Result
509 {
510 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
511 }
512
523 public function updateRecurrence(
524 SyncEvent $recurrenceEvent,
525 SectionConnection $sectionConnection,
526 Context $context
527 ): Result
528 {
529 return $this->saveRecurrence($recurrenceEvent, $sectionConnection, $context);
530 }
531
532
539 public function prepareContextForInstance(Event $event, EventContext $context): ?EventContext
540 {
541 if (
542 ($context->getEventConnection() === null)
543 || ($context->getSectionConnection() === null)
544 )
545 {
546 return null;
547 }
548
549 $masterVendorEventId = $context->getEventConnection()->getVendorEventId();
550
551 if ($context->getEventConnection()->getEvent()->getVersion() > $event->getVersion())
552 {
553 $event->setVersion($context->getEventConnection()->getEvent()->getVersion());
554 }
555
556 return (new EventContext())
557 ->setSectionConnection($context->getSectionConnection())
558 ->setEventConnection(
559 (new EventConnection())
560 ->setVendorEventId(
561 $this->getInstanceId($masterVendorEventId, $event)
562 )
563 ->setRecurrenceId($masterVendorEventId)
564 );
565 }
566
573 private function getInstanceId(string $masterId, Event $event): string
574 {
575 $base = $masterId . '_';
576 if ($event->isFullDayEvent())
577 {
578 return $base . $event->getOriginalDateFrom()->setTimezone(Util::prepareTimezone())->format('Ymd');
579 }
580 else
581 {
582 return $base . $event->getOriginalDateFrom()->setTimezone(Util::prepareTimezone())->format('Ymd\THis\Z');
583 }
584 }
585
592 public function getInstanceForDay(
593 Event $event,
594 Core\Base\Date $excludeDate,
595 ?Core\Base\Date $originalDate = null
596 ): Event
597 {
598 $instanceEvent = clone $event;
599
600 $instanceEvent->getStart()->getDate()->setDate(
601 $excludeDate->getYear(),
602 $excludeDate->getMonth(),
603 $excludeDate->getDay()
604 );
605 $instanceEvent->getEnd()->getDate()->setDate(
606 $excludeDate->getYear(),
607 $excludeDate->getMonth(),
608 $excludeDate->getDay()
609 );
610 $instanceEvent
611 ->setOriginalDateFrom($originalDate ?? $event->getStart())
612 ->setRecurringRule(null)
613 ;
614
615 return $instanceEvent;
616 }
617
629 private function createRecurrenceEntity(
630 SyncEvent $syncEvent,
631 SectionConnection $sectionConnection,
632 Context $context,
633 EventConnection $masterLink = null
634 ): Result
635 {
636 $eventContext = new EventContext();
637 $eventContext->merge($context);
638
639 $eventContext->setSectionConnection($sectionConnection);
640
641 if ($masterLink)
642 {
643 $eventContext->setEventConnection($masterLink);
644 $result = $this->createInstance($syncEvent->getEvent(), $eventContext);
645 }
646 else
647 {
648 $result = $this->create($syncEvent->getEvent(), $eventContext);
649 }
650
651 if ($result->isSuccess())
652 {
653 if (!$syncEvent->getEvent()->isDeleted())
654 {
655 $link = (new EventConnection())
656 ->setEvent($syncEvent->getEvent())
657 ->setConnection($sectionConnection->getConnection())
658 ->setVersion($syncEvent->getEvent()->getVersion())
659 ->setVendorEventId($result->getData()['event']['id'])
660 ->setEntityTag($result->getData()['event']['etag'])
661 ;
662 $syncEvent->setEventConnection($link);
663 }
664 else
665 {
666 $syncEvent->setAction(Dictionary::SYNC_EVENT_ACTION['delete']);
667 }
668 }
669
670 return $result;
671 }
672
682 private function updateRecurrenceEntity(
683 SyncEvent $syncEvent,
684 Context $context,
685 EventConnection $masterLink = null
686 ): Result
687 {
688 $eventContext = new EventContext();
689 $eventContext->merge($context);
690 if ($masterLink)
691 {
692 $eventContext->setEventConnection($masterLink);
693 $result = $this->updateInstance($syncEvent->getEvent(), $eventContext);
694 }
695 else
696 {
697 $eventContext->setEventConnection($syncEvent->getEventConnection());
698 $result = $this->update($syncEvent->getEvent(), $eventContext);
699 }
700 if ($result->isSuccess())
701 {
702 $syncEvent->getEventConnection()->setEntityTag($result->getData()['event']['etag']);
703 }
704
705 return $result;
706 }
707
711 private function encode(array $event)
712 {
713 return Json::encode($event, JSON_UNESCAPED_SLASHES);
714 }
715
724 private function prepareResult(string $result, Event $event): array
725 {
726 $externalEvent = Json::decode($result);
727
728 // $eventConnection = (new Sync\Google\Builders\BuilderEventConnectionFromExternalEvent($externalEvent, $event, $this->connection))->build();
729 // $syncEvent =
730 // (new Sync\Entities\SyncEvent())
731 // ->setEvent($event)
732 // ->setEventConnection($eventConnection)
733 // ;
734
735 return ['event' => [
736 'id' => $externalEvent['id'],
737 'etag' => $externalEvent['etag'],
738 'version' => $externalEvent['etag'],
739 'recurrence' => $externalEvent['recurringEventId'] ?? null,
740 ]];
741 }
742
743 private function handleUnauthorizeException()
744 {
745 $this->handleUnauthorize($this->connection);
746 }
747}
updateRecurrence(SyncEvent $recurrenceEvent, SectionConnection $sectionConnection, Context $context)
createRecurrence(SyncEvent $recurrenceEvent, SectionConnection $sectionConnection, Context $context)
prepareContextForInstance(Event $event, EventContext $context)
createInstance(Core\Event\Event $event, EventContext $context)
getInstanceForDay(Event $event, Core\Base\Date $excludeDate, ?Core\Base\Date $originalDate=null)
create(Core\Event\Event $event, EventContext $context)
updateInstance(Event $event, EventContext $context)
deleteInstance(Event $event, EventContext $context)
static prepareTimezone(?string $tz=null)
Definition util.php:75
update(Event $event, EventContext $context)