ical.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. <?php
  2. trait ICalHomepageItem
  3. {
  4. public function calendarDaysCheck($entryStart, $entryEnd)
  5. {
  6. $success = false;
  7. $entryStart = intval($entryStart);
  8. $entryEnd = intval($entryEnd);
  9. if ($entryStart >= 0 && $entryEnd <= 0) {
  10. $success = true;
  11. }
  12. return $success;
  13. }
  14. public function calendarStandardizeTimezone($timezone)
  15. {
  16. switch ($timezone) {
  17. case('CST'):
  18. case('Central Time'):
  19. case('Central Standard Time'):
  20. $timezone = 'America/Chicago';
  21. break;
  22. case('CET'):
  23. case('Central European Time'):
  24. $timezone = 'Europe/Berlin';
  25. break;
  26. case('EST'):
  27. case('Eastern Time'):
  28. case('Eastern Standard Time'):
  29. $timezone = 'America/New_York';
  30. break;
  31. case('MST'):
  32. case('Mountain Time'):
  33. case('Mountain Standard Time'):
  34. $timezone = 'America/Denver';
  35. break;
  36. case('PST'):
  37. case('Pacific Time'):
  38. case('Pacific Standard Time'):
  39. $timezone = 'America/Los_Angeles';
  40. break;
  41. case('AKST'):
  42. case('Alaska Time'):
  43. case('Alaska Standard Time'):
  44. $timezone = 'America/Anchorage';
  45. break;
  46. case('HST'):
  47. case('Hawaii Time'):
  48. case('Hawaii Standard Time'):
  49. $timezone = 'Pacific/Honolulu';
  50. break;
  51. case('China Time'):
  52. case('China Standard Time'):
  53. $timezone = 'Asia/Beijing';
  54. break;
  55. case('IST'):
  56. case('India Time'):
  57. case('India Standard Time'):
  58. $timezone = 'Asia/New_Delhi';
  59. break;
  60. case('JST'):
  61. case('Japan Time'):
  62. case('Japan Standard Time'):
  63. $timezone = 'Asia/Tokyo';
  64. break;
  65. case('WET'):
  66. case('WEST'):
  67. case('Western European Time'):
  68. case('Western European Standard Time'):
  69. case('Western European Summer Time'):
  70. case('W. Europe Time'):
  71. case('W. Europe Standard Time'):
  72. case('W. Europe Summer Time'):
  73. $timezone = 'Europe/Lisbon';
  74. break;
  75. }
  76. return in_array($timezone, timezone_identifiers_list()) ? $timezone : 'UTC';
  77. }
  78. public function getCalendarExtraDates($start, $rule, $timezone)
  79. {
  80. $extraDates = [];
  81. try {
  82. if (stripos($rule, 'FREQ') !== false) {
  83. $until = $this->getCalenderRepeatUntil($rule);
  84. $start = new DateTime ($start);
  85. $startDate = new DateTime ($this->currentTime);
  86. $startDate->setTime($start->format('H'), $start->format('i'));
  87. $startDate->modify('-' . $this->config['calendarStart'] . ' days');
  88. $endDate = new DateTime ($this->currentTime);
  89. $endDate->modify('+' . $this->config['calendarEnd'] . ' days');
  90. $start = (stripos($rule, 'BYDAY') !== false || stripos($rule, 'BYMONTHDAY') !== false || stripos($rule, 'DAILY') !== false) ? $startDate : $start;
  91. $until = $until ? new DateTime($until) : $endDate;
  92. $dates = new \Recurr\Rule(trim($rule));
  93. $dates->setStartDate($start)->setUntil($until);
  94. $transformer = new \Recurr\Transformer\ArrayTransformer();
  95. $transformerConfig = new \Recurr\Transformer\ArrayTransformerConfig();
  96. $transformerConfig->enableLastDayOfMonthFix();
  97. $transformer->setConfig($transformerConfig);
  98. foreach (@$transformer->transform($dates) as $key => $date) {
  99. if ($date->getStart() >= $startDate) {
  100. $extraDates[$key]['start'] = $date->getStart();
  101. $extraDates[$key]['end'] = $date->getEnd();
  102. }
  103. }
  104. }
  105. } catch (\Recurr\Exception\InvalidRRule | \Recurr\Exception\InvalidWeekday | Exception $e) {
  106. return $extraDates;
  107. }
  108. return $extraDates;
  109. }
  110. public function getCalenderRepeat($value)
  111. {
  112. //FREQ=DAILY
  113. //RRULE:FREQ=WEEKLY;BYDAY=TH
  114. $first = explode('=', $value);
  115. if (count($first) > 1) {
  116. $second = explode(';', $first[1]);
  117. } else {
  118. return $value;
  119. }
  120. if ($second) {
  121. return $second[0];
  122. } else {
  123. return $first[1];
  124. }
  125. }
  126. public function getCalenderRepeatUntil($value)
  127. {
  128. $first = explode('UNTIL=', $value);
  129. if (count($first) > 1) {
  130. if (strpos($first[1], ';') !== false) {
  131. $check = explode(';', $first[1]);
  132. return $check[0];
  133. } else {
  134. return $first[1];
  135. }
  136. } else {
  137. return false;
  138. }
  139. }
  140. public function getCalenderRepeatCount($value)
  141. {
  142. $first = explode('COUNT=', $value);
  143. if (count($first) > 1) {
  144. return $first[1];
  145. } else {
  146. return false;
  147. }
  148. }
  149. public function file_get_contents_curl($url)
  150. {
  151. $ch = curl_init();
  152. curl_setopt($ch, CURLOPT_AUTOREFERER, true);
  153. curl_setopt($ch, CURLOPT_HEADER, 0);
  154. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  155. curl_setopt($ch, CURLOPT_URL, $url);
  156. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  157. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  158. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  159. $data = curl_exec($ch);
  160. curl_close($ch);
  161. return $data;
  162. }
  163. public function getIcsEventsAsArray($file)
  164. {
  165. $icalString = $this->file_get_contents_curl($file);
  166. $icsDates = array();
  167. /* Explode the ICs Data to get datas as array according to string ‘BEGIN:’ */
  168. $icsData = explode("BEGIN:", $icalString);
  169. /* Iterating the icsData value to make all the start end dates as sub array */
  170. foreach ($icsData as $key => $value) {
  171. $icsDatesMeta [$key] = explode("\n", $value);
  172. }
  173. /* Itearting the Ics Meta Value */
  174. foreach ($icsDatesMeta as $key => $value) {
  175. foreach ($value as $subKey => $subValue) {
  176. /* to get ics events in proper order */
  177. $icsDates = $this->getICSDates($key, $subKey, $subValue, $icsDates);
  178. }
  179. }
  180. return $icsDates;
  181. }
  182. /* function is to avoid the elements which is not having the proper start, end and summary information */
  183. public function getICSDates($key, $subKey, $subValue, $icsDates)
  184. {
  185. if ($key != 0 && $subKey == 0) {
  186. $icsDates [$key] ["BEGIN"] = $subValue;
  187. } else {
  188. $subValueArr = explode(":", $subValue, 2);
  189. if (isset ($subValueArr [1])) {
  190. $icsDates [$key] [$subValueArr [0]] = $subValueArr [1];
  191. }
  192. }
  193. return $icsDates;
  194. }
  195. public function getICalendar()
  196. {
  197. if (!$this->config['homepageCalendarEnabled']) {
  198. $this->setAPIResponse('error', 'iCal homepage item is not enabled', 409);
  199. return false;
  200. }
  201. if (!$this->qualifyRequest($this->config['homepageCalendarAuth'])) {
  202. $this->setAPIResponse('error', 'User not approved to view this homepage item', 401);
  203. return false;
  204. }
  205. if (empty($this->config['calendariCal'])) {
  206. $this->setAPIResponse('error', 'iCal URL is not defined', 422);
  207. return false;
  208. }
  209. $calendarItems = array();
  210. $calendars = array();
  211. $calendarURLList = explode(',', $this->config['calendariCal']);
  212. $icalEvents = array();
  213. foreach ($calendarURLList as $key => $value) {
  214. $dates = [];
  215. $icsEvents = $this->getIcsEventsAsArray($value);
  216. if (isset($icsEvents) && !empty($icsEvents)) {
  217. $timeZone = isset($icsEvents [1] ['X-WR-TIMEZONE']) ? trim($icsEvents[1]['X-WR-TIMEZONE']) : date_default_timezone_get();
  218. $originalTimeZone = isset($icsEvents [1] ['X-WR-TIMEZONE']) ? str_replace('"', '', trim($icsEvents[1]['X-WR-TIMEZONE'])) : false;
  219. unset($icsEvents [1]);
  220. foreach ($icsEvents as $icsEvent) {
  221. $startKeys = $this->array_filter_key($icsEvent, function ($key) {
  222. return strpos($key, 'DTSTART') === 0;
  223. });
  224. $endKeys = $this->array_filter_key($icsEvent, function ($key) {
  225. return strpos($key, 'DTEND') === 0;
  226. });
  227. if (!empty($startKeys) && !empty($endKeys) && isset($icsEvent['SUMMARY'])) {
  228. /* Getting start date and time */
  229. $repeat = $icsEvent ['RRULE'] ?? false;
  230. if (!$originalTimeZone) {
  231. $tzKey = array_keys($startKeys);
  232. if (strpos($tzKey[0], 'TZID=') !== false) {
  233. $originalTimeZone = explode('TZID=', (string)$tzKey[0]);
  234. $originalTimeZone = (count($originalTimeZone) >= 2) ? str_replace('"', '', $originalTimeZone[1]) : false;
  235. $originalTimeZone = stripos($originalTimeZone, ';') !== false ? explode(';', $originalTimeZone)[0] : $originalTimeZone;
  236. }
  237. }
  238. $start = reset($startKeys);
  239. $end = reset($endKeys);
  240. $oldestDay = new DateTime ($this->currentTime);
  241. $oldestDay->modify('-' . $this->config['calendarStart'] . ' days');
  242. $newestDay = new DateTime ($this->currentTime);
  243. $newestDay->modify('+' . $this->config['calendarEnd'] . ' days');
  244. if ($repeat) {
  245. $dates = $this->getCalendarExtraDates($start, $icsEvent['RRULE'], $originalTimeZone);
  246. } else {
  247. $dates[] = [
  248. 'start' => new DateTime ($start),
  249. 'end' => new DateTime ($end)
  250. ];
  251. if ($oldestDay > new DateTime ($end)) {
  252. continue;
  253. }
  254. }
  255. foreach ($dates as $eventDate) {
  256. /* Converting to datetime and apply the timezone to get proper date time */
  257. $startDt = $eventDate['start'];
  258. /* Getting end date with time */
  259. $endDt = $eventDate['end'];
  260. $calendarStartDiff = date_diff($startDt, $newestDay);
  261. $calendarEndDiff = date_diff($startDt, $oldestDay);
  262. if ($originalTimeZone && $originalTimeZone !== 'UTC' && (strpos($start, 'Z') == false)) {
  263. $originalTimeZone = $this->calendarStandardizeTimezone($originalTimeZone);
  264. $dateTimeOriginalTZ = new DateTimeZone($originalTimeZone);
  265. $dateTimeOriginal = new DateTime('now', $dateTimeOriginalTZ);
  266. $dateTimeUTCTZ = new DateTimeZone(date_default_timezone_get());
  267. $dateTimeUTC = new DateTime('now', $dateTimeUTCTZ);
  268. $dateTimeOriginalOffset = $dateTimeOriginal->getOffset() / 3600;
  269. $dateTimeUTCOffset = $dateTimeUTC->getOffset() / 3600;
  270. $diff = $dateTimeUTCOffset - $dateTimeOriginalOffset;
  271. if ((int)$diff >= 0) {
  272. $startDt->modify('+ ' . $diff . ' hour');
  273. $endDt->modify('+ ' . $diff . ' hour');
  274. }
  275. }
  276. $startDt->setTimeZone(new DateTimezone ($timeZone));
  277. $endDt->setTimeZone(new DateTimezone ($timeZone));
  278. $startDate = $startDt->format(DateTime::ATOM);
  279. $endDate = $endDt->format(DateTime::ATOM);
  280. $dates = isset($icsEvent['RRULE']) ? $dates : null;
  281. if (new DateTime() < $endDt) {
  282. $extraClass = 'text-info';
  283. } else {
  284. $extraClass = 'text-success';
  285. }
  286. /* Getting the name of event */
  287. $eventName = $icsEvent['SUMMARY'];
  288. if (!$this->calendarDaysCheck($calendarStartDiff->format('%R') . $calendarStartDiff->days, $calendarEndDiff->format('%R') . $calendarEndDiff->days)) {
  289. break;
  290. }
  291. if ($startDt->format('H') == 0 && $startDt->format('i') == 0) {
  292. $startDate = $startDt->format('Y-m-d');
  293. }
  294. $icalEvents[] = array(
  295. 'title' => $eventName,
  296. 'imagetype' => 'calendar-o text-warning text-custom-calendar ' . $extraClass,
  297. 'imagetypeFilter' => 'ical',
  298. 'className' => 'bg-calendar calendar-item bg-custom-calendar',
  299. 'start' => $startDate,
  300. 'end' => $endDate,
  301. 'bgColor' => str_replace('text', 'bg', $extraClass),
  302. );
  303. }
  304. }
  305. }
  306. }
  307. }
  308. $calendarSources = $icalEvents;
  309. $this->setAPIResponse('success', null, 200, $calendarSources);
  310. return $calendarSources;
  311. }
  312. }