province abbreviation patch - issue 724
[civicrm-core.git] / CRM / Utils / ICalendar.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
19 * @file
20 * API for event export in iCalendar format
21 * as outlined in Internet Calendaring and
22 * Scheduling Core Object Specification
6a488035
TO
23 */
24class CRM_Utils_ICalendar {
25
26 /**
fe482240 27 * Escape text elements for safe ICalendar use.
6a488035 28 *
046dd6c2 29 * @param string $text
77855840 30 * Text to escape.
a2cadd75
FW
31 * @param bool $keep_html
32 * Flag to retain HTML formatting
33 * @param int $position
34 * Column number of the start of the string in the ICal output - used to
35 * determine allowable length of the first line
6a488035 36 *
72b3a70c 37 * @return string
6a488035 38 */
a2cadd75
FW
39 public static function formatText($text, $keep_html = FALSE, int $position = 0) {
40 if (!$keep_html) {
41 $text = preg_replace(
42 '{ <a [^>]+ \\b href=(?: "( [^"]+ )" | \'( [^\']+ )\' ) [^>]* > ( [^<]* ) </a> }xi',
43 '$3 ($1$2)',
44 $text
45 );
46 $text = preg_replace(
47 '{ < / [^>]+ > \s* }',
48 "\$0 ",
49 $text
50 );
51 $text = preg_replace(
52 '{ <(br|/tr|/div|/h[1-6]) (\s [^>]*)? > (\s* \n)? }xi',
53 "\$0\n",
54 $text
55 );
56 $text = preg_replace(
57 '{ </p> (\s* \n)? }xi',
58 "\$0\n\n",
59 $text
60 );
61 $text = strip_tags($text);
62 $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML401, 'UTF-8');
63 }
64
6a488035 65 $text = str_replace("\\", "\\\\", $text);
046dd6c2
CW
66 $text = str_replace(',', '\,', $text);
67 $text = str_replace(';', '\;', $text);
be2fb01f 68 $text = str_replace(["\r\n", "\n", "\r"], "\\n ", $text);
a2cadd75 69
977d1c85
ML
70 // Remove this check after PHP 7.4 becomes a minimum requirement
71 $str_split = function_exists('mb_str_split') ? 'mb_str_split' : 'str_split';
a2cadd75
FW
72
73 if ($keep_html) {
74 $text = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><html><body>' . $text . '</body></html>';
75 }
76 $prefix = '';
77 if ($position) {
78 $prefixlen = max(50 - $position, 0);
79 $prefix = mb_substr($text, 0, $prefixlen) . "\n ";
80 $text = mb_substr($text, $prefixlen);
81 }
82 $text = $prefix . implode("\n ", $str_split($text, 50));
6a488035
TO
83 return $text;
84 }
85
046dd6c2
CW
86 /**
87 * Restore iCal formatted text to normal.
88 *
89 * @param string $text
90 * Text to unescape.
91 *
92 * @return string
93 */
94 public static function unformatText($text) {
977d1c85 95 $text = str_replace("\n ", "", $text);
046dd6c2
CW
96 $text = str_replace('\n ', "\n", $text);
97 $text = str_replace('\;', ';', $text);
98 $text = str_replace('\,', ',', $text);
99 $text = str_replace("\\\\", "\\", $text);
100 $text = str_replace("DQUOTE", "\"", $text);
101 return $text;
102 }
103
6a488035 104 /**
fe482240 105 * Escape date elements for safe ICalendar use.
6a488035 106 *
fa3fdebc 107 * @param string $date
77855840 108 * Date to escape.
6a488035 109 *
f4aaa82a 110 * @param bool $gdata
6a488035 111 *
72b3a70c
CW
112 * @return string
113 * Escaped date
6a488035 114 */
00be9182 115 public static function formatDate($date, $gdata = FALSE) {
6a488035
TO
116
117 if ($gdata) {
118 return date("Y-m-d\TH:i:s.000",
119 strtotime($date)
120 );
121 }
122 else {
123 return date("Ymd\THis",
124 strtotime($date)
125 );
126 }
127 }
128
129 /**
6a488035 130 * Send the ICalendar to the browser with the specified content type
cb087737 131 * - 'text/calendar' : used for iCal formatted feed
6a488035
TO
132 * - 'text/xml' : used for gData or rss formatted feeds
133 *
6a488035 134 *
77855840
TO
135 * @param string $calendar
136 * The calendar data to be published.
f4aaa82a 137 * @param string $content_type
77855840
TO
138 * @param string $charset
139 * The character set to use, defaults to 'us-ascii'.
140 * @param string $fileName
141 * The file name (for downloads).
142 * @param string $disposition
143 * How the file should be sent ('attachment' for downloads).
6a488035 144 */
00be9182 145 public static function send($calendar, $content_type = 'text/calendar', $charset = 'us-ascii', $fileName = NULL, $disposition = NULL) {
6a488035
TO
146 $config = CRM_Core_Config::singleton();
147 $lang = $config->lcMessages;
d42a224c
CW
148 CRM_Utils_System::setHttpHeader("Content-Language", $lang);
149 CRM_Utils_System::setHttpHeader("Content-Type", "$content_type; charset=$charset");
6a488035 150
cb087737 151 if ($fileName) {
d42a224c
CW
152 CRM_Utils_System::setHttpHeader('Content-Length', strlen($calendar));
153 CRM_Utils_System::setHttpHeader("Content-Disposition", "$disposition; filename=\"$fileName\"");
154 CRM_Utils_System::setHttpHeader("Pragma", "no-cache");
155 CRM_Utils_System::setHttpHeader("Expires", "0");
156 CRM_Utils_System::setHttpHeader("Cache-Control", "no-cache, must-revalidate");
6a488035
TO
157 }
158
159 echo $calendar;
160 }
96025800 161
a2cadd75
FW
162 /**
163 * @param array $timezones - Timezone strings
164 * @param $date_min
165 * @param $date_max
166 *
167 * @return array
168 */
169 public static function generate_timezones(array $timezones, $date_min, $date_max) {
170 if (empty($timezones)) {
171 return [];
172 }
173
174 $tz_items = [];
175
176 foreach ($timezones as $tzstr) {
177 $timezone = new DateTimeZone($tzstr);
178
179 $transitions = $timezone->getTransitions($date_min, $date_max);
180
181 if (count($transitions) === 1) {
182 $transitions[] = array_values($transitions)[0];
183 }
184
185 $item = [
186 'id' => $timezone->getName(),
187 'transitions' => [],
188 ];
189
190 $last_transition = array_shift($transitions);
191
192 foreach ($transitions as $transition) {
193 $item['transitions'][] = [
194 'type' => $transition['isdst'] ? 'DAYLIGHT' : 'STANDARD',
195 'offset_from' => self::format_tz_offset($last_transition['offset']),
196 'offset_to' => self::format_tz_offset($transition['offset']),
197 'abbr' => $transition['abbr'],
198 'dtstart' => date_create($transition['time'], $timezone)->format("Ymd\THis"),
199 ];
200
201 $last_transition = $transition;
202 }
203
204 $tz_items[] = $item;
205 }
206
207 return $tz_items;
208 }
209
210 protected static function format_tz_offset($offset) {
211 $offset /= 60;
212 $hours = intval($offset / 60);
213 $minutes = abs(intval($offset % 60));
214
215 return sprintf('%+03d%02d', $hours, $minutes);
216 }
217
6a488035 218}