1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
icsbuilder.php
См. документацию.
1<?php
2namespace Bitrix\Calendar\ICal;
3
4use Bitrix\Calendar\Core\Base\DateTimeZone;
5use Bitrix\Calendar\Core\Event\Properties\ExcludedDatesCollection;
6use Bitrix\Calendar\Core\Event\Properties\RecurringEventRules;
7
8class IcsBuilder {
9 //const DT_FORMAT = 'Ymd\THis\Z';
10 const
11 PRODID = '-//Bitrix//Bitrix Calendar//EN',
12 DATE_FORMAT = 'Ymd',
13 DATETIME_FORMAT = 'Ymd\THis',
14 TIME_FORMAT = 'His',
15 UTC_DATETIME_FORMAT = 'Ymd\\THis\\Z',
17 ;
18
19 protected const DAY_LENGTH = 86400;
20
21 protected
22 $fullDayMode = false,
28
29 protected ?RecurringEventRules $rrule = null;
31
32 private
33 $availableProperties = [
34 'summary',
35 'description',
36 'dtstart',
37 'dtend',
38 'dtstamp',
39 'location',
40 'url',
41 'alarm',
42 'transp',
43 'status',
44 'uid',
45 'attendee',
46 'created',
47 'last-modified',
48 'sequence',
49 'transp',
50 'rrule',
51 'priority',
52 ];
53 private static
54 $METHOD = 'REQUEST';
55
56
62 public function __construct($properties = [], $config = [])
63 {
65 $this->setConfig($config);
66 }
67
68 public function setProperties($properties)
69 {
70 if (is_array($properties))
71 {
72 foreach ($properties as $key => $value)
73 {
74 if (in_array($key, $this->availableProperties) && !empty($value))
75 {
76 $this->properties[$key] = $this->prepareValue($value, $key);
77 }
78 }
79 }
80 }
81
82 public function setConfig($config)
83 {
84 if (is_array($config))
85 {
86 if (isset($config['timezoneFrom']))
87 {
88 $this->timezoneFrom = $config['timezoneFrom'];
89 }
90 if (isset($config['timezoneTo']))
91 {
92 $this->timezoneTo = $config['timezoneTo'];
93 }
94 }
95 }
96
97 public function setFullDayMode($value)
98 {
99 $this->fullDayMode = $value;
100 }
101
102 public function setOrganizer($name, $email, $phone)
103 {
104 $this->organizer = ['name' => $name, 'email' => $email, 'phone' => $phone];
105 }
106
107 public function setAttendees($attendeeDataList = [])
108 {
109 if (is_array($attendeeDataList))
110 {
111 foreach($attendeeDataList as $attendeeData)
112 {
113 $this->attendees[] = $attendeeData;
114 }
115 }
116 }
117
118 public function setRrule(RecurringEventRules $rrule): void
119 {
120 $this->rrule = $rrule;
121 }
122
124 {
125 $this->excludeDates = $excludeDates;
126 }
127
128 public function render()
129 {
130 return implode("\r\n", $this->buildBody());
131 }
132
133 private function buildBody()
134 {
135 // Build ICS properties - add header
136 $ics_props = [
137 'BEGIN:VCALENDAR',
138 'PRODID:'.self::PRODID,
139 'VERSION:2.0',
140 'CALSCALE:GREGORIAN',
141 'METHOD:'.self::$METHOD,
142 'BEGIN:VEVENT'
143 ];
144
145 $props = [];
146
147 // Add organizer field
148 if (isset($this->organizer['email']))
149 {
150 $props[self::formatOrganizerKey($this->organizer)] = self::formatEmailValue($this->organizer['email']);
151 }
152 else if (isset($this->organizer['phone']))
153 {
154 $props[self::formatOrganizerKey($this->organizer)] = self::formatPhoneValue($this->organizer['phone']);
155 }
156
157 // Add attendees
158 if (is_array($this->attendees))
159 {
160 foreach($this->attendees as $k => $attendee)
161 {
162 $props[self::formatAttendeeKey($attendee)] = self::formatEmailValue($attendee['email']);
163 }
164 }
165
166 // Build ICS properties - add header
167 foreach($this->properties as $k => $v)
168 {
169 switch ($k)
170 {
171 case 'dtstamp':
172 $props['DTSTAMP'] = self::formatDateTimeValue($v);
173 break;
174 case 'url':
175 $props['URL;VALUE=URI'] = $v;
176 break;
177 case 'dtstart':
178 case 'dtend':
179 if ($this->fullDayMode)
180 {
181 $props[mb_strtoupper($k).';VALUE=DATE'] = self::formatDateValue($v);
182 }
183 else
184 {
185 $tzid = ($k === 'dtstart') ? $this->timezoneFrom : $this->timezoneTo;
186 $props[mb_strtoupper($k).';TZID='.$tzid] = self::formatDateTimeValue($v);
187 }
188
189 break;
190 case 'last-modified':
191 $props[mb_strtoupper($k)] = self::formatDateTimeValue($v);
192 break;
193 case 'priority':
194 $priority = match ($v)
195 {
196 'low' => 9,
197 'high' => 1,
198 default => 5,
199 };
200 $props[mb_strtoupper($k)] = $priority;
201 break;
202 default:
203 $props[mb_strtoupper($k)] = $v;
204 }
205 }
206
207 if ($this->rrule !== null)
208 {
209 $props['RRULE'] = self::prepareRecurrenceRule($this->rrule, $this->timezoneTo);
210 }
211
212 if ($this->excludeDates && $this->rrule)
213 {
214 $props['EXDATE'] = $this->formatExcludedDates($this->prepareExcludedDates());
215 }
216
217 // Append properties
218 foreach ($props as $k => $v)
219 {
220 switch ($k)
221 {
222 case 'ALARM':
223 $ics_props[] = 'BEGIN:VALARM';
224 $ics_props[] = 'TRIGGER:-PT' . $props['ALARM'];
225 $ics_props[] = 'ACTION:DISPLAY';
226 $ics_props[] = 'END:VALARM';
227 break;
228 case 'EXDATE':
229 $ics_props[] = $k . $v;
230 break;
231 default:
232 $ics_props[] = "$k:$v";
233 }
234 }
235
236 // Build ICS properties - add footer
237 $ics_props[] = 'END:VEVENT';
238 $ics_props[] = 'END:VCALENDAR';
239
240 return $ics_props;
241 }
242
243 private function prepareExcludedDates(): array
244 {
245 $result = [];
246 $exDate = $this->excludeDates->getCollection();
247
248 foreach ($exDate as $date)
249 {
250 $formattedDate = date('Ymd', MakeTimeStamp($date->getFields()['date']));
251 if ($this->fullDayMode)
252 {
253 $result[] = [
254 'VALUE' => $formattedDate,
255 'PARAMETERS' => ['VALUE' => 'DATE'],
256 ];
257 }
258 else
259 {
260 $result[] = [
261 'VALUE' => sprintf(
262 '%sT%s',
263 $formattedDate,
264 $this->formatDateTimeValue($this->properties['dtstart'], self::TIME_FORMAT)
265 ),
266 'PARAMETERS' => ['TZID' => $this->prepareTimeZone($this->timezoneFrom)],
267 ];
268 }
269 }
270
271 return $result;
272 }
273
274 private function prepareTimeZone(?DateTimeZone $timeZone): string
275 {
276 if ($timeZone)
277 {
278 return $timeZone->getTimeZone()->getName();
279 }
280
281 return 'UTC';
282 }
283
284 private function formatExcludedDates(array $preparedExDate): string
285 {
286 $timezone = null;
287 $dates = [];
288
289 foreach ($preparedExDate as $date)
290 {
291 $timezone = $date['PARAMETERS']['TZID'] ?? null;
292 $dates[] = $date['VALUE'];
293 }
294
295 $timezone = $timezone ? ';TZID=' . $timezone : ';VALUE=DATE';
296
297 return $timezone . ':' . implode(',', $dates);
298 }
299
300 private function prepareValue($val, $key = false)
301 {
302 switch($key)
303 {
304// case 'dtstamp':
305// case 'dtstart':
306// case 'dtend':
307// $val = $this->formatDateValue($val);
308// break;
309 default:
310 $val = $this->escapeString($val);
311 }
312 return $val;
313 }
314
315 private static function formatDateValue($timestamp)
316 {
317 $dt = new \DateTime();
318 $dt->setTimestamp($timestamp);
319 return $dt->format(self::DATE_FORMAT);
320 }
321
322 private static function formatDateTimeValue($timestamp, string $format = self::DATETIME_FORMAT)
323 {
324 $dt = new \DateTime();
325 if ($timestamp)
326 {
327 $dt->setTimestamp($timestamp);
328 }
329 return $dt->format($format);
330 }
331
332 private static function formatEmailValue($email)
333 {
334 return 'mailto:'.$email;
335 }
336
337 private static function formatPhoneValue($phone): string
338 {
339 return 'tel:'.$phone;
340 }
341
342
343 private static function formatAttendeeKey($attendee)
344 {
345 $key = 'ATTENDEE';
346 $key .= ';CUTYPE=INDIVIDUAL';
347 //$key .= ';PARTSTAT=ACCEPTED'; // NEEDS-ACTION
348 $key .= ';RSVP=TRUE';
349 $key .= ';CN='.$attendee['email'];
350 return $key;
351 }
352
353 private static function formatOrganizerKey($organizer)
354 {
355 $key = 'ORGANIZER';
356 if ($organizer['name'])
357 {
358 $key .= ';CN='.$organizer['name'];
359 }
360 return $key;
361 }
362
363 public static function prepareRecurrenceRule(RecurringEventRules $rrule, ?DateTimeZone $timeZone): string
364 {
365 $result = 'FREQ=' . $rrule->getFrequency();
366 if ($rrule->getInterval())
367 {
368 $result .= ';INTERVAL=' . $rrule->getInterval();
369 }
370 if ($rrule->getByday())
371 {
372 $result .= ';BYDAY=' . implode(',', $rrule->getByday());
373 }
374 if ($rrule->getCount())
375 {
376 $result .= ';COUNT=' . $rrule->getCount();
377 }
378 else if (
379 $rrule->getUntil()
380 && $rrule->getUntil()->getDate()->getTimestamp()
381 && $rrule->getUntil()->getDate()->getTimestamp() < 2145830400
382 )
383 {
384 $offset = 0;
385 if ($timeZone)
386 {
387 $offset = $timeZone->getTimeZone()->getOffset(new \DateTime('now', $timeZone->getTimeZone()));
388 }
389
390 $untilTimestamp = $rrule->getUntil()->getDate()->getTimestamp() + (self::DAY_LENGTH - 1) - $offset;
391 $result .= ';UNTIL=' . date('Ymd\\THis\\Z', $untilTimestamp);
392 }
393
394 return $result;
395 }
396
397 private static function escapeString($str)
398 {
399 return preg_replace('/([\,;])/','\\\$1', $str);
400 }
401}
setFullDayMode($value)
Определения icsbuilder.php:97
const UTC_DATETIME_FORMAT
Определения icsbuilder.php:15
__construct($properties=[], $config=[])
Определения icsbuilder.php:62
const DATETIME_FORMAT
Определения icsbuilder.php:13
static prepareRecurrenceRule(RecurringEventRules $rrule, ?DateTimeZone $timeZone)
Определения icsbuilder.php:363
setOrganizer($name, $email, $phone)
Определения icsbuilder.php:102
RecurringEventRules $rrule
Определения icsbuilder.php:29
ExcludedDatesCollection $excludeDates
Определения icsbuilder.php:30
const DEFAULT_DATETIME_FORMAT
Определения icsbuilder.php:16
const DATE_FORMAT
Определения icsbuilder.php:12
setProperties($properties)
Определения icsbuilder.php:68
const TIME_FORMAT
Определения icsbuilder.php:14
setAttendees($attendeeDataList=[])
Определения icsbuilder.php:107
setRrule(RecurringEventRules $rrule)
Определения icsbuilder.php:118
setExclude(ExcludedDatesCollection $excludeDates)
Определения icsbuilder.php:123
setConfig($config)
Определения icsbuilder.php:82
$str
Определения commerceml2.php:63
</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
MakeTimeStamp($datetime, $format=false)
Определения tools.php:538
$name
Определения menu_edit.php:35
$email
Определения payment.php:49
$config
Определения quickway.php:69
if(empty($signedUserToken)) $key
Определения quickway.php:257
$props
Определения template.php:269
$val
Определения options.php:1793
$k
Определения template_pdf.php:567