ical.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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 retrieveCalenderByURL($url)
  196. {
  197. $events = [];
  198. $icsEvents = $this->getIcsEventsAsArray($url);
  199. if (isset($icsEvents) && !empty($icsEvents)) {
  200. $timeZone = isset($icsEvents [1] ['X-WR-TIMEZONE']) ? trim($icsEvents[1]['X-WR-TIMEZONE']) : date_default_timezone_get();
  201. $originalTimeZone = isset($icsEvents [1] ['X-WR-TIMEZONE']) ? str_replace('"', '', trim($icsEvents[1]['X-WR-TIMEZONE'])) : false;
  202. unset($icsEvents [1]);
  203. foreach ($icsEvents as $icsEvent) {
  204. $startKeys = $this->array_filter_key($icsEvent, function ($key) {
  205. return strpos($key, 'DTSTART') === 0;
  206. });
  207. $endKeys = $this->array_filter_key($icsEvent, function ($key) {
  208. return strpos($key, 'DTEND') === 0;
  209. });
  210. if (!empty($startKeys) && !empty($endKeys) && isset($icsEvent['SUMMARY'])) {
  211. /* Getting start date and time */
  212. $dates = [];
  213. $repeat = $icsEvent ['RRULE'] ?? false;
  214. if (!$originalTimeZone) {
  215. $tzKey = array_keys($startKeys);
  216. if (strpos($tzKey[0], 'TZID=') !== false) {
  217. $originalTimeZone = explode('TZID=', (string)$tzKey[0]);
  218. $originalTimeZone = (count($originalTimeZone) >= 2) ? str_replace('"', '', $originalTimeZone[1]) : false;
  219. $originalTimeZone = stripos($originalTimeZone, ';') !== false ? explode(';', $originalTimeZone)[0] : $originalTimeZone;
  220. }
  221. }
  222. $start = reset($startKeys);
  223. $end = reset($endKeys);
  224. $oldestDay = new DateTime ($this->currentTime);
  225. $oldestDay->modify('-' . $this->config['calendarStart'] . ' days');
  226. $newestDay = new DateTime ($this->currentTime);
  227. $newestDay->modify('+' . $this->config['calendarEnd'] . ' days');
  228. if ($repeat) {
  229. $dates = $this->getCalendarExtraDates($start, $icsEvent['RRULE'], $originalTimeZone);
  230. } else {
  231. $dates[] = [
  232. 'start' => new DateTime ($start),
  233. 'end' => new DateTime ($end)
  234. ];
  235. if ($oldestDay > new DateTime ($end)) {
  236. continue;
  237. }
  238. }
  239. foreach ($dates as $eventDate) {
  240. /* Converting to datetime and apply the timezone to get proper date time */
  241. $startDt = $eventDate['start'];
  242. /* Getting end date with time */
  243. $endDt = $eventDate['end'];
  244. $calendarStartDiff = date_diff($startDt, $newestDay);
  245. $calendarEndDiff = date_diff($startDt, $oldestDay);
  246. if ($originalTimeZone && $originalTimeZone !== 'UTC' && (strpos($start, 'Z') == false)) {
  247. $originalTimeZone = $this->calendarStandardizeTimezone($originalTimeZone);
  248. $dateTimeOriginalTZ = new DateTimeZone($originalTimeZone);
  249. $dateTimeOriginal = new DateTime('now', $dateTimeOriginalTZ);
  250. $dateTimeUTCTZ = new DateTimeZone(date_default_timezone_get());
  251. $dateTimeUTC = new DateTime('now', $dateTimeUTCTZ);
  252. $dateTimeOriginalOffset = $dateTimeOriginal->getOffset() / 3600;
  253. $dateTimeUTCOffset = $dateTimeUTC->getOffset() / 3600;
  254. $diff = $dateTimeUTCOffset - $dateTimeOriginalOffset;
  255. if ((int)$diff >= 0) {
  256. $startDt->modify('+ ' . $diff . ' hour');
  257. $endDt->modify('+ ' . $diff . ' hour');
  258. }
  259. }
  260. $startDt->setTimeZone(new DateTimezone ($timeZone));
  261. $endDt->setTimeZone(new DateTimezone ($timeZone));
  262. $startDate = $startDt->format(DateTime::ATOM);
  263. $endDate = $endDt->format(DateTime::ATOM);
  264. $dates = isset($icsEvent['RRULE']) ? $dates : null;
  265. if (new DateTime() < $endDt) {
  266. $extraClass = 'text-info';
  267. } else {
  268. $extraClass = 'text-success';
  269. }
  270. /* Getting the name of event */
  271. $eventName = $icsEvent['SUMMARY'];
  272. if (!$this->calendarDaysCheck($calendarStartDiff->format('%R') . $calendarStartDiff->days, $calendarEndDiff->format('%R') . $calendarEndDiff->days)) {
  273. break;
  274. }
  275. if ($startDt->format('H') == 0 && $startDt->format('i') == 0) {
  276. $startDate = $startDt->format('Y-m-d');
  277. }
  278. $events[] = array(
  279. 'title' => $eventName,
  280. 'imagetype' => 'calendar-o text-warning text-custom-calendar ' . $extraClass,
  281. 'imagetypeFilter' => 'ical',
  282. 'className' => 'bg-calendar calendar-item bg-custom-calendar',
  283. 'start' => (strlen(trim($start)) == 8) ? $eventDate['start']->format('Y-m-d') : $startDate,
  284. 'end' => (strlen(trim($end)) == 8) ? $eventDate['end']->format('Y-m-d') : $endDate,
  285. 'bgColor' => str_replace('text', 'bg', $extraClass),
  286. );
  287. }
  288. }
  289. }
  290. }
  291. return $events;
  292. }
  293. public function getICalendar()
  294. {
  295. if (!$this->config['homepageCalendarEnabled']) {
  296. $this->setAPIResponse('error', 'iCal homepage item is not enabled', 409);
  297. return false;
  298. }
  299. if (!$this->qualifyRequest($this->config['homepageCalendarAuth'])) {
  300. $this->setAPIResponse('error', 'User not approved to view this homepage item', 401);
  301. return false;
  302. }
  303. if (empty($this->config['calendariCal'])) {
  304. $this->setAPIResponse('error', 'iCal URL is not defined', 422);
  305. return false;
  306. }
  307. $iCalEvents = [];
  308. $calendarURLList = explode(',', $this->config['calendariCal']);
  309. foreach ($calendarURLList as $key => $value) {
  310. $iCalEvents = array_merge($iCalEvents, $this->retrieveCalenderByURL($value));
  311. }
  312. $calendarSources = $iCalEvents;
  313. $this->setAPIResponse('success', null, 200, $calendarSources);
  314. return $calendarSources;
  315. }
  316. }