: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Helper function to return pretty printed JSON string.
public static function getPrettyJSONString($jsonObject) {
return str_replace(" ", " ", json_encode($jsonObject, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
public static function sort_calendars(&$items) {
// Set locale to UTF-8 variant if this is not the case.
if (strpos(setlocale(LC_COLLATE, 0), '.UTF-8') === false) {
// If we set this to a non existing locale it will be the default locale after this call.
setlocale(LC_COLLATE, get_locale() . '.UTF-8');
usort($items, function($a, $b) {
return strcoll($a['summary'], $b['summary']);
public static function shortcode($atts = [], $content = null) {
// When we have no attributes, $atts is an empty string
foreach ($atts as &$value) {
$value = esc_attr($value);
unset($value); // Unset reference
wp_enqueue_style('dashicons');
wp_enqueue_style( 'fullcalendar');
wp_enqueue_style( 'fullcalendar_daygrid');
wp_enqueue_style( 'fullcalendar_timegrid');
wp_enqueue_style( 'fullcalendar_list');
wp_enqueue_style( 'epgc');
wp_enqueue_style( 'tippy_light');
wp_enqueue_script('popper');
wp_enqueue_script('tippy');
wp_enqueue_script('my_moment');
wp_enqueue_script('my_moment_timezone');
wp_enqueue_script('fullcalendar');
wp_enqueue_script('fullcalendar_moment');
wp_enqueue_script('fullcalendar_moment_timezone');
wp_enqueue_script('fullcalendar_daygrid');
wp_enqueue_script('fullcalendar_timegrid');
wp_enqueue_script('fullcalendar_list');
wp_enqueue_script('fullcalendar_locales');
wp_enqueue_script('epgc');
'left' => 'prev,next today',
'right' => 'dayGridMonth,timeGridWeek,listWeek'
$userConfig = $defaultConfig; // copy
$userEventPopup = 'true';
$userHidePassed = 'false';
$userHideFuture = 'false';
$userEventDescription = 'true';
$userEventLocation = 'true';
$userEventAttendees = 'false';
$userEventAttachments = 'false';
$userEventCreator = 'false';
$userEventCalendarname = 'false';
$uncheckedCalendarIds = ''; // in filter
// Get all non-fullcalendar known properties
foreach ($atts as $key => $value) {
// This existsed in old versions, but we don't want it in our shortcode output, so skip it.
$userFilter = $value === 'true' ? 'top' : $value;
if ($key === 'eventpopup') {
$userEventPopup = $value;
if ($key === 'eventlink') {
if ($key === 'hidepassed') {
$userHidePassed = $value;
if ($key === 'hidefuture') {
$userHideFuture = $value;
if ($key === 'eventdescription') {
$userEventDescription = $value;
if ($key === 'eventattachments') {
$userEventAttachments = $value;
if ($key === 'eventattendees') {
$userEventAttendees = $value;
if ($key === 'eventlocation') {
$userEventLocation = $value;
if ($key === 'eventcreator') {
$userEventCreator = $value;
if ($key === 'eventcalendarname') {
$userEventCalendarname = $value;
if ($key === 'uncheckedcalendarids' && !empty($value)) {
$uncheckedCalendarIds = $value; // comma separated string
if ($key === 'calendarids') {
$calendarIds = $value; // comma separated string
if ($key === 'fullcalendarconfig') {
// A JSON string that we can directly send to FullCalendar
$userConfig = json_decode($value, true);
// Fullcalendar properties that get passed to fullCalendar instance.
$parts = explode('-', $key);
$partsCount = count($parts);
$currentUserConfigLayer = &$userConfig;
for ($i = 0; $i < $partsCount; $i++) {
if ($i + 1 === $partsCount) {
} elseif ($value === 'false') {
$currentUserConfigLayer[$part] = $value;
if (!array_key_exists($part, $currentUserConfigLayer)) {
$currentUserConfigLayer[$part] = [];
$currentUserConfigLayer = &$currentUserConfigLayer[$part];
$userConfig[$key] = $value;
if (!empty($calendarIds)) {
$dataCalendarIds = 'data-calendarids=\'' . json_encode(array_map('trim', explode(',', $calendarIds))) . '\'';
$privateSettingsSelectedCalendarListIds = get_option('epgc_selected_calendar_ids', []);
if (!empty($privateSettingsSelectedCalendarListIds)) {
$dataCalendarIds = 'data-calendarids=\'' . json_encode($privateSettingsSelectedCalendarListIds) . '\'';
$dataUnchekedCalendarIds = '';
if (!empty($uncheckedCalendarIds)) {
$dataUnchekedCalendarIds = 'data-uncheckedcalendarids=\'' . json_encode(array_map('trim', explode(',', $uncheckedCalendarIds))) . '\'';
$filterHTML = '<div class="epgc-calendar-filter" ' . $dataUnchekedCalendarIds . '></div>';
return '<div class="epgc-calendar-wrapper epgc-calendar-page">' . ($userFilter === 'top' ? wp_kses_post($filterHTML) : '') . '<div '
. esc_attr($dataCalendarIds) . ' data-filter=\''
. esc_attr($userFilter) . '\' data-eventpopup=\''
. esc_attr($userEventPopup) . '\' data-eventlink=\''
. esc_attr($userEventLink) . '\' data-eventdescription=\''
. esc_attr($userEventDescription) . '\' data-eventlocation=\''
. esc_attr($userEventLocation) . '\' data-eventattachments=\''
. esc_attr($userEventAttachments) . '\' data-eventattendees=\''
. esc_attr($userEventAttendees) . '\' data-eventcreator=\''
. esc_attr($userEventCreator) . '\' data-eventcalendarname=\''
. esc_attr($userEventCalendarname) . '\' data-hidefuture=\''
. esc_attr($userHideFuture) . '\' data-hidepassed=\''
. esc_attr($userHidePassed) . '\' data-config=\''
. json_encode($userConfig) . '\' data-locale="'
. '" class="epgc-calendar"></div>'
. ($userFilter === 'bottom' ? wp_kses_post($filterHTML) : '')
public static function admin_post_calendarlist() {
$client = static::getGoogleClient(true);
if ($client->isAccessTokenExpired()) {
if (!$client->getRefreshToken()) {
throw new Exception(EPGC_ERRORS_REFRESH_TOKEN_MISSING);
$client->refreshAccessToken();
$service = new Embedpress_GoogleCalendarClient($client);
$items = $service->getCalendarList();
self::sort_calendars($items);
update_option('epgc_calendarlist', self::getPrettyJSONString($items), false);
self::add_notice(PGC_NOTICES_CALENDARLIST_UPDATE_SUCCESS, 'success', true);
} catch (Exception $ex) {
self::embedpress_die($ex);
public static function admin_post_colorlist() {
$client = static::getGoogleClient(true);
if ($client->isAccessTokenExpired()) {
if (!$client->getRefreshToken()) {
throw new Exception(PGC_ERRORS_REFRESH_TOKEN_MISSING);
$client->refreshAccessToken();
$service = new Embedpress_GoogleCalendarClient($client);
$items = $service->getColorList();
update_option('epgc_colorlist', self::getPrettyJSONString($items), false);
self::add_notice(EPGC_NOTICES_COLORLIST_UPDATE_SUCCESS, 'success', true);
} catch (Exception $ex) {
self::embedpress_die($ex);
public static function admin_post_deletecache() {
if ( ! isset( $_POST['epgc_deletecache_data'] ) || ! wp_verify_nonce( $_POST['epgc_deletecache_data'], 'epgc_deletecache' ) || !current_user_can('manage_options')) {
print 'Sorry, your nonce did not verify.';
self::delete_calendar_cache();
self::add_notice(PGC_NOTICES_CACHE_DELETED, 'success', true);
public static function admin_post_verify() {
$client = static::getGoogleClient(true);
$client->refreshAccessToken();
self::add_notice(PGC_NOTICES_VERIFY_SUCCESS, 'success', true);
} catch (Exception $ex) {
self::embedpress_die($ex);
public static function enqueue_scripts() {
wp_enqueue_style('dashicons');
wp_register_style('fullcalendar', EPGC_ASSET_URL . 'lib/fullcalendar4/core/main.min.css', null, EMBEDPRESS_VERSION);
wp_register_style('fullcalendar_daygrid', EPGC_ASSET_URL . 'lib/fullcalendar4/daygrid/main.min.css', ['fullcalendar'], EMBEDPRESS_VERSION);
wp_register_style('fullcalendar_timegrid', EPGC_ASSET_URL . 'lib/fullcalendar4/timegrid/main.min.css', ['fullcalendar_daygrid'], EMBEDPRESS_VERSION);
wp_register_style('fullcalendar_list', EPGC_ASSET_URL . 'lib/fullcalendar4/list/main.min.css', ['fullcalendar'], EMBEDPRESS_VERSION);
wp_register_style('epgc', EPGC_ASSET_URL . 'css/epgc.css', ['fullcalendar_timegrid'], EMBEDPRESS_VERSION);
wp_register_style('tippy_light', EPGC_ASSET_URL . 'lib/tippy/light-border.css', null, EMBEDPRESS_VERSION);
//wp_enqueue_style( 'fullcalendar');
//wp_enqueue_style( 'fullcalendar_daygrid');
//wp_enqueue_style( 'fullcalendar_timegrid');
//wp_enqueue_style( 'fullcalendar_list');
//wp_enqueue_style( 'epgc');
//wp_enqueue_style( 'tippy_light');
wp_register_script('popper',EPGC_ASSET_URL . 'lib/popper.min.js', null, EMBEDPRESS_VERSION, true);
wp_register_script('tippy',EPGC_ASSET_URL . 'lib/tippy/tippy-bundle.umd.min.js', ['popper'], EMBEDPRESS_VERSION, true);
wp_register_script('my_moment',EPGC_ASSET_URL . 'lib/moment/moment-with-locales.min.js', null, EMBEDPRESS_VERSION, true);
wp_register_script('my_moment_timezone',EPGC_ASSET_URL . 'lib/moment/moment-timezone-with-data.min.js', ['my_moment'], EMBEDPRESS_VERSION, true);
wp_register_script('fullcalendar',EPGC_ASSET_URL . 'lib/fullcalendar4/core/main.min.js', ['my_moment_timezone'], EMBEDPRESS_VERSION, true);
wp_register_script('fullcalendar_moment',EPGC_ASSET_URL . 'lib/fullcalendar4/moment/main.min.js', ['fullcalendar'], EMBEDPRESS_VERSION, true);
wp_register_script('fullcalendar_moment_timezone',EPGC_ASSET_URL . 'lib/fullcalendar4/moment-timezone/main.min.js', ['fullcalendar_moment'], EMBEDPRESS_VERSION, true);
wp_register_script('fullcalendar_daygrid',EPGC_ASSET_URL . 'lib/fullcalendar4/daygrid/main.min.js', ['fullcalendar'], EMBEDPRESS_VERSION, true);
wp_register_script('fullcalendar_timegrid',EPGC_ASSET_URL . 'lib/fullcalendar4/timegrid/main.min.js', ['fullcalendar_daygrid'], EMBEDPRESS_VERSION, true);
wp_register_script('fullcalendar_list',EPGC_ASSET_URL . 'lib/fullcalendar4/list/main.min.js', ['fullcalendar'], EMBEDPRESS_VERSION, true);
wp_register_script('fullcalendar_locales',EPGC_ASSET_URL . 'lib/fullcalendar4/core/locales-all.min.js',['fullcalendar'], EMBEDPRESS_VERSION, true);
wp_register_script('epgc', EPGC_ASSET_URL . 'js/main.js',['fullcalendar'], EMBEDPRESS_VERSION, true);
//wp_enqueue_script('popper');
//wp_enqueue_script('my_moment');
//wp_enqueue_script('my_moment_timezone');
//wp_enqueue_script('fullcalendar');
//wp_enqueue_script('fullcalendar_moment');
//wp_enqueue_script('fullcalendar_moment_timezone');
//wp_enqueue_script('fullcalendar_daygrid');
//wp_enqueue_script('fullcalendar_timegrid');
//wp_enqueue_script('fullcalendar_list');
//wp_enqueue_script('fullcalendar_locales');
//wp_enqueue_script('epgc');
$nonce = wp_create_nonce('epgc_nonce');
wp_localize_script('epgc', 'epgc_object', [
'ajax_url' => admin_url('admin-ajax.php'),
'all_day' => __('All day', 'embedpress'),
'created_by' => __('Created by', 'embedpress'),
'go_to_event' => __('Go to event', 'embedpress'),
'unknown_error' => __('Unknown error', 'embedpress'),
'request_error' => __('Request error', 'embedpress'),
'loading' => __('Loading', 'embedpress')
public static function remove_private_data() {
if ( ! isset( $_POST['epgc_remove_private_data'] ) || ! wp_verify_nonce( $_POST['epgc_remove_private_data'], 'epgc_remove_private' ) || !current_user_can('manage_options')) {
print 'Sorry, your nonce did not verify.';
self::delete_plugin_data('private');
self::add_notice(EPGC_NOTICES_REMOVE_SUCCESS, 'success', true);
public static function admin_post_remove() {
if ( ! isset( $_POST['epgc_remove_private_data'] ) || ! wp_verify_nonce( $_POST['epgc_remove_private_data'], 'epgc_remove_private' ) || !current_user_can('manage_options')) {
print 'Sorry, your nonce did not verify.';
self::delete_plugin_data();
self::add_notice(EPGC_NOTICES_REMOVE_SUCCESS, 'success', true);
public static function admin_post_revoke() {
$client = self::getGoogleClient();
$accessToken = self::getDecoded('epgc_access_token');
if (!empty($accessToken)) {
$client->setAccessTokenInfo($accessToken);
$refreshToken = get_option("epgc_refresh_token");
if (!empty($refreshToken)) {
$client->setRefreshToken($refreshToken);
if (empty($accessToken) && empty($refreshToken)) {
throw new Exception(EPGC_ERRORS_ACCESS_REFRESH_TOKEN_MISSING);
// Clear access and refresh tokens
self::delete_plugin_data('private');
self::add_notice(EPGC_NOTICES_REVOKE_SUCCESS, 'success', true);
} catch (Exception $ex) {
self::embedpress_die($ex);
public static function admin_post_authorize() {
if ( ! isset( $_POST['epgc_authorize_data'] ) || ! wp_verify_nonce( $_POST['epgc_authorize_data'], 'epgc_authorize' ) || !current_user_can('manage_options')) {
print 'Sorry, your nonce did not verify.';
$client = self::getGoogleClient();
} catch (Exception $ex) {
self::embedpress_die($ex);
public static function fetch_calendar() {
if ( empty( $_GET['page']) || 'embedpress' !== $_GET['page'] ) {
if ( !current_user_can( 'manage_options') ) {
if (!empty($_GET['code'])) {
// Redirect from Google authorize with code that we can use to get access and refresh tokens.
$client = self::getGoogleClient();
// This will also set the access and refresh tokens on the client
// and call the token callback we have set to save them in the options table.
$client->handleCodeRedirect();
$service = new Embedpress_GoogleCalendarClient($client);
$items = $service->getCalendarList();
self::sort_calendars($items);
update_option('epgc_calendarlist', self::getPrettyJSONString($items), false);
wp_redirect(EPGC_REDIRECT_URL);
} catch (Exception $ex) {
self::embedpress_die($ex);
$clientSecret = self::get_valid_client_secret($clientSecretError);
$accessToken = self::getDecoded('epgc_access_token');
if (empty($clientSecret) || !empty($clientSecretError)) {
update_option('epgc_selected_calendar_ids', [], false);
if (!empty($accessToken)) {
// validate_selected_calendar_ids
if (empty($clientSecret) || !empty($clientSecretError)) {
// save new data from user input, show them input
} elseif (self::getDecoded('epgc_calendarlist')) {
* Add 'eepgcnotice' to the removable_query_args filter, so we can set this and
* WP will remove it for us. We use this for our custom admin notices. This way
* you can add parameters to the URL and check for them, but we won't see them
add_filter('removable_query_args', [Embedpress_Google_Helper::class, 'removable_query_args']);
* Check for 'epgcnotice' parameter and show admin notice if we have a option.
add_action('admin_init', [Embedpress_Google_Helper::class,'notices_init']);
* Handle AJAX request from frontend.
add_action('wp_ajax_epgc_ajax_get_calendar', [Embedpress_Google_Helper::class, 'ajax_get_calendar']);
add_action('wp_ajax_nopriv_epgc_ajax_get_calendar', [Embedpress_Google_Helper::class, 'ajax_get_calendar']);
add_action('admin_post_epgc_calendarlist', [Embedpress_Google_Helper::class,'admin_post_calendarlist']);
add_action('admin_post_epgc_colorlist', [Embedpress_Google_Helper::class, 'admin_post_colorlist']);
add_action('admin_post_epgc_deletecache', [Embedpress_Google_Helper::class, 'admin_post_deletecache']);
* Admin post action to verify if we have valid access and refresh token.
add_action('admin_post_epgc_verify', [Embedpress_Google_Helper::class, 'admin_post_verify']);
add_shortcode( 'embedpress_calendar', [Embedpress_Google_Helper::class, 'shortcode']);
add_action('wp_enqueue_scripts', [Embedpress_Google_Helper::class, 'enqueue_scripts'], EPGC_ENQUEUE_ACTION_PRIORITY);
add_action('admin_post_epgc_remove_private', [Embedpress_Google_Helper::class, 'remove_private_data']);
* Admin post action to authorize access.
add_action('admin_post_epgc_authorize', [Embedpress_Google_Helper::class, 'admin_post_authorize']);
add_action('admin_init', [Embedpress_Google_Helper::class, 'fetch_calendar']);