PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents / 4.4.7
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents v4.4.7
4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / EmbedPress / ThirdParty / Googlecalendar / Embedpress_Google_Helper.php
embedpress / EmbedPress / ThirdParty / Googlecalendar Last commit date
assets 11 months ago Embedpress_Google_Helper.php 9 months ago GoogleClient.php 4 years ago
Embedpress_Google_Helper.php
943 lines
1 <?php
2
3 if ( !class_exists( 'EmbedPress_GoogleClient') ) {
4 require_once 'GoogleClient.php';
5 }
6 if ( !defined( 'EPGC_NOTICES_VERIFY_SUCCESS') ) {
7 define('EPGC_NOTICES_VERIFY_SUCCESS', 'Verify OK!');
8 define('EPGC_NOTICES_REVOKE_SUCCESS', 'Access revoked. This plugin does not have access to your calendars anymore.');
9 define('EPGC_NOTICES_REMOVE_SUCCESS', sprintf('Plugin data removed. Make sure to also manually revoke access to your calendars in the Google <a target="__blank" href="%s">Permissions</a> page!', 'https://myaccount.google.com/permissions'));
10 define('EPGC_NOTICES_CALENDARLIST_UPDATE_SUCCESS', 'Calendars updated.');
11 define('EPGC_NOTICES_COLORLIST_UPDATE_SUCCESS', 'Colors updated.');
12 define('EPGC_NOTICES_CACHE_DELETED', 'Cache deleted.');
13
14 define('EPGC_ERRORS_CLIENT_SECRET_MISSING', 'No client secret.');
15 define('EPGC_ERRORS_CLIENT_SECRET_INVALID', 'Invalid client secret.');
16 define('EPGC_ERRORS_ACCESS_TOKEN_MISSING', 'No access token.');
17 define('EPGC_ERRORS_REFRESH_TOKEN_MISSING', sprintf('Your refresh token is missing!<br><br>This can only be solved by manually revoking this plugin&#39;s access in the Google <a target="__blank" href="%s">Permissions</a> page and remove all plugin data.', 'https://myaccount.google.com/permissions'));
18 define('EPGC_ERRORS_ACCESS_REFRESH_TOKEN_MISSING', 'No access and refresh tokens.');
19 define('EPGC_ERRORS_REDIRECT_URI_MISSING', 'URI <code>%s</code> missing in the client secret file. Adjust your Google project and upload the new client secret file.');
20 define('EPGC_ERRORS_INVALID_FORMAT', 'Invalid format');
21 define('EPGC_ERRORS_NO_CALENDARS', 'No calendars');
22 define('EPGC_ERRORS_NO_SELECTED_CALENDARS', 'No selected calendars');
23 define('EPGC_ERRORS_TOKEN_AND_API_KEY_MISSING', 'Access token and API key are missing.');
24 define('EPGC_TRANSIENT_PREFIX', 'pgc_ev_');
25 define('EPGC_ENQUEUE_ACTION_PRIORITY', 11);
26 define( 'EPGC_REDIRECT_URL', admin_url('admin.php?page=embedpress&page_type=google-calendar'));
27 }
28 if (!defined('EPGC_EVENTS_MAX_RESULTS')) {
29 define('EPGC_EVENTS_MAX_RESULTS', 250);
30 }
31
32 if (!defined('EPGC_EVENTS_DEFAULT_TITLE')) {
33 define('EPGC_EVENTS_DEFAULT_TITLE', '');
34 }
35
36
37 if (!defined('EPGC_ASSET_URL')) {
38 define('EPGC_ASSET_URL', plugin_dir_url(__FILE__) .'assets/');
39 }
40
41 class Embedpress_Google_Helper {
42
43 public static function print_calendar_list($calendarList = []) {
44 if ( empty( $calendarList) ) {
45 $calendarList = static::getDecoded( 'epgc_calendarlist' ); //settings_selected_calendar_ids_json_cb
46 }
47 if ( ! empty( $calendarList ) ) {
48 $selectedCalendarIds = get_option( 'epgc_selected_calendar_ids' ); // array
49 if ( empty( $selectedCalendarIds ) ) {
50 $selectedCalendarIds = [];
51 }
52 ?>
53 <ul>
54 <?php foreach ( $calendarList as $calendar ) { ?>
55 <?php
56 $calendarId = $calendar['id'];
57 $htmlId = md5( $calendarId );
58 ?>
59 <p class="epgc-calendar-filter">
60 <input id="<?php echo $htmlId; ?>" type="checkbox" name="epgc_selected_calendar_ids[]"
61 <?php if ( in_array( $calendarId, $selectedCalendarIds ) ) {
62 echo ' checked ';
63 } ?>
64 value="<?php echo esc_attr( $calendarId ); ?>"/>
65 <label for="<?php echo $htmlId; ?>">
66 <span class="epgc-calendar-color" style="background-color:<?php echo esc_attr( $calendar['backgroundColor'] ); ?>"></span>
67 <?php echo esc_html( $calendar['summary'] ); ?><?php if ( ! empty( $calendar['primary'] ) ) {
68 echo ' (primary)';
69 } ?>
70 </label>
71 <br>ID: <?php echo esc_html( $calendarId ); ?>
72 </p>
73 <?php } ?>
74 </ul>
75 <?php
76 $refreshToken = get_option( "epgc_refresh_token" );
77 if ( empty( $refreshToken ) ) {
78 static::show_notice( EPGC_ERRORS_REFRESH_TOKEN_MISSING, 'error', false );
79 }
80 } else {
81 ?>
82 <p><?php _e( 'No calendar was found.', 'embedpress' ); ?></p>
83 <?php
84 }
85 }
86
87 /**
88 * Helper function to return array from option (that should be a JSON string).
89 * @return array or $default = null
90 */
91 public static function getDecoded($optionName, $default = null) {
92 $item = get_option($optionName);
93 // $item should be a JSON string.
94 if (!empty($item)) {
95 return json_decode($item, true);
96 }
97 return $default;
98 }
99
100
101 public static function show_notice($notice, $type, $dismissable) {
102 ?>
103 <div class="notice notice-<?php echo esc_attr($type); echo $dismissable ? ' is-dismissible' : ''; ?>">
104 <p><?php echo $notice; ?></p>
105 </div>
106 <?php
107 }
108
109 public static function ajax_get_calendar() {
110
111 check_ajax_referer('epgc_nonce');
112
113 try {
114
115 if (empty($_POST['start']) || empty($_POST['end'])) {
116 throw new Exception(EPGC_ERRORS_INVALID_FORMAT);
117 }
118
119 // Start and end are in ISO8601 string format with timezone offset (e.g. 2018-09-01T12:30:00-05:00)
120 $start = $_POST['start'];
121 $end = $_POST['end'];
122
123 $thisCalendarids = [];
124 $postedCalendarIds = [];
125 if (array_key_exists('thisCalendarids', $_POST) && !empty($_POST['thisCalendarids'])) {
126 $postedCalendarIds = array_map('trim', explode(',', $_POST['thisCalendarids']));
127 }
128 $privateSettingsCalendarListIds = array_map(function($item) {
129 return $item['id'];
130 }, static::getDecoded('epgc_calendarlist', []));
131 if (!empty($privateSettingsCalendarListIds)) {
132 $privateSettingsSelectedCalendarListIds = get_option('epgc_selected_calendar_ids');
133 // if (empty($postedCalendarIds)) {
134 // // If we have private selected calendars in settings and we get NO selected calendars from widget, shortcode, Gutenberg block, this means
135 // // ALL private calendars will be used.
136 // $postedCalendarIds = $privateSettingsSelectedCalendarListIds;
137 // }
138 foreach ($postedCalendarIds as $calId) {
139 if (!in_array($calId, $privateSettingsCalendarListIds) || in_array($calId, $privateSettingsSelectedCalendarListIds)) {
140 $thisCalendarids[] = $calId;
141 }
142 }
143 } else {
144 $thisCalendarids = $postedCalendarIds;
145 }
146
147 $cacheTime = get_option('epgc_cache_time'); // empty == no cache!
148
149 // We can have mutiple calendars with different calendar selections,
150 // so key should be including calendar selection.
151 $transientKey = EPGC_TRANSIENT_PREFIX . $start . $end . md5(implode('-', $thisCalendarids));
152
153 $transientItems = !empty($cacheTime) ? get_transient($transientKey) : false;
154
155 $calendarListByKey = static::get_calendars_by_key($thisCalendarids);
156
157 if ($transientItems !== false) {
158 wp_send_json(['items' => $transientItems, 'calendars' => $calendarListByKey]);
159 wp_die();
160 }
161
162 $colorList = false; // false means not queried yet / otherwise [] or filled []
163
164 $results = [];
165
166 $optParams = array(
167 'maxResults' => EPGC_EVENTS_MAX_RESULTS,
168 'orderBy' => 'startTime',
169 'singleEvents' => 'true',
170 'timeMin' => $start,
171 'timeMax' => $end,
172 );
173 if (!empty($_POST['timeZone'])) {
174 $optParams['timeZone'] = $_POST['timeZone'];
175 }
176
177 $hasAccessToken = get_option('epgc_access_token');
178
179 if (!empty($hasAccessToken)) {
180
181 $client = static::getGoogleClient(true);
182 if ($client->isAccessTokenExpired()) {
183 if (!$client->getRefreshTOken()) {
184 throw new Exception(EPGC_ERRORS_REFRESH_TOKEN_MISSING);
185 }
186 $client->refreshAccessToken();
187 }
188 $service = new EmbedPress_GoogleCalendarClient($client);
189
190 foreach ($thisCalendarids as $calendarId) {
191 $results[$calendarId] = $service->getEvents($calendarId, $optParams);
192 }
193
194 } elseif (!empty(get_option('epgc_api_key'))) {
195
196 $referer = !empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
197 $apiKey = get_option('epgc_api_key');
198 $service = new EmbedPress_GoogleCalendarClient(null);
199 foreach ($thisCalendarids as $calendarId) {
200 $results[$calendarId] = $service->getEventsPublic($calendarId, $optParams, $apiKey, $referer);
201 }
202
203 } else {
204 // No API key and no OAuth2 token
205 throw new Exception(EPGC_ERRORS_TOKEN_AND_API_KEY_MISSING);
206 }
207
208 $items = [];
209 foreach ($results as $calendarId => $events) {
210 foreach ($events as $item) {
211 $newItem = [
212 'title' => empty($item['summary']) ? EPGC_EVENTS_DEFAULT_TITLE : $item['summary'],
213 'htmlLink' => $item['htmlLink'],
214 'description' => !empty($item['description']) ? $item['description'] : '',
215 'calId' => $calendarId,
216 'creator' => !empty($item['creator']) ? $item['creator'] : [],
217 'attendees' => !empty($item['attendees']) ? $item['attendees'] : [],
218 'attachments' => !empty($item['attachments']) ? $item['attachments'] : [],
219 'location' => !empty($item['location']) ? $item['location'] : ''
220 ];
221 if (!empty($item['start']['date'])) {
222 $newItem['allDay'] = true;
223 $newItem['start'] = $item['start']['date'];
224 $newItem['end'] = $item['end']['date'];
225 // $newItem['timeZone'] = $item['start']['timeZone']; // TODO? end timezone also exists...
226 } else {
227 $newItem['start'] = $item['start']['dateTime'];
228 $newItem['end'] = $item['end']['dateTime'];
229 // $newItem['timeZone'] = $item['start']['timeZone']; // TODO? end timezone also exists...
230 }
231 if (!empty($item['colorId'])) {
232 if ($colorList === false) {
233 $colorList = static::getDecoded('pgc_colorlist', []);
234 }
235 if (array_key_exists('event', $colorList) && array_key_exists($item['colorId'], $colorList['event'])) {
236 $newItem['bColor'] = $colorList['event'][$item['colorId']]['background'];
237 $newItem['fColor'] = $colorList['event'][$item['colorId']]['foreground'];
238 }
239 }
240
241 $items[] = $newItem;
242 }
243 }
244
245 if (!empty($cacheTime)) {
246 set_transient($transientKey, $items, $cacheTime * MINUTE_IN_SECONDS);
247 }
248
249 wp_send_json(['items' => $items, 'calendars' => $calendarListByKey]);
250 wp_die();
251 } catch (EmbedPress_GoogleClient_RequestException $ex) {
252 wp_send_json([
253 'error' => $ex->getMessage(),
254 'errorCode' => $ex->getCode(),
255 'errorDescription' => $ex->getDescription()]);
256 wp_die();
257 } catch (Exception $ex) {
258 wp_send_json([
259 'error' => $ex->getMessage(),
260 'errorCode' => $ex->getCode()]);
261 wp_die();
262 }
263 }
264
265 public static function get_calendars_by_key($calendarIds) {
266
267 $publicCalendarList = get_option('pgc_public_calendarlist');
268 if (empty($publicCalendarList)) {
269 $publicCalendarList = [];
270 }
271 $privateCalendarList = static::getDecoded('epgc_calendarlist', []);
272 if (empty($privateCalendarList)) {
273 $privateCalendarList = [];
274 }
275 $calendarList = $publicCalendarList + $privateCalendarList;
276 $keyedCalendarList = [];
277 foreach ($calendarList as $cal) {
278 $keyedCalendarList[$cal['id']] = $cal;
279 }
280
281 $calendarListByKey = [];
282 foreach ($calendarIds as $calId) {
283 $cal = array_key_exists($calId, $keyedCalendarList) ? $keyedCalendarList[$calId] : [
284 'summary' => $calId,
285 'backgroundColor' => 'rgb(121, 134, 203)'
286 ];
287 $calendarListByKey[$calId] = [
288 'summary' => $cal['summary'],
289 'backgroundColor' => $cal['backgroundColor']
290 ];
291 }
292
293 return $calendarListByKey;
294 }
295 /**
296 * Helper function that returns a valid Google Client.
297 * @return Embedpress_GoogleClient instance
298 * @param bool $withTokens If true, also get tokens.
299 * @throws Exception.
300 */
301 public static function getGoogleClient($withTokens = false) {
302
303 $authConfig = get_option('epgc_client_secret');
304 if (empty($authConfig)) {
305 throw new Exception(EPGC_ERRORS_CLIENT_SECRET_MISSING);
306 }
307 $authConfig = static::getDecoded('epgc_client_secret');
308 if (empty($authConfig)) {
309 throw new Exception(EPGC_ERRORS_CLIENT_SECRET_INVALID);
310 }
311
312 $c = new Embedpress_GoogleClient($authConfig);
313 $c->setScope('https://www.googleapis.com/auth/calendar.readonly');
314 if (!self::check_redirect_uri($authConfig)) {
315 throw new Exception(sprintf(EPGC_ERRORS_REDIRECT_URI_MISSING, EPGC_REDIRECT_URL));
316 }
317 $c->setRedirectUri(EPGC_REDIRECT_URL);
318 $c->setTokenCallback(function($accessTokenInfo, $refreshToken) {
319 update_option('epgc_access_token', json_encode($accessTokenInfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), false);
320 if (!empty($refreshToken)) {
321 update_option('epgc_refresh_token', $refreshToken, false);
322 }
323 });
324
325 if ($withTokens) {
326 $accessToken = static::getDecoded('epgc_access_token');
327 if (empty($accessToken)) {
328 throw new Exception(EPGC_ERRORS_ACCESS_TOKEN_MISSING);
329 }
330 $c->setAccessTokenInfo($accessToken);
331 $refreshToken = get_option("epgc_refresh_token");
332 if (empty($refreshToken)) {
333 throw new Exception(EPGC_ERRORS_REFRESH_TOKEN_MISSING);
334 }
335 $c->setRefreshToken($refreshToken);
336 }
337
338 return $c;
339
340 }
341
342 /**
343 * Helper function to check if we have a valid redirect uri in the client secret.
344 * @return bool
345 */
346 public static function check_redirect_uri($decodedClientSecret) {
347 return !empty($decodedClientSecret)
348 && !empty($decodedClientSecret['web'])
349 && !empty($decodedClientSecret['web']['redirect_uris'])
350 && in_array(EPGC_REDIRECT_URL, $decodedClientSecret['web']['redirect_uris']);
351 }
352
353
354 /**
355 * Get a valid formatted client secret.
356 * @return array|false Secret Array, false if no exists, Exception for invalid one
357 **/
358 public static function get_valid_client_secret(&$error = '') {
359 $clientSecret = get_option('epgc_client_secret');
360 if (empty($clientSecret)) {
361 return false;
362 }
363 $clientSecret = static::getDecoded('epgc_client_secret');
364 if (empty($clientSecret)
365 || empty($clientSecret['web'])
366 || empty($clientSecret['web']['client_secret'])
367 || empty($clientSecret['web']['client_id']))
368 {
369 $error = EPGC_ERRORS_CLIENT_SECRET_INVALID;
370 } elseif (!self::check_redirect_uri($clientSecret))
371 {
372 $error = sprintf(EPGC_ERRORS_REDIRECT_URI_MISSING, admin_url('options-general.php?page=pgc'));
373 }
374 return $clientSecret;
375 }
376
377 public static function delete_calendar_cache() {
378 global $wpdb;
379 $wpdb->query("DELETE FROM " . $wpdb->options
380 . " WHERE option_name LIKE '_transient_timeout_" . EPGC_TRANSIENT_PREFIX . "%' OR option_name LIKE '_transient_" . EPGC_TRANSIENT_PREFIX . "%'");
381 }
382
383 /**
384 * Helper function to delete all plugin options.
385 */
386 public static function delete_options($which) { // which = all, public, private
387 if ($which === 'all' || $which === 'private') {
388 delete_option('epgc_access_token');
389 delete_option('epgc_refresh_token');
390 delete_option('epgc_selected_calendar_ids');
391 delete_option('epgc_calendarlist');
392 delete_option('epgc_client_secret');
393 }
394 if ($which === 'all' || $which === 'public') {
395 delete_option('epgc_api_key');
396 }
397 if ($which === 'all') {
398 delete_option('epgc_cache_time');
399 }
400 }
401
402 public static function uninstall() {
403 try {
404 $client = static::getGoogleClient();
405 $accessToken = static::getDecoded('epgc_access_token');
406 if (!empty($accessToken)) {
407 $client->setAccessTokenInfo($accessToken);
408 }
409 $refreshToken = get_option("epgc_refresh_token");
410 if (!empty($refreshToken)) {
411 $client->setRefreshToken($refreshToken);
412 }
413 if (empty($accessToken) && empty($refreshToken)) {
414 throw new Exception(EPGC_ERRORS_ACCESS_REFRESH_TOKEN_MISSING);
415 }
416 $client->revoke();
417 } catch (Exception $ex) {
418 // Too bad...
419 } finally {
420 // Clear all plugin data
421 static::delete_plugin_data();
422 }
423 }
424
425 /**
426 * Helper function to delete all plugin data.
427 */
428 public static function delete_plugin_data($which = 'all') {
429 self::delete_calendar_cache();
430 self::delete_options($which);
431 }
432 public static function removable_query_args($removable_query_args) {
433 $removable_query_args[] = 'epgcnotice';
434 return $removable_query_args;
435 }
436
437 public static function notices_init() {
438 if (!empty($_GET['epgcnotice'])) {
439 $epgcnotices = get_option('epgc_notices_' . get_current_user_id());
440 if (empty($epgcnotices)) {
441 return;
442 }
443 delete_option('epgc_notices_' . get_current_user_id());
444 add_action('admin_notices', function() use ($epgcnotices) {
445 foreach ($epgcnotices as $notice) {
446 ?>
447 <div class="notice notice-<?php echo esc_attr($notice['type']); ?> is-dismissible">
448 <p><?php echo esc_html($notice['content']); ?></p>
449 </div>
450 <?php
451 }
452 });
453 }
454 }
455
456 /**
457 * Helper function to add notice messages.
458 * @param bool $redirect Redirect if true.
459 */
460 public static function add_notice($content, $type = 'success', $redirect = false) {
461 $epgcnotices = get_option('epgc_notices_' . get_current_user_id());
462 if (empty($epgcnotices)) {
463 $epgcnotices = [];
464 }
465 $epgcnotices[] = [
466 'content' => $content,
467 'type' => $type
468 ];
469 update_option('epgc_notices_' . get_current_user_id(), $epgcnotices, false);
470 if ($redirect) {
471 wp_redirect(EPGC_REDIRECT_URL ."&epgcnotice=true");
472 }
473 }
474
475 /**
476 * Helper function die with different kind of errors.
477 */
478 public static function embedpress_die($error = null) {
479 $backLink = '<br><br><a href="' . admin_url('admin.php?page=embedpress&page_type=google-calendar') . '">' . __('Back', 'embedpress') . '</a>';
480 if (empty($error)) {
481 wp_die(__('Unknown error', 'embedpress') . $backLink);
482 }
483 if ($error instanceof Exception) {
484 $s = [];
485 if ($error->getCode()) {
486 $x[] = $error->getCode();
487 }
488 $s[] = $error->getMessage();
489 if ($error instanceof Embedpress_GoogleClient_RequestException) {
490 if ($error->getDescription()) {
491 $s[] = $error->getDescription();
492 }
493 }
494 wp_die(implode("<br>", $s) . $backLink);
495 } elseif (is_array($error)) {
496 wp_die(implode("<br>", $error) . $backLink);
497 } elseif (is_string($error)) {
498 wp_die($error . $backLink);
499 } else {
500 wp_die(__('Unknown error format', 'embedpress') . $backLink);
501 }
502 }
503
504 /**
505 * Helper function to return pretty printed JSON string.
506 * @return string
507 */
508 public static function getPrettyJSONString($jsonObject) {
509 return str_replace(" ", " ", json_encode($jsonObject, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
510 }
511
512 public static function sort_calendars(&$items) {
513 // Set locale to UTF-8 variant if this is not the case.
514 if (strpos(setlocale(LC_COLLATE, 0), '.UTF-8') === false) {
515 // If we set this to a non existing locale it will be the default locale after this call.
516 setlocale(LC_COLLATE, get_locale() . '.UTF-8');
517 }
518 usort($items, function($a, $b) {
519 return strcoll($a['summary'], $b['summary']);
520 });
521 }
522 public static function shortcode($atts = [], $content = null) {
523
524 // When we have no attributes, $atts is an empty string
525 if (!is_array($atts)) {
526 $atts = [];
527 }
528
529 foreach ($atts as &$value) {
530 $value = esc_attr($value);
531 }
532 unset($value); // Unset reference
533
534 // Assets are now handled by AssetManager
535 // Calendar-specific assets are loaded when calendar shortcode is used
536 $defaultConfig = [
537 'header' => [
538 'left' => 'prev,next today',
539 'center' => 'title',
540 'right' => 'dayGridMonth,timeGridWeek,listWeek'
541 ]
542 ];
543 $userConfig = $defaultConfig; // copy
544 $userFilter = 'top';
545 $userEventPopup = 'true';
546 $userEventLink = 'true';
547 $userHidePassed = 'false';
548 $userHideFuture = 'false';
549 $userEventDescription = 'true';
550 $userEventLocation = 'true';
551 $userEventAttendees = 'false';
552 $userEventAttachments = 'false';
553 $userEventCreator = 'false';
554 $userEventCalendarname = 'false';
555 $calendarIds = '';
556 $uncheckedCalendarIds = ''; // in filter
557 // Get all non-fullcalendar known properties
558 foreach ($atts as $key => $value) {
559 if ($key === 'public') {
560 // This existsed in old versions, but we don't want it in our shortcode output, so skip it.
561 continue;
562 }
563 if ($key === 'filter') {
564 $userFilter = $value === 'true' ? 'top' : $value;
565 continue;
566 }
567 if ($key === 'eventpopup') {
568 $userEventPopup = $value;
569 continue;
570 }
571 if ($key === 'eventlink') {
572 $userEventLink = $value;
573 continue;
574 }
575 if ($key === 'hidepassed') {
576 $userHidePassed = $value;
577 continue;
578 }
579 if ($key === 'hidefuture') {
580 $userHideFuture = $value;
581 continue;
582 }
583 if ($key === 'eventdescription') {
584 $userEventDescription = $value;
585 continue;
586 }
587 if ($key === 'eventattachments') {
588 $userEventAttachments = $value;
589 continue;
590 }
591 if ($key === 'eventattendees') {
592 $userEventAttendees = $value;
593 continue;
594 }
595 if ($key === 'eventlocation') {
596 $userEventLocation = $value;
597 continue;
598 }
599 if ($key === 'eventcreator') {
600 $userEventCreator = $value;
601 continue;
602 }
603 if ($key === 'eventcalendarname') {
604 $userEventCalendarname = $value;
605 continue;
606 }
607 if ($key === 'uncheckedcalendarids' && !empty($value)) {
608 $uncheckedCalendarIds = $value; // comma separated string
609 continue;
610 }
611
612 if ($key === 'calendarids') {
613 if (!empty($value)) {
614 $calendarIds = $value; // comma separated string
615 }
616 continue;
617 }
618 if ($key === 'fullcalendarconfig') {
619 // A JSON string that we can directly send to FullCalendar
620 $userConfig = json_decode($value, true);
621 } else {
622 // Fullcalendar properties that get passed to fullCalendar instance.
623 $parts = explode('-', $key);
624 $partsCount = count($parts);
625 if ($partsCount > 1) {
626 $currentUserConfigLayer = &$userConfig;
627 for ($i = 0; $i < $partsCount; $i++) {
628 $part = $parts[$i];
629 if ($i + 1 === $partsCount) {
630 if ($value === 'true') {
631 $value = true;
632 } elseif ($value === 'false') {
633 $value = $value;
634 }
635 $currentUserConfigLayer[$part] = $value;
636 } else {
637 if (!array_key_exists($part, $currentUserConfigLayer)) {
638 $currentUserConfigLayer[$part] = [];
639 }
640 $currentUserConfigLayer = &$currentUserConfigLayer[$part];
641 }
642 }
643 } else {
644 $userConfig[$key] = $value;
645 }
646 }
647 }
648
649 $dataCalendarIds = '';
650 if (!empty($calendarIds)) {
651 $dataCalendarIds = 'data-calendarids=\'' . json_encode(array_map('trim', explode(',', $calendarIds))) . '\'';
652 } else {
653 $privateSettingsSelectedCalendarListIds = get_option('epgc_selected_calendar_ids', []);
654 if (!empty($privateSettingsSelectedCalendarListIds)) {
655 $dataCalendarIds = 'data-calendarids=\'' . json_encode($privateSettingsSelectedCalendarListIds) . '\'';
656 }
657 }
658
659 $dataUnchekedCalendarIds = '';
660 if (!empty($uncheckedCalendarIds)) {
661 $dataUnchekedCalendarIds = 'data-uncheckedcalendarids=\'' . json_encode(array_map('trim', explode(',', $uncheckedCalendarIds))) . '\'';
662 }
663
664 $filterHTML = '<div class="epgc-calendar-filter" ' . $dataUnchekedCalendarIds . '></div>';
665
666 return '<div class="epgc-calendar-wrapper epgc-calendar-page">' . ($userFilter === 'top' ? wp_kses_post($filterHTML) : '') . '<div '
667
668 . $dataCalendarIds . ' data-filter="' . esc_attr($userFilter) . '" data-eventpopup="' . esc_attr($userEventPopup) . '" data-eventlink="'
669 . esc_attr($userEventLink) . '" data-eventdescription="' . esc_attr($userEventDescription) . '" data-eventlocation="'
670 . esc_attr($userEventLocation) . '" data-eventattachments="' . esc_attr($userEventAttachments) . '" data-eventattendees="'
671 . esc_attr($userEventAttendees) . '" data-eventcreator="' . esc_attr($userEventCreator) . '" data-eventcalendarname="'
672 . esc_attr($userEventCalendarname) . '" data-hidefuture="' . esc_attr($userHideFuture) . '" data-hidepassed="'
673 . esc_attr($userHidePassed) . '" data-config="' . esc_attr(json_encode($userConfig)) . '" data-locale="'
674 . esc_attr(get_locale()) . '" class="epgc-calendar"></div>' . ($userFilter === 'bottom' ? $filterHTML : '') . '</div>';
675 }
676
677 public static function admin_post_calendarlist() {
678 try {
679 $client = static::getGoogleClient(true);
680 if ($client->isAccessTokenExpired()) {
681 if (!$client->getRefreshToken()) {
682 throw new Exception(EPGC_ERRORS_REFRESH_TOKEN_MISSING);
683 }
684 $client->refreshAccessToken();
685 }
686 $service = new Embedpress_GoogleCalendarClient($client);
687 $items = $service->getCalendarList();
688
689 self::sort_calendars($items);
690
691 update_option('epgc_calendarlist', self::getPrettyJSONString($items), false);
692 self::add_notice(PGC_NOTICES_CALENDARLIST_UPDATE_SUCCESS, 'success', true);
693 exit;
694 } catch (Exception $ex) {
695 self::embedpress_die($ex);
696 }
697 }
698 public static function admin_post_colorlist() {
699 try {
700 $client = static::getGoogleClient(true);
701 if ($client->isAccessTokenExpired()) {
702 if (!$client->getRefreshToken()) {
703 throw new Exception(PGC_ERRORS_REFRESH_TOKEN_MISSING);
704 }
705 $client->refreshAccessToken();
706 }
707 $service = new Embedpress_GoogleCalendarClient($client);
708 $items = $service->getColorList();
709 update_option('epgc_colorlist', self::getPrettyJSONString($items), false);
710 self::add_notice(EPGC_NOTICES_COLORLIST_UPDATE_SUCCESS, 'success', true);
711 exit;
712 } catch (Exception $ex) {
713 self::embedpress_die($ex);
714 }
715 }
716 public static function admin_post_deletecache() {
717 if ( ! isset( $_POST['epgc_deletecache_data'] ) || ! wp_verify_nonce( $_POST['epgc_deletecache_data'], 'epgc_deletecache' ) || !current_user_can('manage_options')) {
718 print 'Sorry, your nonce did not verify.';
719 exit;
720 } else {
721 self::delete_calendar_cache();
722 self::add_notice(PGC_NOTICES_CACHE_DELETED, 'success', true);
723 exit;
724 }
725 }
726 public static function admin_post_verify() {
727 try {
728 $client = static::getGoogleClient(true);
729 $client->refreshAccessToken();
730 self::add_notice(PGC_NOTICES_VERIFY_SUCCESS, 'success', true);
731 exit;
732 } catch (Exception $ex) {
733 self::embedpress_die($ex);
734 }
735 }
736 public static function enqueue_scripts() {
737 // Dashicons are now handled by AssetManager
738 wp_register_style('fullcalendar', EPGC_ASSET_URL . 'lib/fullcalendar4/core/main.min.css', null, EMBEDPRESS_VERSION);
739 wp_register_style('fullcalendar_daygrid', EPGC_ASSET_URL . 'lib/fullcalendar4/daygrid/main.min.css', ['fullcalendar'], EMBEDPRESS_VERSION);
740 wp_register_style('fullcalendar_timegrid', EPGC_ASSET_URL . 'lib/fullcalendar4/timegrid/main.min.css', ['fullcalendar_daygrid'], EMBEDPRESS_VERSION);
741 wp_register_style('fullcalendar_list', EPGC_ASSET_URL . 'lib/fullcalendar4/list/main.min.css', ['fullcalendar'], EMBEDPRESS_VERSION);
742 wp_register_style('epgc', EPGC_ASSET_URL . 'css/epgc.css', ['fullcalendar_timegrid'], EMBEDPRESS_VERSION);
743 wp_register_style('tippy_light', EPGC_ASSET_URL . 'lib/tippy/light-border.css', null, EMBEDPRESS_VERSION);
744
745 //wp_enqueue_style( 'fullcalendar');
746 //wp_enqueue_style( 'fullcalendar_daygrid');
747 //wp_enqueue_style( 'fullcalendar_timegrid');
748 //wp_enqueue_style( 'fullcalendar_list');
749 //wp_enqueue_style( 'epgc');
750 //wp_enqueue_style( 'tippy_light');
751
752
753 wp_register_script('popper',EPGC_ASSET_URL . 'lib/popper.min.js', null, EMBEDPRESS_VERSION, true);
754 wp_register_script('tippy',EPGC_ASSET_URL . 'lib/tippy/tippy-bundle.umd.min.js', ['popper'], EMBEDPRESS_VERSION, true);
755 wp_register_script('my_moment',EPGC_ASSET_URL . 'lib/moment/moment-with-locales.min.js', null, EMBEDPRESS_VERSION, true);
756 wp_register_script('my_moment_timezone',EPGC_ASSET_URL . 'lib/moment/moment-timezone-with-data.min.js', ['my_moment'], EMBEDPRESS_VERSION, true);
757 wp_register_script('fullcalendar',EPGC_ASSET_URL . 'lib/fullcalendar4/core/main.min.js', ['my_moment_timezone'], EMBEDPRESS_VERSION, true);
758 wp_register_script('fullcalendar_moment',EPGC_ASSET_URL . 'lib/fullcalendar4/moment/main.min.js', ['fullcalendar'], EMBEDPRESS_VERSION, true);
759 wp_register_script('fullcalendar_moment_timezone',EPGC_ASSET_URL . 'lib/fullcalendar4/moment-timezone/main.min.js', ['fullcalendar_moment'], EMBEDPRESS_VERSION, true);
760 wp_register_script('fullcalendar_daygrid',EPGC_ASSET_URL . 'lib/fullcalendar4/daygrid/main.min.js', ['fullcalendar'], EMBEDPRESS_VERSION, true);
761 wp_register_script('fullcalendar_timegrid',EPGC_ASSET_URL . 'lib/fullcalendar4/timegrid/main.min.js', ['fullcalendar_daygrid'], EMBEDPRESS_VERSION, true);
762 wp_register_script('fullcalendar_list',EPGC_ASSET_URL . 'lib/fullcalendar4/list/main.min.js', ['fullcalendar'], EMBEDPRESS_VERSION, true);
763 wp_register_script('fullcalendar_locales',EPGC_ASSET_URL . 'lib/fullcalendar4/core/locales-all.min.js',['fullcalendar'], EMBEDPRESS_VERSION, true);
764 wp_register_script('epgc', EPGC_ASSET_URL . 'js/main.js',['fullcalendar'], EMBEDPRESS_VERSION, true);
765
766 //wp_enqueue_script('popper');
767 //wp_enqueue_script('my_moment');
768 //wp_enqueue_script('my_moment_timezone');
769 //wp_enqueue_script('fullcalendar');
770 //wp_enqueue_script('fullcalendar_moment');
771 //wp_enqueue_script('fullcalendar_moment_timezone');
772 //wp_enqueue_script('fullcalendar_daygrid');
773 //wp_enqueue_script('fullcalendar_timegrid');
774 //wp_enqueue_script('fullcalendar_list');
775 //wp_enqueue_script('fullcalendar_locales');
776 //wp_enqueue_script('epgc');
777
778 // Localization is now handled by LocalizationManager
779 // This section is kept for backward compatibility but functionality has been moved
780
781 }
782
783 public static function remove_private_data() {
784 if ( ! isset( $_POST['epgc_remove_private_data'] ) || ! wp_verify_nonce( $_POST['epgc_remove_private_data'], 'epgc_remove_private' ) || !current_user_can('manage_options')) {
785 print 'Sorry, your nonce did not verify.';
786 exit;
787 } else {
788 self::delete_plugin_data('private');
789 self::add_notice(EPGC_NOTICES_REMOVE_SUCCESS, 'success', true);
790 exit;
791 }
792 }
793
794 public static function admin_post_remove() {
795
796 if ( ! isset( $_POST['epgc_remove_private_data'] ) || ! wp_verify_nonce( $_POST['epgc_remove_private_data'], 'epgc_remove_private' ) || !current_user_can('manage_options')) {
797 print 'Sorry, your nonce did not verify.';
798 exit;
799 } else {
800
801 self::delete_plugin_data();
802 self::add_notice(EPGC_NOTICES_REMOVE_SUCCESS, 'success', true);
803 exit;
804 }
805
806 }
807 public static function admin_post_revoke() {
808 try {
809 $client = self::getGoogleClient();
810 $accessToken = self::getDecoded('epgc_access_token');
811 if (!empty($accessToken)) {
812 $client->setAccessTokenInfo($accessToken);
813 }
814 $refreshToken = get_option("epgc_refresh_token");
815 if (!empty($refreshToken)) {
816 $client->setRefreshToken($refreshToken);
817 }
818 if (empty($accessToken) && empty($refreshToken)) {
819 throw new Exception(EPGC_ERRORS_ACCESS_REFRESH_TOKEN_MISSING);
820 }
821 $client->revoke();
822 // Clear access and refresh tokens
823 self::delete_plugin_data('private');
824 self::add_notice(EPGC_NOTICES_REVOKE_SUCCESS, 'success', true);
825 exit;
826 } catch (Exception $ex) {
827 self::embedpress_die($ex);
828 }
829 }
830 public static function admin_post_authorize() {
831 if ( ! isset( $_POST['epgc_authorize_data'] ) || ! wp_verify_nonce( $_POST['epgc_authorize_data'], 'epgc_authorize' ) || !current_user_can('manage_options')) {
832 print 'Sorry, your nonce did not verify.';
833 exit;
834 } else {
835 try {
836 $client = self::getGoogleClient();
837 $client->authorize();
838 exit;
839 } catch (Exception $ex) {
840 self::embedpress_die($ex);
841 }
842 }
843
844 }
845
846 public static function fetch_calendar() {
847 if ( empty( $_GET['page']) || 'embedpress' !== $_GET['page'] ) {
848 return;
849 }
850
851 if ( !current_user_can( 'manage_options') ) {
852 return;
853 }
854 if (!empty($_GET['code'])) {
855 // Redirect from Google authorize with code that we can use to get access and refresh tokens.
856 try {
857 $client = self::getGoogleClient();
858 // This will also set the access and refresh tokens on the client
859 // and call the token callback we have set to save them in the options table.
860 $client->handleCodeRedirect();
861 $service = new Embedpress_GoogleCalendarClient($client);
862 $items = $service->getCalendarList();
863 self::sort_calendars($items);
864
865 update_option('epgc_calendarlist', self::getPrettyJSONString($items), false);
866 wp_redirect(EPGC_REDIRECT_URL);
867 exit;
868 } catch (Exception $ex) {
869 self::embedpress_die($ex);
870 }
871
872 }
873
874 $clientSecretError = '';
875 $clientSecret = self::get_valid_client_secret($clientSecretError);
876
877 $accessToken = self::getDecoded('epgc_access_token');
878
879 if (empty($clientSecret) || !empty($clientSecretError)) {
880 update_option('epgc_selected_calendar_ids', [], false);
881 }
882 if (!empty($accessToken)) {
883 // validate_selected_calendar_ids
884 }
885 if (empty($clientSecret) || !empty($clientSecretError)) {
886 // save new data from user input, show them input
887
888 } elseif (self::getDecoded('epgc_calendarlist')) {
889 // show calendar list
890 }
891
892 }
893 }
894
895
896
897
898
899
900 /**
901 * Add 'eepgcnotice' to the removable_query_args filter, so we can set this and
902 * WP will remove it for us. We use this for our custom admin notices. This way
903 * you can add parameters to the URL and check for them, but we won't see them
904 * in the URL.
905 */
906 add_filter('removable_query_args', [Embedpress_Google_Helper::class, 'removable_query_args']);
907
908 /**
909 * Check for 'epgcnotice' parameter and show admin notice if we have a option.
910 */
911 add_action('admin_init', [Embedpress_Google_Helper::class,'notices_init']);
912
913 /**
914 * Handle AJAX request from frontend.
915 */
916 add_action('wp_ajax_epgc_ajax_get_calendar', [Embedpress_Google_Helper::class, 'ajax_get_calendar']);
917 add_action('wp_ajax_nopriv_epgc_ajax_get_calendar', [Embedpress_Google_Helper::class, 'ajax_get_calendar']);
918
919 add_action('admin_post_epgc_calendarlist', [Embedpress_Google_Helper::class,'admin_post_calendarlist']);
920
921
922 add_action('admin_post_epgc_colorlist', [Embedpress_Google_Helper::class, 'admin_post_colorlist']);
923 add_action('admin_post_epgc_deletecache', [Embedpress_Google_Helper::class, 'admin_post_deletecache']);
924
925
926 /**
927 * Admin post action to verify if we have valid access and refresh token.
928 */
929 add_action('admin_post_epgc_verify', [Embedpress_Google_Helper::class, 'admin_post_verify']);
930
931 add_shortcode( 'embedpress_calendar', [Embedpress_Google_Helper::class, 'shortcode']);
932 add_action('wp_enqueue_scripts', [Embedpress_Google_Helper::class, 'enqueue_scripts'], EPGC_ENQUEUE_ACTION_PRIORITY);
933
934 add_action('admin_post_epgc_remove_private', [Embedpress_Google_Helper::class, 'remove_private_data']);
935
936 /**
937 * Admin post action to authorize access.
938 */
939 add_action('admin_post_epgc_authorize', [Embedpress_Google_Helper::class, 'admin_post_authorize']);
940
941 add_action('admin_init', [Embedpress_Google_Helper::class, 'fetch_calendar']);
942
943