Source: my-calendar-core.php

  1. <?php
  2. /**
  3. * Core functions of My Calendar infrastructure - installation, upgrading, action links, etc.
  4. *
  5. * @category Core
  6. * @package My Calendar
  7. * @author Joe Dolson
  8. * @license GPLv2 or later
  9. * @link https://www.joedolson.com/my-calendar/
  10. */
  11. if ( ! defined( 'ABSPATH' ) ) {
  12. exit;
  13. }
  14. /**
  15. * Add feeds to WordPress feed handler.
  16. */
  17. function mc_add_feed() {
  18. add_feed( 'my-calendar-ics', 'my_calendar_ical' );
  19. add_feed( 'my-calendar-google', 'mc_ics_subscribe_google' );
  20. add_feed( 'my-calendar-outlook', 'mc_ics_subscribe_outlook' ); // Deprecated. Still in place for back compat.
  21. }
  22. /**
  23. * If user is logged in, do not cache feeds.
  24. *
  25. * @param object $feed Feed object.
  26. */
  27. function mc_cache_feeds( &$feed ) {
  28. if ( is_user_logged_in() ) {
  29. $feed->enable_cache( false );
  30. }
  31. }
  32. add_action( 'wp_feed_options', 'mc_cache_feeds' );
  33. /**
  34. * Add plug-in info page links to Plugins page
  35. *
  36. * @param array $links default set of plug-in links.
  37. * @param string $file Current file (not used by custom function.).
  38. *
  39. * @return array updated set of links
  40. */
  41. function mc_plugin_action( $links, $file ) {
  42. if ( plugin_basename( __DIR__ . '/my-calendar.php' ) === $file ) {
  43. $links[] = '<a href="admin.php?page=my-calendar-config">' . __( 'Settings', 'my-calendar' ) . '</a>';
  44. $links[] = '<a href="admin.php?page=my-calendar-help">' . __( 'Help', 'my-calendar' ) . '</a>';
  45. if ( ! function_exists( 'mcs_submissions' ) ) {
  46. $links[] = '<a href="https://www.joedolson.com/my-calendar-pro/">' . __( 'Go Pro', 'my-calendar' ) . '</a>';
  47. }
  48. }
  49. return $links;
  50. }
  51. /**
  52. * Get custom styles dir locations, with trailing slash,
  53. * or get custom styles url locations, with trailing slash.
  54. *
  55. * @param string $type path or url, default = path.
  56. *
  57. * @return array with locations or empty.
  58. */
  59. function mc_custom_dirs( $type = 'path' ) {
  60. $dirs = array();
  61. $dirs[] = ( 'path' === $type ) ? plugin_dir_path( __DIR__ ) . 'my-calendar-custom/styles/' : plugin_dir_url( __DIR__ ) . 'my-calendar-custom/styles/';
  62. $dirs[] = ( 'path' === $type ) ? plugin_dir_path( __DIR__ ) . 'my-calendar-custom/' : plugin_dir_url( __DIR__ ) . 'my-calendar-custom/';
  63. $dirs[] = ( 'path' === $type ) ? get_stylesheet_directory() . '/css/' : get_stylesheet_directory_uri() . '/css/';
  64. $dirs[] = ( 'path' === $type ) ? get_stylesheet_directory() . '/' : get_stylesheet_directory_uri() . '/';
  65. /**
  66. * Filter My Calendar's array of directories to check for custom files. Use to define where your custom templates and styles will be found.
  67. *
  68. * @hook mc_custom_dirs
  69. *
  70. * @param {array} $dirs Array of directory paths to check.
  71. * @param {string} $type Checking paths or URLs.
  72. *
  73. * @return {array}
  74. */
  75. $directories = apply_filters( 'mc_custom_dirs', $dirs, $type );
  76. return ( is_array( $directories ) && ! empty( $directories ) ) ? $directories : $dirs;
  77. }
  78. /**
  79. * Check whether requested file exists in calendar custom directory.
  80. *
  81. * @param string $file file name.
  82. *
  83. * @return boolean
  84. */
  85. function mc_file_exists( $file ) {
  86. $file = sanitize_file_name( $file );
  87. /**
  88. * Filter test for whether a file exists. Return true to confirm file exists.
  89. *
  90. * @hook mc_file_exists
  91. *
  92. * @param {bool} $path File path.
  93. * @param {string} $file File name.
  94. *
  95. * @return {bool}
  96. */
  97. $return = apply_filters( 'mc_file_exists', false, $file );
  98. if ( $return ) {
  99. return true;
  100. }
  101. foreach ( mc_custom_dirs() as $dir ) {
  102. if ( file_exists( $dir . $file ) ) {
  103. return true;
  104. }
  105. }
  106. return false;
  107. }
  108. /**
  109. * Fetch a file by path or URL. Checks multiple directories to see which to get.
  110. *
  111. * @param string $file name of file to get.
  112. * @param string $type either path or url.
  113. *
  114. * @return string full path or url.
  115. */
  116. function mc_get_file( $file, $type = 'path' ) {
  117. $file = sanitize_file_name( $file ); // This will remove slashes as well.
  118. $dir = plugin_dir_path( __FILE__ );
  119. $url = plugin_dir_url( __FILE__ );
  120. $path = ( 'path' === $type ) ? $dir . $file : $url . $file;
  121. foreach ( mc_custom_dirs() as $key => $dir ) {
  122. if ( file_exists( $dir . $file ) ) {
  123. if ( 'path' === $type ) {
  124. $path = $dir . $file;
  125. } else {
  126. $urls = mc_custom_dirs( $type );
  127. $path = $urls[ $key ] . $file;
  128. }
  129. break;
  130. }
  131. }
  132. /**
  133. * Filter file paths for My Calendar files.
  134. *
  135. * @hook mc_get_file
  136. *
  137. * @param {string} $path File path.
  138. * @param {string} $file File name.
  139. *
  140. * @return {string}
  141. */
  142. $path = apply_filters( 'mc_get_file', $path, $file );
  143. return $path;
  144. }
  145. add_filter( 'mc_registered_stylesheet', 'mc_preview_stylesheet', 10, 1 );
  146. /**
  147. * Allow users with 'mc_edit_styles' permissions to preview stylesheets.
  148. *
  149. * @param string $file CSS filename.
  150. *
  151. * @return string
  152. */
  153. function mc_preview_stylesheet( $file ) {
  154. if ( isset( $_GET['mcpreview'] ) && current_user_can( 'mc_edit_styles' ) ) {
  155. $file = mc_get_style_path( sanitize_text_field( $_GET['mcpreview'] ), 'url' );
  156. }
  157. return $file;
  158. }
  159. /**
  160. * Ensure that expected style variables are always present.
  161. *
  162. * @param array $styles Array of style variables saved in settings.
  163. *
  164. * @return array
  165. */
  166. function mc_style_variables( $styles = array() ) {
  167. $core_styles = array(
  168. '--close-button' => '#b32d2e',
  169. '--search-highlight-bg ' => '#f5e6ab',
  170. '--navbar-background' => 'transparent',
  171. '--nav-button-bg' => '#fff',
  172. '--nav-button-color' => '#313233',
  173. '--nav-button-border' => '#313233',
  174. '--nav-input-border' => '#313233',
  175. '--nav-input-background' => '#fff',
  176. '--nav-input-color' => '#313233',
  177. '--grid-cell-border' => '#0000001f',
  178. '--grid-header-border' => '#313233',
  179. '--grid-header-color' => '#313233',
  180. '--grid-weekend-color' => '#313233',
  181. '--grid-header-bg' => 'transparent',
  182. '--grid-weekend-bg' => 'transparent',
  183. '--grid-cell-background' => 'transparent',
  184. '--current-day-border' => '#313233',
  185. '--current-day-color' => '#313233',
  186. '--current-day-bg' => 'transparent',
  187. '--date-has-events-bg' => '#313233',
  188. '--date-has-events-color' => '#f6f7f7',
  189. '--primary-dark' => '#313233',
  190. '--primary-light' => '#f6f7f7',
  191. '--secondary-light' => '#fff',
  192. '--secondary-dark' => '#000',
  193. '--highlight-dark' => '#646970',
  194. '--highlight-light' => '#f0f0f1',
  195. 'text' => array(
  196. '--calendar-heading' => 'clamp( 1.125rem, 24px, 2.5rem )',
  197. '--event-title' => 'clamp( 1.25rem, 24px, 2.5rem )',
  198. '--grid-date' => '16px',
  199. '--grid-date-heading' => 'clamp( .75rem, 16px, 1.5rem )',
  200. '--modal-title' => '1.5rem',
  201. '--navigation-controls' => 'clamp( .75rem, 16px, 1.5rem )',
  202. '--card-heading' => '1.125rem',
  203. '--list-date' => '1.25rem',
  204. '--author-card' => 'clamp( .75rem, 14px, 1.5rem)',
  205. '--single-event-title' => 'clamp( 1.25rem, 24px, 2.5rem )',
  206. '--mini-time-text' => 'clamp( .75rem, 14px 1.25rem )',
  207. '--list-event-date' => '1.25rem',
  208. '--list-event-title' => '1.2rem',
  209. ),
  210. 'sizing' => array(
  211. '--grid-max-width' => '1260px',
  212. ),
  213. 'list-presets' => array(
  214. '--list-preset-border-color' => '#000000',
  215. '--list-preset-stripe-background' => 'rgba( 0,0,0,.04 )',
  216. '--list-preset-date-badge-background' => '#000',
  217. '--list-preset-date-badge-color' => '#fff',
  218. '--list-preset-background' => 'transparent',
  219. '--list-preset-color' => '',
  220. ),
  221. );
  222. foreach ( $core_styles as $key => $value ) {
  223. if ( 'text' === $key || 'sizing' === $key || 'list-presets' === $key ) {
  224. foreach ( $value as $var => $text ) {
  225. if ( ! isset( $styles[ $key ][ $var ] ) ) {
  226. $styles[ $key ][ $var ] = $text;
  227. }
  228. }
  229. }
  230. if ( ! isset( $styles[ $key ] ) ) {
  231. $styles[ $key ] = $value;
  232. }
  233. }
  234. return $styles;
  235. }
  236. add_action( 'wp_enqueue_scripts', 'mc_register_styles', 20 );
  237. /**
  238. * Publically enqueued styles & scripts
  239. */
  240. function mc_register_styles() {
  241. global $wp_query;
  242. $version = mc_get_version();
  243. if ( SCRIPT_DEBUG ) {
  244. $version .= wp_rand( 10000, 99999 );
  245. }
  246. $this_post = $wp_query->get_queried_object();
  247. /**
  248. * Filter url to get My Calendar stylesheet.
  249. *
  250. * @hook mc_registered_stylesheet
  251. *
  252. * @param {string} $stylesheet URL to locate My Calendar's stylesheet.
  253. *
  254. * @return {string}
  255. */
  256. $stylesheet = apply_filters( 'mc_registered_stylesheet', mc_get_style_path( mc_get_option( 'css_file' ), 'url' ) );
  257. wp_register_style( 'my-calendar-lists', plugins_url( 'css/list-presets.css', __FILE__ ), array(), $version );
  258. wp_register_style( 'my-calendar-reset', plugins_url( 'css/reset.css', __FILE__ ), array( 'dashicons', 'my-calendar-lists' ), $version );
  259. wp_register_style( 'my-calendar-style', $stylesheet, array( 'my-calendar-reset' ), $version . '-' . sanitize_title( mc_get_option( 'css_file' ) ) );
  260. wp_register_style( 'my-calendar-locations', plugins_url( 'css/locations.css', __FILE__ ), array( 'dashicons' ), $version );
  261. if ( is_singular( 'mc-locations' ) ) {
  262. wp_enqueue_style( 'my-calendar-locations' );
  263. }
  264. $admin_stylesheet = plugins_url( 'css/mc-admin.css', __FILE__ );
  265. wp_register_style( 'my-calendar-frontend-admin-style', $admin_stylesheet, array(), $version );
  266. if ( current_user_can( 'mc_manage_events' ) ) {
  267. wp_enqueue_style( 'my-calendar-frontend-admin-style' );
  268. }
  269. /**
  270. * Filter whether My Calendar styles should be displayed on archive pages. Default 'true'.
  271. *
  272. * @hook mc_display_css_on_archives
  273. *
  274. * @param {bool} $default 'true' to display.
  275. * @param {WP_Query} $wp_query WP Query.
  276. *
  277. * @return {bool}
  278. */
  279. $default = apply_filters( 'mc_display_css_on_archives', true, $wp_query );
  280. $id = ( is_object( $this_post ) && isset( $this_post->ID ) ) ? $this_post->ID : false;
  281. $js_array = ( '' !== trim( mc_get_option( 'show_js' ) ) ) ? explode( ',', mc_get_option( 'show_js' ) ) : array();
  282. $css_array = ( '' !== trim( mc_get_option( 'show_css' ) ) ) ? explode( ',', mc_get_option( 'show_css' ) ) : array();
  283. $use_default = ( $default && ! $id ) ? true : false;
  284. $js_usage = ( ( empty( $js_array ) ) || ( $id && in_array( (string) $id, $js_array, true ) ) ) ? true : false;
  285. $css_usage = ( ( empty( $css_array ) ) || ( $id && in_array( (string) $id, $css_array, true ) ) ) ? true : false;
  286. // check whether any scripts are actually enabled.
  287. if ( mc_get_option( 'calendar_javascript' ) !== '1' || mc_get_option( 'list_javascript' ) !== '1' || mc_get_option( 'mini_javascript' ) !== '1' || mc_get_option( 'ajax_javascript' ) !== '1' ) {
  288. if ( $use_default || $js_usage || is_singular( 'mc-events' ) || is_singular( 'mc-locations' ) ) {
  289. wp_enqueue_script( 'jquery' );
  290. if ( 'true' === mc_get_option( 'mc_gmap' ) || mc_output_is_visible( 'gmap', 'single' ) || is_singular( 'mc-locations' ) ) {
  291. $api_key = mc_get_option( 'gmap_api_key' );
  292. if ( $api_key ) {
  293. wp_enqueue_script( 'gmaps', "https://maps.googleapis.com/maps/api/js?v=3&key=$api_key", array() );
  294. wp_enqueue_script( 'mc-maps', plugins_url( 'js/gmaps.js', __FILE__ ), array( 'gmaps' ), $version, true );
  295. wp_localize_script(
  296. 'mc-maps',
  297. 'gmaps',
  298. array(
  299. 'toggle' => '<span class="dashicons dashicons-arrow-right" aria-hidden="true"></span><span class="screen-reader-text">' . __( 'Location Details', 'my-calendar' ) . '</span>',
  300. )
  301. );
  302. }
  303. }
  304. }
  305. }
  306. // True means styles are disabled.
  307. if ( 'true' !== mc_get_option( 'use_styles' ) ) {
  308. if ( $use_default || $css_usage ) {
  309. mc_enqueue_calendar_styles( $stylesheet );
  310. }
  311. }
  312. if ( mc_is_tablet() && mc_file_exists( 'mc-tablet.css' ) ) {
  313. $tablet = mc_get_file( 'mc-tablet.css' );
  314. wp_register_style( 'my-calendar-tablet-style', $tablet );
  315. wp_enqueue_style( 'my-calendar-tablet-style' );
  316. }
  317. if ( mc_is_mobile() && mc_file_exists( 'mc-mobile.css' ) ) {
  318. $mobile = mc_get_file( 'mc-mobile.css' );
  319. wp_register_style( 'my-calendar-mobile-style', $mobile );
  320. wp_enqueue_style( 'my-calendar-mobile-style' );
  321. }
  322. }
  323. /**
  324. * Enqueue calendar JS.
  325. */
  326. function mc_enqueue_calendar_js() {
  327. $version = mc_get_version();
  328. if ( SCRIPT_DEBUG ) {
  329. $version = $version . '-' . wp_rand( 10000, 100000 );
  330. }
  331. $grid = '';
  332. $mini = '';
  333. $list = '';
  334. $ajax = '';
  335. $enqueue = false;
  336. if ( '1' !== mc_get_option( 'calendar_javascript' ) && 'true' !== mc_get_option( 'open_uri' ) ) {
  337. /**
  338. * Filter to replace scripts used on front-end for grid behavior. Default empty string.
  339. *
  340. * @hook mc_grid_js
  341. *
  342. * @param {string} $url URL to JS to operate My Calendar grid view.
  343. *
  344. * @return {string}
  345. */
  346. $url = apply_filters( 'mc_grid_js', '' );
  347. $enqueue = true;
  348. if ( $url ) {
  349. wp_enqueue_script( 'mc.grid', $url, array( 'jquery' ), $version );
  350. } else {
  351. $grid = ( 'modal' === mc_get_option( 'calendar_javascript' ) ) ? 'modal' : 'true';
  352. }
  353. }
  354. if ( '1' !== mc_get_option( 'list_javascript' ) ) {
  355. /**
  356. * Filter to replace scripts used on front-end for list behavior. Default empty string.
  357. *
  358. * @hook mc_list_js
  359. *
  360. * @param {string} $url URL to JS to operate My Calendar list view.
  361. *
  362. * @return {string}
  363. */
  364. $url = apply_filters( 'mc_list_js', '' );
  365. $enqueue = true;
  366. if ( $url ) {
  367. wp_enqueue_script( 'mc.list', $url, array( 'jquery' ), $version );
  368. } else {
  369. $list = ( 'modal' === mc_get_option( 'list_javascript' ) ) ? 'modal' : 'true';
  370. }
  371. }
  372. if ( '1' !== mc_get_option( 'mini_javascript' ) && 'true' !== mc_get_option( 'open_day_uri' ) ) {
  373. /**
  374. * Filter to replace scripts used on front-end for mini calendar behavior. Default empty string.
  375. *
  376. * @hook mc_mini_js
  377. *
  378. * @param {string} $url URL to JS to operate My Calendar mini calendar.
  379. *
  380. * @return {string}
  381. */
  382. $url = apply_filters( 'mc_mini_js', '' );
  383. $enqueue = true;
  384. if ( $url ) {
  385. wp_enqueue_script( 'mc.mini', $url, array( 'jquery' ), $version );
  386. } else {
  387. $mini = ( 'modal' === mc_get_option( 'mini_javascript' ) ) ? 'modal' : 'true';
  388. }
  389. }
  390. if ( '1' !== mc_get_option( 'ajax_javascript' ) ) {
  391. /**
  392. * Filter to replace scripts used on front-end for AJAX behavior. Default empty string.
  393. *
  394. * @hook mc_ajax_js
  395. *
  396. * @param {string} $url URL to JS to operate My Calendar AJAX.
  397. *
  398. * @return {string}
  399. */
  400. $url = apply_filters( 'mc_ajax_js', '' );
  401. $enqueue = true;
  402. if ( $url ) {
  403. wp_enqueue_script( 'mc.ajax', $url, array( 'jquery' ), $version );
  404. } else {
  405. $ajax = 'true';
  406. }
  407. }
  408. if ( $enqueue ) {
  409. $url = ( true === SCRIPT_DEBUG ) ? plugins_url( 'js/mcjs.js', __FILE__ ) : plugins_url( 'js/mcjs.min.js', __FILE__ );
  410. wp_enqueue_script( 'mc.mcjs', $url, array( 'jquery', 'wp-a11y', 'wp-i18n' ), $version, true );
  411. $args = array(
  412. 'grid' => $grid,
  413. 'list' => $list,
  414. 'mini' => $mini,
  415. 'ajax' => $ajax,
  416. 'links' => mc_get_option( 'list_link_titles' ),
  417. 'newWindow' => __( 'New tab', 'my-calendar' ),
  418. 'subscribe' => ( '' === mc_get_option( 'subscribe' ) ) ? __( 'Subscribe', 'my-calendar' ) : mc_get_option( 'subscribe' ),
  419. 'export' => ( '' === mc_get_option( 'export' ) ) ? __( 'Export', 'my-calendar' ) : mc_get_option( 'export' ),
  420. 'action' => 'mcjs_action',
  421. 'security' => wp_create_nonce( 'mcjs-action' ),
  422. 'ajaxurl' => admin_url( 'admin-ajax.php' ),
  423. );
  424. wp_localize_script( 'mc.mcjs', 'my_calendar', $args );
  425. }
  426. $gridtype = mc_get_option( 'calendar_javascript' );
  427. $listtype = mc_get_option( 'list_javascript' );
  428. $minitype = mc_get_option( 'mini_javascript' );
  429. if ( 'modal' === $gridtype || 'modal' === $listtype || 'modal' === $minitype ) {
  430. $script = ( SCRIPT_DEBUG ) ? 'modal/accessible-modal-window-aria.js' : 'modal/accessible-modal-window-aria.min.js';
  431. wp_enqueue_script( 'mc-modal', plugins_url( 'js/' . $script, __FILE__ ), array(), $version, true );
  432. wp_localize_script(
  433. 'mc-modal',
  434. 'mcm',
  435. array(
  436. 'context' => (string) is_user_logged_in(),
  437. )
  438. );
  439. }
  440. }
  441. /**
  442. * Enqueue calendar stylesheet and inline styles.
  443. *
  444. * @param string $stylesheet Filename of the calendar stylesheet in use.
  445. */
  446. function mc_enqueue_calendar_styles( $stylesheet ) {
  447. if ( '' !== $stylesheet ) {
  448. wp_enqueue_style( 'my-calendar-style' );
  449. $inline = 'my-calendar-style';
  450. } else {
  451. wp_enqueue_style( 'my-calendar-reset' );
  452. $inline = 'my-calendar-reset';
  453. }
  454. $css = mc_generate_css();
  455. wp_add_inline_style( $inline, $css );
  456. }
  457. /**
  458. * Generate calendar CSS.
  459. */
  460. function mc_generate_css() {
  461. $category_vars = '';
  462. $category_styles = '';
  463. // generate category colors.
  464. $category_css = mc_generate_category_styles();
  465. if ( ! empty( $category_css ) && is_array( $category_css ) ) {
  466. $category_styles = ( isset( $category_css['styles'] ) ) ? $category_css['styles'] : '';
  467. $category_vars = ( isset( $category_css['vars'] ) ) ? $category_css['vars'] : '';
  468. }
  469. $styles = (array) mc_get_option( 'style_vars' );
  470. $styles = mc_style_variables( $styles );
  471. $style_vars = '';
  472. foreach ( $styles as $key => $var ) {
  473. if ( 'text' === $key || 'sizing' === $key || 'list-presets' === $key ) {
  474. foreach ( $var as $variable => $value ) {
  475. if ( $value ) {
  476. $style_vars .= sanitize_key( $variable ) . ': ' . esc_html( $value ) . '; ';
  477. }
  478. }
  479. } else {
  480. if ( $var ) {
  481. $style_vars .= sanitize_key( $key ) . ': ' . esc_html( $var ) . '; ';
  482. }
  483. }
  484. }
  485. if ( '' !== $style_vars ) {
  486. $style_vars = '.mc-main, .mc-event, .my-calendar-modal, .my-calendar-modal-overlay, .mc-event-list {' . $style_vars . $category_vars . '}';
  487. }
  488. $css = "
  489. /* Styles by My Calendar - Joe Dolson https://www.joedolson.com/ */
  490. $category_styles
  491. $style_vars";
  492. return $css;
  493. }
  494. /**
  495. * Add styles to the print view.
  496. */
  497. function mc_enqueue_calendar_print_styles() {
  498. $css = mc_generate_css();
  499. echo '<style>' . esc_html( wp_strip_all_tags( $css ) ) . '</style>';
  500. }
  501. add_action( 'mc_print_view_head', 'mc_enqueue_calendar_print_styles' );
  502. /**
  503. * Publically written head styles & scripts
  504. */
  505. function mc_head() {
  506. // If Yoast SEO is active, we don't need to output this schema.
  507. if ( defined( 'WPSEO_VERSION' ) ) {
  508. return;
  509. }
  510. if ( mc_is_single_event() ) {
  511. $mc_id = ( isset( $_GET['mc_id'] ) ) ? absint( $_GET['mc_id'] ) : false;
  512. if ( $mc_id ) {
  513. $event = mc_get_event( $mc_id );
  514. $schema = mc_event_schema( $event );
  515. echo PHP_EOL . '<script type="application/ld+json">' . PHP_EOL . '[' . wp_json_encode( map_deep( $schema, 'esc_html' ), JSON_UNESCAPED_SLASHES ) . ']' . PHP_EOL . '</script>' . PHP_EOL;
  516. }
  517. }
  518. if ( is_singular( 'mc-locations' ) ) {
  519. $loc_id = mc_get_location_id( get_the_ID() );
  520. $location = mc_get_location( $loc_id );
  521. $schema = mc_location_schema( $location );
  522. echo PHP_EOL . '<script type="application/ld+json">' . PHP_EOL . '[' . wp_json_encode( map_deep( $schema, 'esc_html' ), JSON_UNESCAPED_SLASHES ) . ']' . PHP_EOL . '</script>' . PHP_EOL;
  523. }
  524. $page_id = mc_get_option( 'uri_id' );
  525. $is_page = ( is_single( $page_id ) || is_page( $page_id ) ) ? true : false;
  526. // Force noindex on calendar archive pages as long as they are not the home, front page, or individual event view.
  527. if ( ( $page_id && $is_page ) && ! ( isset( $_GET['mc_id'] ) || is_front_page() ) ) {
  528. echo PHP_EOL . '<meta name="robots" content="noindex,follow" />' . PHP_EOL;
  529. }
  530. }
  531. /**
  532. * Filters the Yoast SEO Schema output, adding in graph blocks for Events and Location.
  533. *
  534. * @param array $graph The schema graph.
  535. * @param Meta_Tags_Context $context Context value object.
  536. *
  537. * @return array
  538. */
  539. function mc_add_yoast_schema( $graph, $context ) {
  540. if ( mc_is_single_event() ) {
  541. $mc_id = ( isset( $_GET['mc_id'] ) ) ? absint( $_GET['mc_id'] ) : false;
  542. if ( $mc_id ) {
  543. $event = mc_get_event( $mc_id );
  544. $schema = mc_event_schema( $event );
  545. unset( $schema['@context'] );
  546. $schema['mainEntityOfPage'] = array( '@id' => $context->canonical );
  547. if ( isset( $schema['location'] ) ) {
  548. unset( $schema['location']['@context'] );
  549. }
  550. $graph[] = $schema;
  551. }
  552. }
  553. if ( is_singular( 'mc-locations' ) ) {
  554. $loc_id = mc_get_location_id( get_the_ID() );
  555. $location = mc_get_location( $loc_id );
  556. $schema = mc_location_schema( $location );
  557. unset( $schema['@context'] );
  558. $schema['mainEntityOfPage'] = array( '@id' => $context->canonical );
  559. $graph[] = $schema;
  560. }
  561. return $graph;
  562. }
  563. /**
  564. * Generate category styles for use by My Calendar core.
  565. *
  566. * @return array Variable styles & category styles.
  567. */
  568. function mc_generate_category_styles() {
  569. $apply = mc_get_option( 'apply_color' );
  570. if ( 'default' === $apply ) {
  571. return array();
  572. }
  573. $styles = get_transient( 'mc_generated_category_styles' );
  574. if ( ! $styles ) {
  575. $mcdb = mc_is_remote_db();
  576. $category_styles = '';
  577. $category_vars = '';
  578. $inv = '';
  579. $type = '';
  580. $alt = '';
  581. $categories = $mcdb->get_results( 'SELECT * FROM ' . my_calendar_categories_table( get_current_blog_id() ) . ' ORDER BY category_id ASC' );
  582. foreach ( $categories as $category ) {
  583. $class = mc_category_class( $category, 'mc_' );
  584. $hex = ( strpos( $category->category_color, '#' ) !== 0 ) ? '#' : '';
  585. $color = $hex . $category->category_color;
  586. if ( '#' !== $color ) {
  587. $hcolor = mc_shift_color( $category->category_color );
  588. if ( 'font' === $apply ) {
  589. $type = 'color';
  590. $alt = 'background';
  591. } elseif ( 'background' === $apply ) {
  592. $type = 'background';
  593. $alt = 'color';
  594. }
  595. $inverse = mc_inverse_color( $color );
  596. $inv = "$alt: $inverse !important;";
  597. if ( 'font' === $apply || 'background' === $apply ) {
  598. if ( 'background' === $apply ) {
  599. $category_styles .= "\n.my-calendar-modal .event-title svg { background-color: $color; padding: 3px; }";
  600. }
  601. // always an anchor as of 1.11.0, apply also to title.
  602. $category_styles .= "\n.mc-main .$class .event-title, .mc-main .$class .event-title a { $type: $color !important; $inv }";
  603. $category_styles .= "\n.mc-main .$class .event-title button { $type: $color !important; $inv }";
  604. $category_styles .= "\n.mc-main .$class .event-title a:hover, .mc-main .$class .event-title a:focus { $type: $hcolor !important;}";
  605. $category_styles .= "\n.mc-main .$class .event-title button:hover, .mc-main .$class .event-title button:focus { $type: $hcolor !important;}";
  606. }
  607. // Variables aren't dependent on options.
  608. $category_vars .= '--category-' . $class . ': ' . $color . '; ';
  609. }
  610. }
  611. $styles = array(
  612. 'styles' => $category_styles,
  613. 'vars' => $category_vars,
  614. );
  615. set_transient( 'mc_generated_category_styles', $styles, WEEK_IN_SECONDS );
  616. }
  617. return $styles;
  618. }
  619. /**
  620. * Deal with events posted by a user when that user is deleted
  621. *
  622. * @param int $id user ID of deleted user.
  623. * @param int $reassign User ID chosen for reassignment of content.
  624. */
  625. function mc_deal_with_deleted_user( $id, $reassign ) {
  626. global $wpdb;
  627. $new = ( ! $reassign ) ? $wpdb->get_var( 'SELECT MIN(ID) FROM ' . $wpdb->users, 0, 0 ) : $reassign;
  628. // This may not work quite right in multi-site. Need to explore further when I have time.
  629. $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_author=%d WHERE event_author=%d', $reassign, $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  630. $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_host=%d WHERE event_host=%d', $reassign, $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  631. }
  632. /**
  633. * Write custom JS in admin head.
  634. */
  635. function mc_write_js() {
  636. $is_calendar = ( isset( $_GET['page'] ) && 'my-calendar' === $_GET['page'] ) ? true : false;
  637. $is_edit = ( isset( $_GET['mode'] ) && 'edit' === $_GET['mode'] ) ? true : false;
  638. if ( function_exists( 'wpt_post_to_service' ) && $is_calendar && ! $is_edit ) {
  639. ?>
  640. <script>
  641. //<![CDATA[
  642. jQuery(document).ready(function ($) {
  643. let mc_allowed = $( '#mc_twitter' ).attr( 'data-allowed' );
  644. $('#mc_twitter').charCount({
  645. allowed: mc_allowed,
  646. counterText: '<?php esc_html_e( 'Characters left: ', 'my-calendar' ); ?>'
  647. });
  648. });
  649. //]]>
  650. </script>
  651. <?php
  652. }
  653. }
  654. add_action( 'in_plugin_update_message-my-calendar/my-calendar.php', 'mc_plugin_update_message' );
  655. /**
  656. * Display notices from WordPress.org about updated versions.
  657. */
  658. function mc_plugin_update_message() {
  659. define( 'MC_PLUGIN_README_URL', 'http://svn.wp-plugins.org/my-calendar/trunk/readme.txt' );
  660. $response = wp_remote_get(
  661. MC_PLUGIN_README_URL,
  662. array(
  663. 'user-agent' => 'WordPress/My Calendar' . mc_get_version() . '; ' . get_bloginfo( 'url' ),
  664. )
  665. );
  666. if ( ! is_wp_error( $response ) || is_array( $response ) ) {
  667. $data = $response['body'];
  668. $bits = explode( '== Upgrade Notice ==', $data );
  669. echo '</div><div id="mc-upgrade" class="notice inline notice-warning"><ul><li><strong style="color:#c22;">Upgrade Notes:</strong> ' . esc_html( str_replace( '* ', '', nl2br( trim( $bits[1] ) ) ) ) . '</li></ul>';
  670. }
  671. }
  672. /**
  673. * Scripts for My Calendar footer; ideally only on pages where My Calendar exists
  674. */
  675. function mc_footer_js() {
  676. global $wp_query;
  677. $version = mc_get_version();
  678. if ( SCRIPT_DEBUG ) {
  679. $version = $version . '-' . wp_rand( 10000, 100000 );
  680. }
  681. /**
  682. * Disable scripting on mobile devices.
  683. *
  684. * @hook mc_disable_mobile_js
  685. *
  686. * @param {bool} $disable Return true to disable JS on detected mobile devices.
  687. *
  688. * @return {bool}
  689. */
  690. if ( mc_is_mobile() && apply_filters( 'mc_disable_mobile_js', false ) ) {
  691. return;
  692. } else {
  693. $pages = array();
  694. $show_js = mc_get_option( 'show_js' );
  695. if ( '' !== $show_js ) {
  696. $pages = explode( ',', $show_js );
  697. }
  698. if ( is_object( $wp_query ) && isset( $wp_query->post ) ) {
  699. $id = (string) $wp_query->post->ID;
  700. } else {
  701. $id = false;
  702. }
  703. if ( ! $id || ( is_array( $pages ) && in_array( $id, $pages, true ) ) || '' === $show_js ) {
  704. mc_enqueue_calendar_js();
  705. }
  706. }
  707. }
  708. /**
  709. * Register scripts and styles.
  710. */
  711. function mc_register_scripts() {
  712. $version = mc_get_version();
  713. if ( SCRIPT_DEBUG ) {
  714. $version .= wp_rand( 10000, 100000 );
  715. }
  716. wp_register_style( 'my-calendar-lists', plugins_url( 'css/list-presets.css', __FILE__ ), array(), $version );
  717. wp_register_style( 'my-calendar-reset', plugins_url( 'css/reset.css', __FILE__ ), array( 'dashicons', 'my-calendar-lists' ), $version );
  718. wp_register_style( 'my-calendar-admin-style', plugins_url( 'css/admin.css', __FILE__ ), array( 'my-calendar-reset' ), $version );
  719. $mcjs = ( true === SCRIPT_DEBUG ) ? plugins_url( 'js/mcjs.js', __FILE__ ) : plugins_url( 'js/mcjs.min.js', __FILE__ );
  720. wp_register_script( 'mc.mcjs', $mcjs, array( 'jquery', 'wp-a11y', 'wp-i18n' ), $version, true );
  721. $modal = ( SCRIPT_DEBUG ) ? 'modal/accessible-modal-window-aria.js' : 'modal/accessible-modal-window-aria.min.js';
  722. wp_register_script( 'mc-modal', plugins_url( 'js/' . $modal, __FILE__ ), array(), $version, true );
  723. wp_register_style( 'my-calendar-lists', plugins_url( 'css/list-presets.css', __FILE__ ), array(), $version );
  724. wp_register_style( 'my-calendar-reset', plugins_url( 'css/reset.css', __FILE__ ), array( 'dashicons', 'my-calendar-lists' ), $version );
  725. $stylesheet = apply_filters( 'mc_registered_stylesheet', mc_get_style_path( mc_get_option( 'css_file' ), 'url' ) );
  726. wp_register_style( 'my-calendar-style', $stylesheet, array( 'my-calendar-reset' ), $version . '-' . sanitize_title( mc_get_option( 'css_file' ) ) );
  727. $admin_stylesheet = plugins_url( 'css/mc-admin.css', __FILE__ );
  728. wp_register_style( 'my-calendar-frontend-admin-style', $admin_stylesheet, array(), $version );
  729. wp_register_style( 'mc-styles', plugins_url( 'css/mc-styles.css', __FILE__ ), array( 'my-calendar-lists' ), $version );
  730. wp_register_style( 'mc-user-styles', plugins_url( 'css/mc-user-styles.css', __FILE__ ), array(), $version );
  731. wp_register_script( 'duet.js', plugins_url( 'js/duet/duet.js', __FILE__ ), array(), $version, true );
  732. wp_register_style( 'duet.css', plugins_url( 'js/duet/themes/default.css', __FILE__ ), array(), $version );
  733. // Enqueue datepicker options.
  734. $mcdp = ( SCRIPT_DEBUG ) ? plugins_url( 'js/mc-datepicker.js', __FILE__ ) : plugins_url( 'js/mc-datepicker.min.js', __FILE__ );
  735. wp_register_script( 'mc.duet', $mcdp, array( 'duet.js' ), $version, true );
  736. $charcount = ( SCRIPT_DEBUG ) ? plugins_url( 'js/jquery.charcount.js', __FILE__ ) : plugins_url( 'js/jquery.charcount.min.js', __FILE__ );
  737. wp_register_script( 'mc.charcount', $charcount, array( 'jquery' ), $version );
  738. $adminjs = ( SCRIPT_DEBUG ) ? plugins_url( 'js/jquery.admin.js', __FILE__ ) : plugins_url( 'js/jquery.admin.min.js', __FILE__ );
  739. wp_register_script( 'mc.admin', $adminjs, array( 'jquery', 'jquery-ui-sortable', 'wp-a11y' ), $version, true );
  740. wp_register_script( 'mc.admin-footer', plugins_url( 'js/admin.js', __FILE__ ), array( 'wp-a11y', 'clipboard' ), $version, true );
  741. if ( version_compare( $version, '2.1', '<' ) ) {
  742. wp_register_style( 'mcs-back-compat', plugins_url( 'css/backcompat.css', __FILE__ ), array(), $version );
  743. }
  744. wp_register_script( 'mc-stickyscroll', plugins_url( 'js/jquery.stick.js', __FILE__ ), array( 'jquery' ), $version );
  745. wp_register_script( 'mc-color-picker', plugins_url( 'js/color-picker.js', __FILE__ ), array( 'wp-color-picker', 'mc-stickyscroll' ), $version, true );
  746. $api_key = mc_get_option( 'gmap_api_key' );
  747. if ( $api_key ) {
  748. $gmaps = ( SCRIPT_DEBUG ) ? plugins_url( 'js/gmaps.js', __FILE__ ) : plugins_url( 'js/gmaps.min.js', __FILE__ );
  749. wp_register_script( 'gmaps', "https://maps.googleapis.com/maps/api/js?v=3&key=$api_key", array() );
  750. wp_register_script( 'mc-maps', $gmaps, array( 'gmaps' ), $version, true );
  751. }
  752. $ajax = ( SCRIPT_DEBUG ) ? plugins_url( 'js/ajax.js', __FILE__ ) : plugins_url( 'js/ajax.min.js', __FILE__ );
  753. wp_register_script( 'mc.ajax', $ajax, array( 'jquery' ), $version );
  754. if ( SCRIPT_DEBUG ) {
  755. wp_register_script( 'accessible-autocomplete', plugins_url( '/js/accessible-autocomplete.js', __FILE__ ), array(), $version );
  756. } else {
  757. wp_register_script( 'accessible-autocomplete', plugins_url( '/js/accessible-autocomplete.min.js', __FILE__ ), array(), $version );
  758. }
  759. wp_register_script( 'mc-autocomplete', plugins_url( '/js/autocomplete.js', __FILE__ ), array( 'jquery', 'accessible-autocomplete' ), $version, true );
  760. }
  761. add_action( 'wp_enqueue_scripts', 'mc_register_scripts' );
  762. add_action( 'admin_enqueue_scripts', 'mc_register_scripts' );
  763. /**
  764. * Add stylesheets to My Calendar admin screens
  765. */
  766. function mc_admin_styles() {
  767. global $current_screen;
  768. $version = mc_get_version();
  769. if ( SCRIPT_DEBUG ) {
  770. $version .= wp_rand( 10000, 100000 );
  771. }
  772. $id = $current_screen->id;
  773. $is_mc_page = isset( $_GET['post'] ) && (int) mc_get_option( 'uri_id' ) === (int) $_GET['post'];
  774. $enqueue_mcjs = false;
  775. $grid = 'false';
  776. $list = 'false';
  777. $mini = 'false';
  778. $ajax = 'false';
  779. if ( false !== strpos( $id, 'my-calendar' ) || $is_mc_page ) {
  780. // Toggle CSS & Scripts based on current mode.
  781. $mode = mc_get_option( 'default_admin_view' );
  782. if ( isset( $_GET['view'] ) && 'grid' === $_GET['view'] && 'grid' !== $mode ) {
  783. mc_update_option( 'default_admin_view', 'grid' );
  784. $mode = 'grid';
  785. }
  786. if ( isset( $_GET['view'] ) && 'list' === $_GET['view'] && 'list' !== $mode ) {
  787. mc_update_option( 'default_admin_view', 'list' );
  788. $mode = 'list';
  789. }
  790. $grid = ( 'grid' === $mode );
  791. if ( $grid ) {
  792. mc_enqueue_calendar_styles( '' );
  793. wp_enqueue_style( 'my-calendar-admin-style' );
  794. wp_enqueue_style( 'my-calendar-frontend-admin-style' );
  795. if ( '1' !== mc_get_option( 'calendar_javascript' ) ) {
  796. $enqueue_mcjs = true;
  797. $grid = ( 'modal' === mc_get_option( 'calendar_javascript' ) ) ? 'modal' : 'true';
  798. }
  799. if ( '1' !== mc_get_option( 'list_javascript' ) ) {
  800. $enqueue_mcjs = true;
  801. $list = ( 'modal' === mc_get_option( 'list_javascript' ) ) ? 'modal' : 'true';
  802. }
  803. if ( '1' !== mc_get_option( 'mini_javascript' ) && 'true' !== mc_get_option( 'open_day_uri' ) ) {
  804. $enqueue_mcjs = true;
  805. $mini = ( 'modal' === mc_get_option( 'mini_javascript' ) ) ? 'modal' : 'true';
  806. }
  807. if ( '1' !== mc_get_option( 'ajax_javascript' ) ) {
  808. $enqueue_mcjs = true;
  809. $ajax = 'true';
  810. }
  811. if ( $enqueue_mcjs ) {
  812. wp_enqueue_script( 'mc.mcjs' );
  813. $args = array(
  814. 'grid' => $grid,
  815. 'list' => $list,
  816. 'mini' => $mini,
  817. 'ajax' => $ajax,
  818. 'newWindow' => __( 'New tab', 'my-calendar' ),
  819. );
  820. wp_localize_script( 'mc.mcjs', 'my_calendar', $args );
  821. }
  822. $gridtype = mc_get_option( 'calendar_javascript' );
  823. if ( 'modal' === $gridtype ) {
  824. wp_enqueue_script( 'mc-modal' );
  825. wp_localize_script(
  826. 'mc-modal',
  827. 'mcm',
  828. array(
  829. 'context' => (string) is_user_logged_in(),
  830. )
  831. );
  832. }
  833. }
  834. if ( 'my-calendar_page_my-calendar-design' === $id ) {
  835. /**
  836. * Filter url to get My Calendar stylesheet.
  837. *
  838. * @hook mc_registered_stylesheet
  839. *
  840. * @param {string} $stylesheet URL to locate My Calendar's stylesheet.
  841. *
  842. * @return {string}
  843. */
  844. $stylesheet = apply_filters( 'mc_registered_stylesheet', mc_get_style_path( mc_get_option( 'css_file' ), 'url' ) );
  845. mc_enqueue_calendar_styles( $stylesheet );
  846. wp_enqueue_style( 'my-calendar-frontend-admin-style' );
  847. mc_enqueue_calendar_js();
  848. }
  849. wp_enqueue_style( 'mc-styles' );
  850. }
  851. if ( 'profile' === $id || 'user-edit' === $id || 'widgets' === $id || 'customize' === $id ) {
  852. wp_enqueue_style( 'mc-user-styles' );
  853. }
  854. }
  855. /**
  856. * Toggle admin URL values based on default admin view setting.
  857. *
  858. * @param string $url Admin URL location.
  859. *
  860. * @return string
  861. */
  862. function mc_admin_url( $url ) {
  863. $mode = mc_get_option( 'default_admin_view' );
  864. if ( 'grid' === $mode ) {
  865. $url = add_query_arg( 'view', 'grid', $url );
  866. } else {
  867. $url = add_query_arg( 'view', 'list', $url );
  868. }
  869. return admin_url( $url );
  870. }
  871. /**
  872. * Add custom CSS variables in admin head.
  873. */
  874. function mc_admin_head() {
  875. $category_vars = '';
  876. $category_styles = '';
  877. // generate category colors.
  878. $category_css = mc_generate_category_styles();
  879. if ( ! empty( $category_css ) && is_array( $category_css ) ) {
  880. $category_styles = ( isset( $category_css['styles'] ) ) ? $category_css['styles'] : '';
  881. $category_vars = ( isset( $category_css['vars'] ) ) ? $category_css['vars'] : '';
  882. }
  883. $styles = (array) mc_get_option( 'style_vars' );
  884. $style_vars = '';
  885. foreach ( $styles as $key => $var ) {
  886. if ( ( 'text' === $key || 'sizing' === $key || 'list-presets' === $key ) && is_array( $var ) ) {
  887. foreach ( $var as $variable => $text ) {
  888. if ( $variable ) {
  889. $style_vars .= sanitize_key( $variable ) . ': ' . esc_html( $text ) . '; ';
  890. }
  891. }
  892. } else {
  893. if ( $var ) {
  894. $style_vars .= sanitize_key( $key ) . ': ' . esc_html( $var ) . '; ';
  895. }
  896. }
  897. }
  898. if ( '' !== $style_vars ) {
  899. $style_vars = '.mc-main {' . $style_vars . $category_vars . '}';
  900. }
  901. ?>
  902. <style>
  903. /* Styles by My Calendar - Joseph C Dolson https://www.joedolson.com/ */
  904. <?php
  905. echo esc_html( wp_strip_all_tags( $category_styles ) );
  906. echo esc_html( wp_strip_all_tags( $style_vars ) );
  907. ?>
  908. </style>
  909. <?php
  910. }
  911. add_action( 'admin_head', 'mc_admin_head' );
  912. /**
  913. * Get current admin URL.
  914. *
  915. * @return string
  916. */
  917. function mc_get_current_admin_url() {
  918. $uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
  919. $uri = preg_replace( '|^.*/wp-admin/|i', '', $uri );
  920. if ( ! $uri ) {
  921. return '';
  922. }
  923. return remove_query_arg( array( '_wpnonce' ), admin_url( $uri ) );
  924. }
  925. /**
  926. * Attempts to correctly identify the current URL.
  927. */
  928. function mc_get_current_url() {
  929. if ( is_admin() ) {
  930. return mc_get_current_admin_url();
  931. }
  932. global $wp, $wp_rewrite;
  933. $args = array();
  934. if ( isset( $_GET['page_id'] ) ) {
  935. $args['page_id'] = absint( $_GET['page_id'] );
  936. }
  937. $current_url = home_url( add_query_arg( $args, $wp->request ) );
  938. if ( $wp_rewrite->using_index_permalinks() && false === strpos( $current_url, 'index.php' ) ) {
  939. $current_url = str_replace( home_url(), home_url( '/' ) . 'index.php', $current_url );
  940. }
  941. if ( $wp_rewrite->using_permalinks() ) {
  942. $current_url = trailingslashit( $current_url );
  943. }
  944. $args = map_deep( $_GET, 'sanitize_text_field' );
  945. $current_url = add_query_arg( $args, $current_url );
  946. /**
  947. * Filter the URL returned for the current URL.
  948. *
  949. * @hook mc_get_current_url
  950. *
  951. * @param {string} $current_url Current URL according to wp_rewrite.
  952. *
  953. * @return {string}
  954. */
  955. $current_url = apply_filters( 'mc_get_current_url', $current_url );
  956. return $current_url;
  957. }
  958. /**
  959. * Check whether the current user should have permissions and doesn't
  960. */
  961. function mc_if_needs_permissions() {
  962. $role = get_role( 'administrator' );
  963. if ( is_object( $role ) ) {
  964. $caps = $role->capabilities;
  965. if ( isset( $caps['mc_add_events'] ) ) {
  966. return;
  967. } else {
  968. $role->add_cap( 'mc_add_events' );
  969. $role->add_cap( 'mc_approve_events' );
  970. $role->add_cap( 'mc_manage_events' );
  971. $role->add_cap( 'mc_edit_cats' );
  972. $role->add_cap( 'mc_edit_styles' );
  973. $role->add_cap( 'mc_edit_behaviors' );
  974. $role->add_cap( 'mc_edit_templates' );
  975. $role->add_cap( 'mc_edit_settings' );
  976. $role->add_cap( 'mc_edit_locations' );
  977. $role->add_cap( 'mc_view_help' );
  978. }
  979. } else {
  980. return;
  981. }
  982. }
  983. /**
  984. * Grant capabilities to standard site roles
  985. *
  986. * @param string|boolean $add Add capabilities to this role.
  987. * @param string|boolean $manage Manage capabilities to this role.
  988. * @param string|boolean $approve Approve capabilities to this role.
  989. */
  990. function mc_add_roles( $add = false, $manage = false, $approve = false ) {
  991. $role = get_role( 'administrator' );
  992. $role->add_cap( 'mc_add_events' );
  993. $role->add_cap( 'mc_approve_events' );
  994. $role->add_cap( 'mc_manage_events' );
  995. $role->add_cap( 'mc_edit_cats' );
  996. $role->add_cap( 'mc_edit_styles' );
  997. $role->add_cap( 'mc_edit_behaviors' );
  998. $role->add_cap( 'mc_edit_templates' );
  999. $role->add_cap( 'mc_edit_settings' );
  1000. $role->add_cap( 'mc_edit_locations' );
  1001. $role->add_cap( 'mc_view_help' );
  1002. if ( $add && $manage && $approve ) {
  1003. // this is an upgrade.
  1004. $subscriber = get_role( 'subscriber' );
  1005. $contributor = get_role( 'contributor' );
  1006. $author = get_role( 'author' );
  1007. $editor = get_role( 'editor' );
  1008. $subscriber->add_cap( 'mc_view_help' );
  1009. $contributor->add_cap( 'mc_view_help' );
  1010. $author->add_cap( 'mc_view_help' );
  1011. $editor->add_cap( 'mc_view_help' );
  1012. switch ( $add ) {
  1013. case 'read':
  1014. $subscriber->add_cap( 'mc_add_events' );
  1015. $contributor->add_cap( 'mc_add_events' );
  1016. $author->add_cap( 'mc_add_events' );
  1017. $editor->add_cap( 'mc_add_events' );
  1018. break;
  1019. case 'edit_posts':
  1020. $contributor->add_cap( 'mc_add_events' );
  1021. $author->add_cap( 'mc_add_events' );
  1022. $editor->add_cap( 'mc_add_events' );
  1023. break;
  1024. case 'publish_posts':
  1025. $author->add_cap( 'mc_add_events' );
  1026. $editor->add_cap( 'mc_add_events' );
  1027. break;
  1028. case 'moderate_comments':
  1029. $editor->add_cap( 'mc_add_events' );
  1030. break;
  1031. }
  1032. switch ( $approve ) {
  1033. case 'read':
  1034. $subscriber->add_cap( 'mc_approve_events' );
  1035. $contributor->add_cap( 'mc_approve_events' );
  1036. $author->add_cap( 'mc_approve_events' );
  1037. $editor->add_cap( 'mc_approve_events' );
  1038. break;
  1039. case 'edit_posts':
  1040. $contributor->add_cap( 'mc_approve_events' );
  1041. $author->add_cap( 'mc_approve_events' );
  1042. $editor->add_cap( 'mc_approve_events' );
  1043. break;
  1044. case 'publish_posts':
  1045. $author->add_cap( 'mc_approve_events' );
  1046. $editor->add_cap( 'mc_approve_events' );
  1047. break;
  1048. case 'moderate_comments':
  1049. $editor->add_cap( 'mc_approve_events' );
  1050. break;
  1051. }
  1052. switch ( $manage ) {
  1053. case 'read':
  1054. $subscriber->add_cap( 'mc_manage_events' );
  1055. $contributor->add_cap( 'mc_manage_events' );
  1056. $author->add_cap( 'mc_manage_events' );
  1057. $editor->add_cap( 'mc_manage_events' );
  1058. break;
  1059. case 'edit_posts':
  1060. $contributor->add_cap( 'mc_manage_events' );
  1061. $author->add_cap( 'mc_manage_events' );
  1062. $editor->add_cap( 'mc_manage_events' );
  1063. break;
  1064. case 'publish_posts':
  1065. $author->add_cap( 'mc_manage_events' );
  1066. $editor->add_cap( 'mc_manage_events' );
  1067. break;
  1068. case 'moderate_comments':
  1069. $editor->add_cap( 'mc_manage_events' );
  1070. break;
  1071. }
  1072. }
  1073. }
  1074. /**
  1075. * Verify that My Calendar tables exist
  1076. */
  1077. function my_calendar_exists() {
  1078. global $wpdb;
  1079. $tables = $wpdb->get_results( 'show tables;' );
  1080. foreach ( $tables as $table ) {
  1081. foreach ( $table as $value ) {
  1082. if ( my_calendar_table() === $value ) {
  1083. // if the table exists, then My Calendar was already installed.
  1084. return true;
  1085. }
  1086. }
  1087. }
  1088. return false;
  1089. }
  1090. /**
  1091. * Check what version of My Calendar is installed; install or upgrade if needed
  1092. */
  1093. function my_calendar_check() {
  1094. // only execute this function for administrators.
  1095. if ( current_user_can( 'manage_options' ) ) {
  1096. mc_if_needs_permissions();
  1097. $old_version = mc_get_version( false );
  1098. // If current version matches, don't bother running this.
  1099. if ( mc_get_version() === $old_version ) {
  1100. return true;
  1101. } else {
  1102. update_option( 'mc_version', mc_get_version() );
  1103. }
  1104. $upgrade_path = array();
  1105. $my_calendar_exists = my_calendar_exists();
  1106. $settings = get_option( 'my_calendar_options' );
  1107. if ( $my_calendar_exists && '' === $old_version ) {
  1108. // If the table exists, but I don't know what version it is, run all upgrades.
  1109. $old_version = '3.1.12';
  1110. }
  1111. if ( $my_calendar_exists ) {
  1112. // For each release requiring an upgrade path, add a version compare.
  1113. // Loop will run every relevant upgrade cycle.
  1114. $valid_upgrades = array( '3.1.13', '3.3.0', '3.4.0', '3.5.0' );
  1115. foreach ( $valid_upgrades as $upgrade ) {
  1116. if ( version_compare( $old_version, $upgrade, '<' ) ) {
  1117. $upgrade_path[] = $upgrade;
  1118. }
  1119. }
  1120. }
  1121. if ( ! empty( $upgrade_path ) ) {
  1122. mc_do_upgrades( $upgrade_path );
  1123. }
  1124. // If there are no settings, set up default settings.
  1125. if ( ! $settings ) {
  1126. mc_default_settings();
  1127. }
  1128. /*
  1129. * If the user has uninstalled the plugin but kept the database of events, this will restore default
  1130. * settings and upgrade db if needed.
  1131. */
  1132. if ( 'true' === get_option( 'mc_uninstalled' ) ) {
  1133. mc_default_settings();
  1134. update_option( 'mc_db_version', mc_get_version() );
  1135. }
  1136. }
  1137. }
  1138. /**
  1139. * Given a valid upgrade path, execute it.
  1140. *
  1141. * @param array $upgrade_path Specific path to execute.
  1142. *
  1143. * @return bool
  1144. */
  1145. function mc_do_upgrades( $upgrade_path ) {
  1146. if ( empty( $upgrade_path ) ) {
  1147. return false;
  1148. }
  1149. foreach ( $upgrade_path as $upgrade ) {
  1150. switch ( $upgrade ) {
  1151. case '3.5.0': // 2024-05-05
  1152. // Need to set card display settings. TODO.
  1153. $options = get_option( 'my_calendar_options' );
  1154. $caljs = $options['calendar_javascript'];
  1155. $minijs = $options['mini_javascript'];
  1156. $listjs = $options['list_javascript'];
  1157. if ( ! $caljs ) {
  1158. $options['calendar_javascript'] = 'disclosure';
  1159. }
  1160. if ( ! $minijs ) {
  1161. $options['mini_javascript'] = 'disclosure';
  1162. }
  1163. if ( ! $listjs ) {
  1164. $options['list_javascript'] = 'disclosure';
  1165. }
  1166. update_option( 'my_calendar_options', $options );
  1167. break;
  1168. case '3.4.0': // 2023-01-08
  1169. mc_migrate_settings();
  1170. delete_option( 'mc_use_custom_js' );
  1171. break;
  1172. case '3.3.0': // 2021-12-13
  1173. // Event repeats is now a string, and prefers a date-like value.
  1174. mc_upgrade_db();
  1175. // Count cache no longer counts 'archived' events as published.
  1176. mc_update_count_cache();
  1177. // Shortcodes now executed by default.
  1178. delete_option( 'mc_process_shortcodes' );
  1179. // Remap display settings.
  1180. $settings = array();
  1181. $single = get_option( 'display_single' );
  1182. $main = get_option( 'display_main' );
  1183. $mini = get_option( 'display_mini' );
  1184. if ( empty( $single ) || empty( $main ) || empty( $mini ) ) {
  1185. $settings[] = ( 'true' === get_option( 'mc_display_author' ) ) ? 'author' : '';
  1186. $settings[] = ( 'true' === get_option( 'mc_display_host' ) ) ? 'host' : '';
  1187. $settings[] = ( 'true' === get_option( 'mc_show_event_vcal' ) ) ? 'ical' : '';
  1188. $settings[] = ( 'true' === get_option( 'mc_show_gcal' ) ) ? 'gcal' : '';
  1189. $settings[] = ( 'true' === get_option( 'mc_show_map' ) ) ? 'gmap_link' : '';
  1190. $settings[] = ( 'true' === get_option( 'mc_gmap' ) ) ? 'gmap' : '';
  1191. $settings[] = ( 'true' === get_option( 'mc_show_address' ) ) ? 'address' : '';
  1192. $settings[] = ( 'true' === get_option( 'mc_short' ) ) ? 'excerpt' : '';
  1193. $settings[] = ( 'true' === get_option( 'mc_desc' ) ) ? 'description' : '';
  1194. $settings[] = ( 'true' === get_option( 'mc_image' ) ) ? 'image' : '';
  1195. $settings[] = ( 'true' === get_option( 'mc_event_registration' ) ) ? 'tickets' : '';
  1196. $settings[] = ( 'true' === get_option( 'mc_event_link' ) ) ? 'link' : '';
  1197. $settings[] = ( 'true' === get_option( 'mc_display_more' ) ) ? 'more' : '';
  1198. foreach ( $settings as $key => $value ) {
  1199. if ( '' === $value ) {
  1200. unset( $settings[ $key ] );
  1201. }
  1202. }
  1203. if ( empty( $single ) ) {
  1204. add_option( 'mc_display_single', $settings );
  1205. }
  1206. if ( empty( $main ) ) {
  1207. add_option( 'mc_display_main', $settings );
  1208. }
  1209. if ( empty( $mini ) ) {
  1210. add_option( 'mc_display_mini', $settings );
  1211. }
  1212. }
  1213. add_option( 'mc_drop_settings', 'true' );
  1214. delete_option( 'mc_display_author' );
  1215. delete_option( 'mc_display_host' );
  1216. delete_option( 'mc_show_event_vcal' );
  1217. delete_option( 'mc_show_gcal' );
  1218. delete_option( 'mc_show_map' );
  1219. delete_option( 'mc_gmap' );
  1220. delete_option( 'mc_show_address' );
  1221. delete_option( 'mc_short' );
  1222. delete_option( 'mc_desc' );
  1223. delete_option( 'mc_image' );
  1224. delete_option( 'mc_short' );
  1225. delete_option( 'mc_event_registration' );
  1226. delete_option( 'mc_event_link' );
  1227. delete_option( 'mc_display_more' );
  1228. delete_option( 'mc_title' );
  1229. break;
  1230. case '3.1.13': // 2019-03-15
  1231. delete_option( 'mc_inverse_color' );
  1232. mc_upgrade_db();
  1233. break;
  1234. default:
  1235. break;
  1236. }
  1237. }
  1238. return true;
  1239. }
  1240. /**
  1241. * Add primary adminbar link.
  1242. *
  1243. * @param int $mc_id Post ID for calendar.
  1244. */
  1245. function mc_add_adminbar_link( $mc_id ) {
  1246. global $wp_admin_bar;
  1247. if ( is_page( $mc_id ) && current_user_can( 'mc_add_events' ) ) {
  1248. /**
  1249. * Filter URL displayed for 'Add Event' link in adminbar. Return empty value to disable.
  1250. *
  1251. * @hook mc_add_events_url
  1252. *
  1253. * @param {string} $url Admin URL for adding events.
  1254. *
  1255. * @return {string}
  1256. */
  1257. $url = apply_filters( 'mc_add_events_url', admin_url( 'admin.php?page=my-calendar' ) );
  1258. $args = array(
  1259. 'id' => 'mc-my-calendar',
  1260. 'title' => __( 'Add Event', 'my-calendar' ),
  1261. 'href' => $url,
  1262. );
  1263. } else {
  1264. /**
  1265. * Filter URL displayed for 'My Calendar' link in adminbar.
  1266. *
  1267. * @hook mc_adminbar_uri
  1268. *
  1269. * @param {string} $url Front-end URL for viewing events.
  1270. *
  1271. * @return {string}
  1272. */
  1273. $url = esc_url( apply_filters( 'mc_adminbar_uri', mc_get_uri() ) );
  1274. $args = array(
  1275. 'id' => 'mc-my-calendar',
  1276. 'title' => __( 'My Calendar', 'my-calendar' ),
  1277. 'href' => $url,
  1278. );
  1279. }
  1280. $wp_admin_bar->add_node( $args );
  1281. }
  1282. add_action( 'admin_bar_menu', 'mc_admin_bar', 200 );
  1283. /**
  1284. * Add primary adminbar link.
  1285. *
  1286. * @param array $classes An array of body class names.
  1287. */
  1288. function mc_primary_page_class( $classes ) {
  1289. $mc_id = mc_get_option( 'uri_id' );
  1290. if ( is_page( $mc_id ) ) {
  1291. $classes[] = 'my-calendar';
  1292. }
  1293. return $classes;
  1294. }
  1295. add_filter( 'body_class', 'mc_primary_page_class', 10, 1 );
  1296. /**
  1297. * Set up adminbar links
  1298. */
  1299. function mc_admin_bar() {
  1300. global $wp_admin_bar;
  1301. $mc_id = mc_get_option( 'uri_id' );
  1302. if ( mc_get_uri( 'boolean' ) ) {
  1303. mc_add_adminbar_link( $mc_id );
  1304. } else {
  1305. $mc_id = mc_get_option( 'uri_id' );
  1306. if ( ! $mc_id ) {
  1307. $url = admin_url( 'admin.php?page=my-calendar-config#my-calendar-manage' );
  1308. $args = array(
  1309. 'id' => 'mc-my-calendar',
  1310. 'title' => __( 'Set Calendar URL', 'my-calendar' ),
  1311. 'href' => $url,
  1312. );
  1313. $wp_admin_bar->add_node( $args );
  1314. } else {
  1315. mc_add_adminbar_link( $mc_id );
  1316. }
  1317. }
  1318. if ( current_user_can( 'mc_add_events' ) && 'true' !== mc_get_option( 'remote' ) ) {
  1319. /**
  1320. * Filter URL displayed for 'Add Event' link in adminbar. Return empty value to disable.
  1321. *
  1322. * @hook mc_add_events_url
  1323. *
  1324. * @param {string} $url Admin URL for adding events.
  1325. *
  1326. * @return {string}
  1327. */
  1328. $url = apply_filters( 'mc_add_events_url', admin_url( 'admin.php?page=my-calendar' ) );
  1329. if ( $url ) {
  1330. $args = array(
  1331. 'id' => 'mc-add-event',
  1332. 'title' => __( 'Add Event', 'my-calendar' ),
  1333. 'href' => $url,
  1334. 'parent' => 'mc-my-calendar',
  1335. );
  1336. $wp_admin_bar->add_node( $args );
  1337. }
  1338. }
  1339. $mc_id = ( isset( $_GET['mc_id'] ) ) ? absint( $_GET['mc_id'] ) : false;
  1340. if ( $mc_id && mc_can_edit_event( mc_get_event( $mc_id ) ) ) {
  1341. $event_id = mc_valid_id( $mc_id );
  1342. $query = array(
  1343. 'event_id' => $event_id,
  1344. 'ref' => urlencode( mc_get_current_url() ),
  1345. );
  1346. $url = add_query_arg( $query, admin_url( 'admin.php?page=my-calendar&mode=edit' ) );
  1347. $args = array(
  1348. 'id' => 'mc-edit-event',
  1349. 'title' => __( 'Edit Event', 'my-calendar' ),
  1350. 'href' => $url,
  1351. 'parent' => 'mc-my-calendar',
  1352. );
  1353. $wp_admin_bar->add_node( $args );
  1354. }
  1355. if ( current_user_can( 'mc_manage_events' ) && current_user_can( 'mc_add_events' ) ) {
  1356. $url = admin_url( 'admin.php?page=my-calendar-manage' );
  1357. $args = array(
  1358. 'id' => 'mc-manage-events',
  1359. 'title' => __( 'Events', 'my-calendar' ),
  1360. 'href' => $url,
  1361. 'parent' => 'mc-my-calendar',
  1362. );
  1363. $wp_admin_bar->add_node( $args );
  1364. }
  1365. if ( current_user_can( 'mc_edit_cats' ) && current_user_can( 'mc_add_events' ) ) {
  1366. $url = admin_url( 'admin.php?page=my-calendar-categories' );
  1367. $args = array(
  1368. 'id' => 'mc-manage-categories',
  1369. 'title' => __( 'Categories', 'my-calendar' ),
  1370. 'href' => $url,
  1371. 'parent' => 'mc-my-calendar',
  1372. );
  1373. $wp_admin_bar->add_node( $args );
  1374. }
  1375. if ( current_user_can( 'mc_edit_locations' ) && current_user_can( 'mc_add_events' ) ) {
  1376. $url = admin_url( 'admin.php?page=my-calendar-location-manager' );
  1377. $args = array(
  1378. 'id' => 'mc-manage-locations',
  1379. 'title' => __( 'Locations', 'my-calendar' ),
  1380. 'href' => $url,
  1381. 'parent' => 'mc-my-calendar',
  1382. );
  1383. $wp_admin_bar->add_node( $args );
  1384. }
  1385. if ( function_exists( 'mcs_submissions' ) && is_numeric( get_option( 'mcs_submit_id' ) ) && mcs_user_can_submit_events() ) {
  1386. $url = get_permalink( get_option( 'mcs_submit_id' ) );
  1387. $args = array(
  1388. 'id' => 'mc-submit-events',
  1389. 'title' => __( 'Public Submissions', 'my-calendar' ),
  1390. 'href' => $url,
  1391. 'parent' => 'mc-my-calendar',
  1392. );
  1393. $wp_admin_bar->add_node( $args );
  1394. }
  1395. }
  1396. /**
  1397. * Label My Calendar pages in the admin.
  1398. *
  1399. * @param array $states States for post.
  1400. * @param object $post The post object.
  1401. *
  1402. * @return array
  1403. */
  1404. function mc_admin_state( $states, $post ) {
  1405. if ( is_admin() ) {
  1406. if ( absint( mc_get_option( 'uri_id' ) ) === $post->ID ) {
  1407. $states[] = __( 'My Calendar Page', 'my-calendar' );
  1408. }
  1409. }
  1410. return $states;
  1411. }
  1412. add_filter( 'display_post_states', 'mc_admin_state', 10, 2 );
  1413. /**
  1414. * Send email notification about an event.
  1415. *
  1416. * @param object $event Event object.
  1417. */
  1418. function my_calendar_send_email( $event ) {
  1419. $details = mc_create_tags( $event );
  1420. $details['edit_link'] = admin_url( "admin.php?page=my-calendar&amp;mode=edit&amp;event_id=$event->event_id" );
  1421. $headers = array();
  1422. $send_email_option = ( 'true' === mc_get_option( 'event_mail' ) ) ? true : false;
  1423. /**
  1424. * Filter whether email notifications should be sent.
  1425. *
  1426. * @hook mc_send_notification
  1427. *
  1428. * @param {bool} $send_email Boolean equivalent of value of event email setting.
  1429. * @param {array} $details Event details for notifications.
  1430. *
  1431. * @return {bool}
  1432. */
  1433. $send_email = apply_filters( 'mc_send_notification', $send_email_option, $details );
  1434. if ( true === $send_email ) {
  1435. add_filter( 'wp_mail_content_type', 'mc_html_type' );
  1436. }
  1437. if ( 'true' === mc_get_option( 'event_mail' ) ) {
  1438. /**
  1439. * Filter event notification email to header.
  1440. *
  1441. * @hook mc_event_mail_to
  1442. *
  1443. * @param {string} $to Email to field string.
  1444. * @param {array} $details Array of details passed to email function.
  1445. *
  1446. * @return {string}
  1447. */
  1448. $to = apply_filters( 'mc_event_mail_to', mc_get_option( 'event_mail_to' ), $details );
  1449. $from = ( '' === mc_get_option( 'event_mail_from' ) ) ? get_bloginfo( 'admin_email' ) : mc_get_option( 'event_mail_from' );
  1450. /**
  1451. * Filter event notification email from header.
  1452. *
  1453. * @hook mc_event_mail_from
  1454. *
  1455. * @param {string} $from Email string for email header from value.
  1456. * @param {array} $details Array of details passed to email function.
  1457. *
  1458. * @return {string}
  1459. */
  1460. $from = apply_filters( 'mc_event_mail_from', $from, $details );
  1461. $headers[] = 'From: ' . __( 'Event Notifications', 'my-calendar' ) . " <$from>";
  1462. /**
  1463. * Filter event notification email bcc headers.
  1464. *
  1465. * @hook mc_event_mail_bcc
  1466. *
  1467. * @param {string} $bcc Comma separated list of emails for BCC.
  1468. * @param {array} $details Array of details passed to email function.
  1469. *
  1470. * @return {string}
  1471. */
  1472. $bcc = apply_filters( 'mc_event_mail_bcc', mc_get_option( 'event_mail_bcc' ), $details );
  1473. if ( $bcc ) {
  1474. $bcc = explode( PHP_EOL, $bcc );
  1475. foreach ( $bcc as $b ) {
  1476. $b = trim( $b );
  1477. if ( is_email( $b ) ) {
  1478. $headers[] = "Bcc: $b";
  1479. }
  1480. }
  1481. }
  1482. /**
  1483. * Filter event notification email headers.
  1484. *
  1485. * @hook mc_customize_email_headers
  1486. *
  1487. * @param {array} $headers Email headers.
  1488. * @param {object} $event Event object.
  1489. *
  1490. * @return {string}
  1491. */
  1492. $headers = apply_filters( 'mc_customize_email_headers', $headers, $event );
  1493. /**
  1494. * Filter event notification email subject.
  1495. *
  1496. * @hook mc_event_mail_subject
  1497. *
  1498. * @param {string} $subject Email subject.
  1499. * @param {array} $details Array of details passed to email function.
  1500. *
  1501. * @return {string}
  1502. */
  1503. $subject = apply_filters( 'mc_event_mail_subject', mc_get_option( 'event_mail_subject' ), $details );
  1504. /**
  1505. * Filter event notification email body.
  1506. *
  1507. * @hook mc_event_mail_body
  1508. *
  1509. * @param {string} $body Email body.
  1510. * @param {array} $details Array of details passed to email function.
  1511. *
  1512. * @return {string}
  1513. */
  1514. $body = apply_filters( 'mc_event_mail_body', mc_get_option( 'event_mail_message' ), $details );
  1515. $subject = mc_draw_template( $details, $subject );
  1516. $message = mc_draw_template( $details, $body );
  1517. wp_mail( $to, $subject, $message, $headers );
  1518. }
  1519. if ( 'true' === mc_get_option( 'html_email' ) ) {
  1520. remove_filter( 'wp_mail_content_type', 'mc_html_type' );
  1521. }
  1522. }
  1523. /**
  1524. * Checks submitted events against akismet, if available
  1525. *
  1526. * @param string $event_url Provided URL.
  1527. * @param string $description Event description.
  1528. * @param array $post Posted details.
  1529. *
  1530. * @return int 1 if spam, 0 if not.
  1531. */
  1532. function mc_spam( $event_url = '', $description = '', $post = array() ) {
  1533. global $akismet_api_host, $akismet_api_port;
  1534. /**
  1535. * Disable automatic spam checking (turned on when Akismet is active.)
  1536. *
  1537. * @hook mc_disable_spam_checking
  1538. *
  1539. * @param {bool} $disabled True to disable spam checking. Default false.
  1540. * @param {array} $post Posted event details for checking.
  1541. *
  1542. * @return {bool}
  1543. */
  1544. if ( current_user_can( 'mc_add_events' ) || apply_filters( 'mc_disable_spam_checking', false, $post ) ) { // is a privileged user.
  1545. /**
  1546. * Test spam status before Akismet runs. Returns immediately and shortcircuits tests.
  1547. *
  1548. * @hook mc_custom_spam_status
  1549. *
  1550. * @param {int} $status Numeric status. 0 for valid, 1 for spam.
  1551. * @param {array} $post Submitted data from POST.
  1552. *
  1553. * @return {int}
  1554. */
  1555. return apply_filters( 'mc_custom_spam_status', 0, $post );
  1556. }
  1557. $akismet = false;
  1558. $c = array();
  1559. // check for Akismet.
  1560. if ( ( function_exists( 'akismet_http_post' ) || method_exists( 'Akismet', 'http_post' ) ) && ( akismet_get_key() ) ) {
  1561. $akismet = true;
  1562. }
  1563. if ( $akismet ) {
  1564. $c['blog'] = home_url();
  1565. $c['user_ip'] = preg_replace( '/[^0-9., ]/', '', $_SERVER['REMOTE_ADDR'] );
  1566. $c['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
  1567. $c['referrer'] = $_SERVER['HTTP_REFERER'];
  1568. $c['comment_type'] = 'calendar-event';
  1569. $c['blog_lang'] = get_bloginfo( 'language' );
  1570. $c['blog_charset'] = get_bloginfo( 'charset' );
  1571. $c['comment_author_url'] = $event_url;
  1572. $c['comment_content'] = $description;
  1573. $c['comment_author'] = $post['mcs_name'];
  1574. $c['comment_author_email'] = $post['mcs_email'];
  1575. $ignore = array( 'HTTP_COOKIE' );
  1576. foreach ( $_SERVER as $key => $value ) {
  1577. if ( ! in_array( $key, (array) $ignore, true ) ) {
  1578. $c[ "$key" ] = $value;
  1579. }
  1580. }
  1581. $query_string = '';
  1582. foreach ( $c as $key => $data ) {
  1583. $query_string .= $key . '=' . urlencode( stripslashes( (string) $data ) ) . '&';
  1584. }
  1585. if ( method_exists( 'Akismet', 'http_post' ) ) {
  1586. $response = Akismet::http_post( $query_string, 'comment-check' );
  1587. } else {
  1588. $response = akismet_http_post( $query_string, $akismet_api_host, '/1.1/comment-check', $akismet_api_port );
  1589. }
  1590. if ( 'true' === $response[1] ) {
  1591. return 1;
  1592. } else {
  1593. return 0;
  1594. }
  1595. }
  1596. return 0;
  1597. }
  1598. /**
  1599. * Cache total number of events for admin.
  1600. */
  1601. function mc_update_count_cache() {
  1602. global $wpdb;
  1603. $all = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_flagged = 0 AND event_status = 1' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1604. $published = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_approved = 1 AND event_flagged = 0 AND event_status = 1' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1605. $cancelled = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_approved = 3 AND event_flagged = 0 AND event_status = 1' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1606. $private = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_approved = 4 AND event_flagged = 0 AND event_status = 1' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1607. $draft = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_approved = 0 AND event_flagged = 0 AND event_status = 1' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1608. $trash = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_approved = 2 AND event_flagged = 0 AND event_status = 1' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1609. $archive = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_approved != 2 AND event_flagged = 0 AND event_status = 0' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1610. $spam = $wpdb->get_var( 'SELECT count(event_id) FROM ' . my_calendar_table() . ' WHERE event_approved != 2 AND event_flagged = 1 AND event_status = 1' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1611. $counts = array(
  1612. 'all' => $all,
  1613. 'published' => $published,
  1614. 'draft' => $draft,
  1615. 'trash' => $trash,
  1616. 'archive' => $archive,
  1617. 'cancel' => $cancelled,
  1618. 'private' => $private,
  1619. 'spam' => $spam,
  1620. );
  1621. update_option( 'mc_count_cache', $counts );
  1622. return $counts;
  1623. }
  1624. add_action( 'admin_enqueue_scripts', 'mc_datepicker' );
  1625. /**
  1626. * Enqueue datepickers.
  1627. */
  1628. function mc_datepicker() {
  1629. global $current_screen;
  1630. $id = $current_screen->id;
  1631. if ( 'toplevel_page_my-calendar' === $id ) {
  1632. mc_enqueue_duet();
  1633. }
  1634. }
  1635. /**
  1636. * Produce placeholders in a meaningful format.
  1637. *
  1638. * @return string
  1639. */
  1640. function mc_parse_date_format() {
  1641. $format = get_option( 'mcs_date_format', 'Y-m-d' );
  1642. switch ( $format ) {
  1643. case 'Y-m-d':
  1644. $parsed = 'YYYY-MM-DD';
  1645. break;
  1646. case 'm/d/Y':
  1647. $parsed = 'MM/DD/YYYY';
  1648. break;
  1649. case 'd-m-Y':
  1650. $parsed = 'DD-MM-YYYY';
  1651. break;
  1652. case 'j F Y':
  1653. $parsed = 'DD MMMM YYYY';
  1654. break;
  1655. case 'M j, Y':
  1656. $parsed = 'MMM DD, YYYY';
  1657. break;
  1658. default:
  1659. $parsed = 'YYYY-MM-DD';
  1660. }
  1661. return $parsed;
  1662. }
  1663. /**
  1664. * Enqueue Duet Date Picker.
  1665. */
  1666. function mc_enqueue_duet() {
  1667. wp_enqueue_script( 'duet.js' );
  1668. wp_enqueue_style( 'duet.css' );
  1669. // Enqueue datepicker options.
  1670. wp_enqueue_script( 'mc.duet' );
  1671. wp_localize_script(
  1672. 'mc.duet',
  1673. 'duetFormats',
  1674. array(
  1675. 'date' => ( get_option( 'mcs_date_format', '' ) ) ? get_option( 'mcs_date_format' ) : 'Y-m-d',
  1676. )
  1677. );
  1678. wp_localize_script(
  1679. 'mc.duet',
  1680. 'duetLocalization',
  1681. array(
  1682. 'buttonLabel' => __( 'Choose date', 'my-calendar' ),
  1683. 'placeholder' => mc_parse_date_format(),
  1684. 'selectedDateMessage' => __( 'Selected date is', 'my-calendar' ),
  1685. 'prevMonthLabel' => __( 'Previous month', 'my-calendar' ),
  1686. 'nextMonthLabel' => __( 'Next month', 'my-calendar' ),
  1687. 'monthSelectLabel' => __( 'Month', 'my-calendar' ),
  1688. 'yearSelectLabel' => __( 'Year', 'my-calendar' ),
  1689. 'closeLabel' => __( 'Close window', 'my-calendar' ),
  1690. 'keyboardInstruction' => __( 'You can use arrow keys to navigate dates', 'my-calendar' ),
  1691. 'calendarHeading' => __( 'Choose a date', 'my-calendar' ),
  1692. 'dayNames' => array(
  1693. date_i18n( 'D', strtotime( 'Sunday' ) ),
  1694. date_i18n( 'D', strtotime( 'Monday' ) ),
  1695. date_i18n( 'D', strtotime( 'Tuesday' ) ),
  1696. date_i18n( 'D', strtotime( 'Wednesday' ) ),
  1697. date_i18n( 'D', strtotime( 'Thursday' ) ),
  1698. date_i18n( 'D', strtotime( 'Friday' ) ),
  1699. date_i18n( 'D', strtotime( 'Saturday' ) ),
  1700. ),
  1701. 'monthNames' => array(
  1702. date_i18n( 'F', strtotime( 'January 1' ) ),
  1703. date_i18n( 'F', strtotime( 'February 1' ) ),
  1704. date_i18n( 'F', strtotime( 'March 1' ) ),
  1705. date_i18n( 'F', strtotime( 'April 1' ) ),
  1706. date_i18n( 'F', strtotime( 'May 1' ) ),
  1707. date_i18n( 'F', strtotime( 'June 1' ) ),
  1708. date_i18n( 'F', strtotime( 'July 1' ) ),
  1709. date_i18n( 'F', strtotime( 'August 1' ) ),
  1710. date_i18n( 'F', strtotime( 'September 1' ) ),
  1711. date_i18n( 'F', strtotime( 'October 1' ) ),
  1712. date_i18n( 'F', strtotime( 'November 1' ) ),
  1713. date_i18n( 'F', strtotime( 'December 1' ) ),
  1714. ),
  1715. 'monthNamesShort' => array(
  1716. date_i18n( 'M', strtotime( 'January 1' ) ),
  1717. date_i18n( 'M', strtotime( 'February 1' ) ),
  1718. date_i18n( 'M', strtotime( 'March 1' ) ),
  1719. date_i18n( 'M', strtotime( 'April 1' ) ),
  1720. date_i18n( 'M', strtotime( 'May 1' ) ),
  1721. date_i18n( 'M', strtotime( 'June 1' ) ),
  1722. date_i18n( 'M', strtotime( 'July 1' ) ),
  1723. date_i18n( 'M', strtotime( 'August 1' ) ),
  1724. date_i18n( 'M', strtotime( 'September 1' ) ),
  1725. date_i18n( 'M', strtotime( 'October 1' ) ),
  1726. date_i18n( 'M', strtotime( 'November 1' ) ),
  1727. date_i18n( 'M', strtotime( 'December 1' ) ),
  1728. ),
  1729. 'locale' => str_replace( '_', '-', get_locale() ),
  1730. )
  1731. );
  1732. }
  1733. add_action( 'admin_enqueue_scripts', 'mc_scripts' );
  1734. /**
  1735. * Enqueue My Calendar admin scripts
  1736. */
  1737. function mc_scripts() {
  1738. global $current_screen;
  1739. $version = mc_get_version();
  1740. if ( SCRIPT_DEBUG ) {
  1741. $version .= wp_rand( 10000, 100000 );
  1742. }
  1743. $id = $current_screen->id;
  1744. $slug = sanitize_title( __( 'My Calendar', 'my-calendar' ) );
  1745. if ( false !== strpos( $id, 'my-calendar' ) || isset( $_GET['post'] ) && mc_get_option( 'uri_id' ) === $_GET['post'] ) {
  1746. // Script needs to be aware of current Pro version.
  1747. $mcs_version = ( get_option( 'mcs_version', '' ) ) ? get_option( 'mcs_version' ) : 1.0;
  1748. wp_enqueue_script( 'mc.admin' );
  1749. wp_localize_script(
  1750. 'mc.admin',
  1751. 'mcAdmin',
  1752. array(
  1753. 'thumbHeight' => get_option( 'thumbnail_size_h' ),
  1754. 'deleteButton' => __( 'Cancel', 'my-calendar' ),
  1755. 'restoreButton' => __( 'Restore', 'my-calendar' ),
  1756. 'imageRemoved' => __( 'Featured image removed', 'my-calendar' ),
  1757. 'modalTitle' => __( 'Choose an Image', 'my-calendar' ),
  1758. 'buttonName' => __( 'Select', 'my-calendar' ),
  1759. 'mcs' => $mcs_version,
  1760. )
  1761. );
  1762. wp_enqueue_script( 'mc.admin-footer' );
  1763. if ( version_compare( $mcs_version, '2.1', '<' ) ) {
  1764. wp_enqueue_style( 'mcs-back-compat' );
  1765. }
  1766. if ( function_exists( 'wp_enqueue_media' ) && ! did_action( 'wp_enqueue_media' ) ) {
  1767. wp_enqueue_media();
  1768. }
  1769. }
  1770. if ( $slug . '_page_my-calendar-design' === $id || $slug . '_page_my-calendar-categories' === $id ) {
  1771. wp_enqueue_style( 'wp-color-picker' );
  1772. wp_enqueue_script( 'mc-color-picker' );
  1773. }
  1774. if ( 'toplevel_page_my-calendar' === $id ) {
  1775. wp_enqueue_script( 'jquery-ui-autocomplete' ); // required for character counting.
  1776. }
  1777. if ( $slug . '_page_my-calendar-locations' === $id || 'toplevel_page_my-calendar' === $id ) {
  1778. $api_key = mc_get_option( 'gmap_api_key' );
  1779. if ( $api_key ) {
  1780. wp_enqueue_script( 'gmaps' );
  1781. wp_enqueue_script( 'mc-maps' );
  1782. wp_localize_script(
  1783. 'mc-maps',
  1784. 'gmaps',
  1785. array(
  1786. 'toggle' => '<span class="dashicons dashicons-arrow-right" aria-hidden="true"></span><span class="screen-reader-text">' . __( 'Location Details', 'my-calendar' ) . '</span>',
  1787. )
  1788. );
  1789. }
  1790. }
  1791. if ( 'toplevel_page_my-calendar' === $id && function_exists( 'wpt_post_to_service' ) ) {
  1792. wp_enqueue_script( 'mc.charcount' );
  1793. }
  1794. if ( 'toplevel_page_my-calendar' === $id || $slug . '_page_my-calendar-manage' === $id ) {
  1795. if ( current_user_can( 'mc_manage_events' ) ) {
  1796. wp_enqueue_script( 'mc.ajax' );
  1797. $event_id = ( isset( $_GET['event_id'] ) ) ? (int) $_GET['event_id'] : '';
  1798. wp_localize_script(
  1799. 'mc.ajax',
  1800. 'mc_data',
  1801. array(
  1802. 'action' => 'delete_occurrence',
  1803. 'recur' => 'add_date',
  1804. 'security' => wp_create_nonce( 'mc-delete-nonce' ),
  1805. 'url' => esc_url( add_query_arg( 'event_id', $event_id, admin_url( 'admin.php?page=my-calendar&mode=edit' ) ) ),
  1806. )
  1807. );
  1808. wp_localize_script(
  1809. 'mc.ajax',
  1810. 'mc_recur',
  1811. array(
  1812. 'action' => 'display_recurrence',
  1813. 'security' => wp_create_nonce( 'mc-recurrence-nonce' ),
  1814. )
  1815. );
  1816. wp_localize_script(
  1817. 'mc.ajax',
  1818. 'mc_cats',
  1819. array(
  1820. 'action' => 'add_category',
  1821. 'security' => wp_create_nonce( 'mc-add-category-nonce' ),
  1822. )
  1823. );
  1824. }
  1825. /**
  1826. * Filter the number of locations required to trigger a switch between a select input and an autocomplete.
  1827. *
  1828. * @hook mc_convert_locations_select_to_autocomplete
  1829. *
  1830. * @param {int} $count Number of locations that will remain a select. Default 90.
  1831. *
  1832. * @return {int}
  1833. */
  1834. if ( mc_count_locations() > apply_filters( 'mc_convert_locations_select_to_autocomplete', 90 ) ) {
  1835. wp_enqueue_script( 'accessible-autocomplete' );
  1836. wp_enqueue_script( 'mc-autocomplete' );
  1837. wp_localize_script(
  1838. 'mc-autocomplete',
  1839. 'mclocations',
  1840. array(
  1841. 'ajaxurl' => admin_url( 'admin-ajax.php' ),
  1842. 'security' => wp_create_nonce( 'mc-search-locations' ),
  1843. 'action' => 'mc_core_autocomplete_search_locations',
  1844. )
  1845. );
  1846. }
  1847. }
  1848. if ( $slug . '_page_my-calendar-config' === $id ) {
  1849. wp_enqueue_script( 'accessible-autocomplete' );
  1850. wp_enqueue_script( 'mc-autocomplete' );
  1851. wp_localize_script(
  1852. 'mc-autocomplete',
  1853. 'mcpages',
  1854. array(
  1855. 'ajaxurl' => admin_url( 'admin-ajax.php' ),
  1856. 'security' => wp_create_nonce( 'mc-search-pages' ),
  1857. 'action' => 'mc_core_autocomplete_search_pages',
  1858. )
  1859. );
  1860. }
  1861. if ( $slug . '_page_my-calendar-categories' === $id ) {
  1862. wp_enqueue_script( 'accessible-autocomplete' );
  1863. wp_enqueue_script( 'mc-autocomplete' );
  1864. wp_localize_script(
  1865. 'mc-autocomplete',
  1866. 'mcicons',
  1867. array(
  1868. 'ajaxurl' => admin_url( 'admin-ajax.php' ),
  1869. 'security' => wp_create_nonce( 'mc-search-icons' ),
  1870. 'action' => 'mc_core_autocomplete_search_icons',
  1871. )
  1872. );
  1873. }
  1874. if ( $slug . '_page_my-calendar-locations' === $id || 'toplevel_page_my-calendar' === $id ) {
  1875. wp_enqueue_script( 'accessible-autocomplete' );
  1876. wp_enqueue_script( 'mc-autocomplete' );
  1877. wp_localize_script(
  1878. 'mc-autocomplete',
  1879. 'mccountries',
  1880. array(
  1881. 'ajaxurl' => admin_url( 'admin-ajax.php' ),
  1882. 'security' => wp_create_nonce( 'mc-search-countries' ),
  1883. 'action' => 'mc_core_autocomplete_search_countries',
  1884. )
  1885. );
  1886. }
  1887. }
  1888. /**
  1889. * Get the My Calendar time format.
  1890. *
  1891. * @return string format.
  1892. */
  1893. function mc_time_format() {
  1894. $mc_time_format = mc_get_option( 'time_format' );
  1895. $time_format = get_option( 'time_format', '' );
  1896. if ( '' === $mc_time_format ) {
  1897. $mc_time_format = $time_format;
  1898. }
  1899. if ( '' === $mc_time_format ) {
  1900. $mc_time_format = 'h:i a';
  1901. }
  1902. return $mc_time_format;
  1903. }
  1904. /**
  1905. * Return a table header with sortability.
  1906. *
  1907. * @param string $label Column label.
  1908. * @param bool|string $sort ascending or descending.
  1909. * @param string $sortby Column currently sorted.
  1910. * @param string $sorted This sort column.
  1911. * @param bool|string $url URL to sort column.
  1912. *
  1913. * @return string
  1914. */
  1915. function mc_table_header( $label, $sort, $sortby, $sorted, $url = false ) {
  1916. $id = sanitize_title( $label ) . ( ( $url ) ? md5( remove_query_arg( 'order', $url ) ) : '' );
  1917. $inner = ( $url ) ? '<a href="' . esc_url( $url ) . '#' . $id . '">' . $label . '</a>' : $label;
  1918. $sort = ( ! $sort ) ? false : ( ( 'ASC' === $sort ) ? 'descending' : 'ascending' );
  1919. $th = ( $sort && ( (string) $sortby === (string) $sorted ) ) ? '<th scope="col" aria-sort="' . $sort . '">' : '<th scope="col">';
  1920. $return = $th . $inner . '</th>';
  1921. return $return;
  1922. }
  1923. /**
  1924. * As of version 3.4.0, this checks for the shortcode, to see if there's a page with the calendar shortcode.
  1925. *
  1926. * @return array
  1927. */
  1928. function mc_locate_calendar() {
  1929. $return = array(
  1930. 'response' => false,
  1931. 'message' => __( 'Calendar query was not able to run.', 'my-calendar' ),
  1932. );
  1933. global $wpdb;
  1934. $has_uri = mc_get_uri( 'boolean' );
  1935. $current = mc_get_uri();
  1936. // check whether calendar page is a valid URL.
  1937. if ( $has_uri && esc_url( $current ) ) {
  1938. $response = wp_remote_head( $current );
  1939. if ( ! is_wp_error( $response ) ) {
  1940. $http = (string) $response['response']['code'];
  1941. // Only modify the value if it's explicitly missing. Redirects or secured pages are fine.
  1942. if ( '404' === $http ) {
  1943. $current = '';
  1944. }
  1945. }
  1946. }
  1947. if ( ! $has_uri ) {
  1948. // Locate oldest post containing my_calendar shortcode. Will also locate upcoming events shortcodes, however.
  1949. $post_ID = $wpdb->get_var( "SELECT id FROM $wpdb->posts WHERE post_content LIKE '%[my_calendar%' AND post_status = 'publish'" );
  1950. if ( $post_ID ) {
  1951. $link = get_permalink( $post_ID );
  1952. mc_update_option( 'uri_id', $post_ID );
  1953. $return = array(
  1954. 'response' => true,
  1955. 'message' => esc_html__( 'Is this your calendar page?', 'my-calendar' ) . ' <a href="' . esc_url( $link ) . '"><code>' . esc_html( $link ) . '</code></a>',
  1956. );
  1957. return $return;
  1958. } else {
  1959. $page = mc_generate_calendar_page( 'my-calendar' );
  1960. mc_update_option( 'uri_id', $page );
  1961. // translators: URL for new My Calendar page.
  1962. $confirmation = sprintf( esc_html__( 'New calendar page created at <a href="%s">My Calendar</a>', 'my-calendar' ), esc_url( get_permalink( $page ) ) );
  1963. $return = array(
  1964. 'response' => true,
  1965. 'message' => $confirmation,
  1966. );
  1967. return $return;
  1968. }
  1969. } else {
  1970. $return = array(
  1971. 'response' => true,
  1972. 'message' => esc_html__( 'Calendar installed.', 'my-calendar' ),
  1973. );
  1974. }
  1975. return $return;
  1976. }
  1977. /**
  1978. * Set up support form
  1979. */
  1980. function mc_get_support_form() {
  1981. global $current_user, $wpdb;
  1982. $current_user = wp_get_current_user();
  1983. // send fields for My Calendar.
  1984. $version = mc_get_version();
  1985. $mc_db_version = get_option( 'mc_db_version' );
  1986. $mc_uri = mc_get_uri();
  1987. $mc_css = mc_get_option( 'css_file' );
  1988. // Pro license status.
  1989. $license = ( ! defined( 'MCS_LICENSE_KEY' ) ) ? get_option( 'mcs_license_key' ) : MCS_LICENSE_KEY;
  1990. $license_valid = get_option( 'mcs_license_key_valid' );
  1991. $checked = ( 'valid' === $license_valid ) ? true : false;
  1992. if ( $license ) {
  1993. $license = "
  1994. License: $license, $license_valid";
  1995. }
  1996. // send fields for all plugins.
  1997. $wp_version = get_bloginfo( 'version' );
  1998. $home_url = home_url();
  1999. $wp_url = site_url();
  2000. $language = get_bloginfo( 'language' );
  2001. $charset = get_bloginfo( 'charset' );
  2002. // server.
  2003. $php_version = phpversion();
  2004. $db_version = $wpdb->db_version();
  2005. $admin_email = get_option( 'admin_email' );
  2006. $db_time = mc_ts( true )['db'];
  2007. $wp_time = mc_ts( true )['wp'];
  2008. $db_type = mc_get_db_type();
  2009. // theme data.
  2010. $theme = wp_get_theme();
  2011. $theme_name = $theme->get( 'Name' );
  2012. $theme_uri = $theme->get( 'ThemeURI' );
  2013. $theme_parent = $theme->get( 'Template' );
  2014. $theme_parent = ( $theme_parent ) ? $theme_parent : __( 'None', 'my-calendar' );
  2015. $theme_version = $theme->get( 'Version' );
  2016. // plugin data.
  2017. $plugins = get_plugins();
  2018. $plugins_string = '';
  2019. foreach ( array_keys( $plugins ) as $key ) {
  2020. if ( is_plugin_active( $key ) ) {
  2021. $plugin =& $plugins[ $key ];
  2022. $plugin_name = $plugin['Name'];
  2023. $plugin_uri = $plugin['PluginURI'];
  2024. $plugin_version = $plugin['Version'];
  2025. $plugins_string .= "$plugin_name: $plugin_version; $plugin_uri\n";
  2026. }
  2027. }
  2028. $data = "
  2029. ================ Installation Data ====================
  2030. ==My Calendar:==
  2031. Version: $version
  2032. DB Version: $mc_db_version
  2033. URI: $mc_uri
  2034. CSS: $mc_css$license
  2035. Requester Email: $current_user->user_email
  2036. Admin Email: $admin_email
  2037. ==WordPress:==
  2038. Version: $wp_version
  2039. URL: $home_url
  2040. Install: $wp_url
  2041. Language: $language
  2042. Charset: $charset
  2043. ==Extra info:==
  2044. PHP Version: $php_version
  2045. DB Version: $db_version
  2046. DB UTC Offset: $db_time
  2047. WP UTC Offset: $wp_time
  2048. DB Type: $db_type
  2049. Server Software: $_SERVER[SERVER_SOFTWARE]
  2050. User Agent: $_SERVER[HTTP_USER_AGENT]
  2051. ==Theme:==
  2052. Name: $theme_name
  2053. URI: $theme_uri
  2054. Parent: $theme_parent
  2055. Version: $theme_version
  2056. ==Active Plugins:==
  2057. $plugins_string
  2058. ";
  2059. $support_data = '<div class="mc-copy-button"><button class="button-primary mc-copy-to-clipboard" data-clipboard-target="#mc-clipboard">' . __( 'Copy to clipboard', 'my-calendar' ) . '</button>
  2060. <span class="mc-notice-copied">' . __( 'Help Info Copied', 'my-calendar' ) . '</span></div>
  2061. <label for="mc-clipboard">' . __( 'Help Info', 'my-calendar' ) . '</label><textarea id="mc-clipboard" class="help" readonly>%s</textarea>';
  2062. if ( $checked ) {
  2063. $request = '';
  2064. if ( isset( $_POST['mc_support'] ) ) {
  2065. $nonce = $_REQUEST['_wpnonce'];
  2066. if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
  2067. wp_die( 'My Calendar: Security check failed' );
  2068. }
  2069. $request = ( ! empty( $_POST['support_request'] ) ) ? stripslashes( $_POST['support_request'] ) : false;
  2070. $subject = 'My Calendar Pro support request.';
  2071. $message = $request . "\n\n" . $data;
  2072. // Get the site domain and get rid of www. from pluggable.php.
  2073. $sitename = strtolower( $_SERVER['SERVER_NAME'] );
  2074. if ( 'www.' === substr( $sitename, 0, 4 ) ) {
  2075. $sitename = substr( $sitename, 4 );
  2076. }
  2077. $from_email = 'wordpress@' . $sitename;
  2078. $from = "From: $current_user->display_name <$from_email>\r\nReply-to: $current_user->display_name <$current_user->user_email>\r\n";
  2079. if ( ! $request ) {
  2080. echo wp_kses_post( '<div class="message error"><p>' . __( 'Please describe your problem in detail. I\'m not psychic.', 'my-calendar' ) . '</p></div>' );
  2081. } else {
  2082. $sent = wp_mail( 'plugins@joedolson.com', $subject, $message, $from );
  2083. if ( $sent ) {
  2084. mc_show_notice( __( 'I\'ll get back to you as soon as I can.', 'my-calendar' ) . __( 'You should receive an automatic response to your request when I receive it. If you do not receive this notice, then either I did not receive your message or the email it was sent from was not a valid address.', 'my-calendar' ) );
  2085. } else {
  2086. // Translators: Support form URL.
  2087. echo wp_kses_post( '<div class="message error"><p>' . __( "Sorry! I couldn't send that message. Here's the text of your request:", 'my-calendar' ) . '</p><p>' . sprintf( __( '<a href="%s">Contact me here</a>, instead', 'my-calendar' ), 'https://www.joedolson.com/contact/' ) . "</p><pre>$request</pre></div>" );
  2088. }
  2089. }
  2090. }
  2091. ?>
  2092. <form method="post" action="<?php echo esc_url( admin_url( 'admin.php?page=my-calendar-help' ) ); ?>">
  2093. <div><input type="hidden" name="_wpnonce" value="<?php echo esc_attr( wp_create_nonce( 'my-calendar-nonce' ) ); ?>" /></div>
  2094. <div>
  2095. <code><?php echo esc_html( __( 'From:', 'my-calendar' ) . " \"$current_user->display_name\" &lt;$current_user->user_email&gt;" ); ?></code>
  2096. </p>
  2097. <p>
  2098. <label for='support_request'>Support Request:</label><br /><textarea name='support_request' id='support_request' required aria-required='true' cols='80' rows='10' class='widefat'><?php echo esc_textarea( stripslashes( $request ) ); ?></textarea>
  2099. </p>
  2100. <p>
  2101. <input type='submit' value='<?php echo esc_attr( __( 'Send Support Request', 'my-calendar' ) ); ?>' name='mc_support' class='button-primary' />
  2102. </p>
  2103. <p><?php esc_html_e( 'The following additional information will be sent with your support request:', 'my-calendar' ); ?></p>
  2104. <?php printf( wp_kses_post( wpautop( $support_data ) ), esc_textarea( $data ) ); ?>
  2105. </div>
  2106. </form>
  2107. <?php
  2108. } else {
  2109. echo wp_kses_post( '<p><a href="https://wordpress.org/support/plugin/my-calendar/">' . __( 'Request support at the WordPress.org Support Forums', 'my-calendar' ) . '</a> &bull; <a href="https://www.joedolson.com/my-calendar/pro/">' . __( 'Upgrade to Pro for direct plugin support!', 'my-calendar' ) . '</a></p>' . sprintf( wpautop( $support_data ), esc_textarea( $data ) ) );
  2110. }
  2111. }
  2112. add_action( 'init', 'mc_register_actions' );
  2113. /**
  2114. * Register actions attached to My Calendar events, usable to add additional actions during those events.
  2115. */
  2116. function mc_register_actions() {
  2117. add_filter( 'mc_event_registration', 'mc_standard_event_registration', 10, 4 );
  2118. add_filter( 'mc_datetime_inputs', 'mc_standard_datetime_input', 10, 4 );
  2119. add_action( 'mc_transition_event', 'mc_tweet_approval', 10, 2 );
  2120. add_action( 'mc_delete_event', 'mc_event_delete_post', 10, 2 );
  2121. add_action( 'mc_mass_delete_events', 'mc_event_delete_posts', 10, 1 );
  2122. add_action( 'parse_request', 'my_calendar_api' );
  2123. add_action( 'delete_post', 'mc_check_calendar_page', 10, 2 );
  2124. }
  2125. // Filters.
  2126. add_filter( 'post_updated_messages', 'mc_posttypes_messages' );
  2127. add_filter( 'next_post_link', 'mc_next_post_link', 10, 2 );
  2128. add_filter( 'previous_post_link', 'mc_previous_post_link', 10, 2 );
  2129. add_filter( 'the_title', 'mc_the_title', 10, 2 );
  2130. add_filter( 'body_class', 'mc_body_classes', 10, 1 );
  2131. // Actions.
  2132. add_action( 'init', 'mc_taxonomies', 0 );
  2133. add_action( 'init', 'mc_posttypes' );
  2134. /**
  2135. * Check if deleted post is the My Calendar page. If it is, unset the My Calendar setting.
  2136. *
  2137. * @param int $post_ID Post ID.
  2138. * @param object $post Post object.
  2139. */
  2140. function mc_check_calendar_page( $post_ID, $post ) {
  2141. $calendar_page = mc_get_option( 'uri_id' );
  2142. if ( $post_ID === (int) $calendar_page ) {
  2143. mc_update_option( 'uri_id', '' );
  2144. }
  2145. }
  2146. /**
  2147. * Change out previous post link for previous event.
  2148. *
  2149. * @param string $output Original link.
  2150. * @param string $format Link anchor format.
  2151. *
  2152. * @return string
  2153. */
  2154. function mc_previous_post_link( $output, $format ) {
  2155. if ( mc_is_single_event() ) {
  2156. $mc_id = ( isset( $_GET['mc_id'] ) && is_numeric( $_GET['mc_id'] ) ) ? $_GET['mc_id'] : false;
  2157. if ( ! $mc_id ) {
  2158. $post_id = get_the_ID();
  2159. $mc_id = get_post_meta( $post_id, '_mc_event_id', true );
  2160. }
  2161. $event = mc_adjacent_event( $mc_id, 'previous' );
  2162. if ( empty( $event ) ) {
  2163. return '';
  2164. }
  2165. remove_filter( 'the_title', 'mc_the_title', 10 );
  2166. $title = apply_filters( 'the_title', $event['title'], $event['post'] );
  2167. add_filter( 'the_title', 'mc_the_title', 10, 2 );
  2168. $link = add_query_arg( 'mc_id', $event['dateid'], $event['permalink'] );
  2169. $date = ' <span class="mc-event-date">' . $event['date'] . '</span>';
  2170. $output = str_replace( '%link', '<a href="' . $link . '" rel="next" class="mc-adjacent">' . $title . $date . '</a>', $format );
  2171. }
  2172. return $output;
  2173. }
  2174. /**
  2175. * Change out next post link for next event.
  2176. *
  2177. * @param string $output Original link.
  2178. * @param string $format Link anchor format.
  2179. *
  2180. * @return string
  2181. */
  2182. function mc_next_post_link( $output, $format ) {
  2183. if ( mc_is_single_event() ) {
  2184. $mc_id = ( isset( $_GET['mc_id'] ) && is_numeric( $_GET['mc_id'] ) ) ? $_GET['mc_id'] : false;
  2185. if ( ! $mc_id ) {
  2186. $post_id = get_the_ID();
  2187. $mc_id = get_post_meta( $post_id, '_mc_event_id', true );
  2188. }
  2189. $event = mc_adjacent_event( $mc_id, 'next' );
  2190. if ( empty( $event ) ) {
  2191. return '';
  2192. }
  2193. remove_filter( 'the_title', 'mc_the_title', 10 );
  2194. $title = apply_filters( 'the_title', $event['title'], $event['post'] );
  2195. add_filter( 'the_title', 'mc_the_title', 10, 2 );
  2196. $link = add_query_arg( 'mc_id', $event['dateid'], $event['permalink'] );
  2197. $date = ' <span class="mc-event-date">' . $event['date'] . '</span>';
  2198. $output = str_replace( '%link', '<a href="' . $link . '" rel="next" class="mc-adjacent">' . $title . $date . '</a>', $format );
  2199. }
  2200. return $output;
  2201. }
  2202. /**
  2203. * Filter the edit post link to point to the event editor.
  2204. *
  2205. * @param string $link Link to editor.
  2206. * @param int $post_id Current post ID.
  2207. * @param string $context Calling context.
  2208. *
  2209. * @return string Link.
  2210. */
  2211. function mc_get_edit_post_link( $link, $post_id, $context ) {
  2212. if ( is_singular( 'mc-events' ) ) {
  2213. $event_id = get_post_meta( $post_id, '_mc_event_id', true );
  2214. $link = admin_url( 'admin.php?page=my-calendar&mode=edit&event_id=' . absint( $event_id ) );
  2215. $link = ( 'display' === $context ) ? esc_url( $link ) : $link;
  2216. }
  2217. return $link;
  2218. }
  2219. add_filter( 'get_edit_post_link', 'mc_get_edit_post_link', 10, 3 );
  2220. /**
  2221. * Filter body classes on event singular posts.
  2222. *
  2223. * @param array $classes Array of body classes.
  2224. *
  2225. * @return array
  2226. */
  2227. function mc_body_classes( $classes ) {
  2228. $event_classes = array();
  2229. if ( is_singular( 'mc-events' ) || isset( $_GET['mc_id'] ) ) {
  2230. $post_id = get_the_ID();
  2231. if ( $post_id && is_single( $post_id ) ) {
  2232. $event = false;
  2233. $event_id = ( isset( $_GET['mc_id'] ) && is_numeric( $_GET['mc_id'] ) ) ? $_GET['mc_id'] : false;
  2234. if ( ! $event_id ) {
  2235. $parent_id = get_post_meta( $post_id, '_mc_event_id', true );
  2236. $event = mc_get_nearest_event( $parent_id, true );
  2237. }
  2238. if ( is_numeric( $event_id ) ) {
  2239. $event = mc_get_event( $event_id );
  2240. if ( ! is_object( $event ) ) {
  2241. $event = mc_get_nearest_event( $event_id, true );
  2242. }
  2243. }
  2244. if ( is_object( $event ) ) {
  2245. $event_classes = explode( ' ', mc_get_event_classes( $event, 'body' ) );
  2246. }
  2247. }
  2248. }
  2249. $classes = array_merge( $classes, $event_classes );
  2250. return $classes;
  2251. }
  2252. /**
  2253. * Replace title on individual event pages with viewed event value & config.
  2254. *
  2255. * @param string $title Original title.
  2256. * @param int $post_id Post ID.
  2257. *
  2258. * @return string new title string
  2259. */
  2260. function mc_the_title( $title, $post_id = null ) {
  2261. // in_the_loop() is not true in Full Site Editing, but is_main_query() is. This is a bug in FSE.
  2262. // However, in classic themes, is_main_query() is true in menus. So, screwed either way.
  2263. if ( is_singular( 'mc-events' ) && ( in_the_loop() ) ) {
  2264. if ( $post_id && is_single( $post_id ) ) {
  2265. $event = false;
  2266. $event_id = ( isset( $_GET['mc_id'] ) && is_numeric( $_GET['mc_id'] ) ) ? $_GET['mc_id'] : false;
  2267. if ( ! $event_id ) {
  2268. $parent_id = get_post_meta( $post_id, '_mc_event_id', true );
  2269. $event = mc_get_nearest_event( $parent_id, true );
  2270. }
  2271. if ( is_numeric( $event_id ) ) {
  2272. $event = mc_get_event( $event_id );
  2273. if ( ! is_object( $event ) ) {
  2274. $event = mc_get_nearest_event( $event_id, true );
  2275. }
  2276. }
  2277. if ( is_object( $event ) && property_exists( $event, 'category_icon' ) ) {
  2278. $icon = mc_category_icon( $event );
  2279. if ( false !== stripos( $icon, 'svg' ) && 'background' === mc_get_option( 'apply_color' ) ) {
  2280. $color = esc_attr( $event->category_color );
  2281. $icon = str_replace( 'fill:', 'background:' . $color . ';fill:', $icon );
  2282. }
  2283. } else {
  2284. $icon = '';
  2285. }
  2286. if ( is_object( $event ) ) {
  2287. $event_title = stripslashes( $event->event_title );
  2288. if ( $event_title !== $title ) {
  2289. $title = $event_title;
  2290. }
  2291. $template = mc_get_template( 'title_solo' );
  2292. if ( '' === $template || '{title}' === $template ) {
  2293. $title = $icon . ' ' . strip_tags( $title, mc_strip_tags() );
  2294. } else {
  2295. $data = mc_create_tags( $event, $event_id );
  2296. $title = mc_draw_template( $data, $template );
  2297. }
  2298. } else {
  2299. // If both queries fail to get title, return original.
  2300. return $title;
  2301. }
  2302. }
  2303. }
  2304. return $title;
  2305. }
  2306. add_action( 'admin_init', 'mc_dismiss_notice' );
  2307. /**
  2308. * Dismiss admin notices
  2309. */
  2310. function mc_dismiss_notice() {
  2311. if ( isset( $_GET['dismiss'] ) && 'update' === $_GET['dismiss'] ) {
  2312. $notice = ( isset( $_GET['notice'] ) ) ? sanitize_text_field( $_GET['notice'] ) : '';
  2313. if ( $notice ) {
  2314. update_option( 'mc_notice_' . $notice, 1 );
  2315. }
  2316. }
  2317. }
  2318. add_action( 'admin_notices', 'mc_update_notice' );
  2319. /**
  2320. * Admin notices
  2321. */
  2322. function mc_update_notice() {
  2323. if ( current_user_can( 'manage_options' ) && isset( $_GET['page'] ) && stripos( $_GET['page'], 'my-calendar' ) !== false ) {
  2324. if ( 'true' === mc_get_option( 'remote' ) ) {
  2325. mc_show_notice( __( 'My Calendar is configured to retrieve events from a remote source.', 'my-calendar' ) . ' <a href="' . admin_url( 'admin.php?page=my-calendar-config' ) . '">' . __( 'Update Settings', 'my-calendar' ) . '</a>' );
  2326. }
  2327. }
  2328. }
  2329. /**
  2330. * Allow CORS from subsites in multisite networks in subdomain setups.
  2331. */
  2332. function mc_setup_cors_access() {
  2333. $cache = get_transient( 'mc_allowed_origins' );
  2334. $origin = str_replace( array( 'http://', 'https://' ), '', get_http_origin() );
  2335. if ( $cache ) {
  2336. $allowed = $cache;
  2337. } else {
  2338. $sites = ( function_exists( 'get_sites' ) ) ? get_sites() : array();
  2339. /**
  2340. * Filter what sites are allowed CORS access.
  2341. *
  2342. * @hook mc_setup_allowed_sites
  2343. *
  2344. * @param {array} $allowed URLs permitted access. Default empty array.
  2345. * @param {string} $origin HTTP origin passed.
  2346. *
  2347. * @return {array}
  2348. */
  2349. $allowed = apply_filters( 'mc_setup_allowed_sites', array(), $origin );
  2350. if ( ! empty( $sites ) ) {
  2351. foreach ( $sites as $site ) {
  2352. $allowed[] = str_replace( array( 'http://', 'https://' ), '', get_home_url( $site->blog_id ) );
  2353. }
  2354. }
  2355. set_transient( 'mc_allowed_origins', $allowed, MONTH_IN_SECONDS );
  2356. }
  2357. if ( $origin && is_array( $allowed ) && in_array( $origin, $allowed, true ) ) {
  2358. header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) );
  2359. header( 'Access-Control-Allow-Methods: GET' );
  2360. header( 'Access-Control-Allow-Credentials: true' );
  2361. }
  2362. }
  2363. add_action( 'send_headers', 'mc_setup_cors_access' );
  2364. /**
  2365. * Register post meta field used by calendar page manager metabox.
  2366. */
  2367. function mc_register_meta() {
  2368. register_post_meta(
  2369. 'page',
  2370. '_mc_calendar',
  2371. array(
  2372. 'show_in_rest' => array(
  2373. 'schema' => array(
  2374. 'type' => 'object',
  2375. 'properties' => array(
  2376. 'shortcode' => array(
  2377. 'type' => 'string',
  2378. ),
  2379. ),
  2380. 'additionalProperties' => array(
  2381. 'type' => 'string',
  2382. ),
  2383. 'items' => array(
  2384. 'type' => 'string',
  2385. ),
  2386. ),
  2387. ),
  2388. 'single' => true,
  2389. 'type' => 'array',
  2390. 'auth_callback' => 'mc_can_update_meta',
  2391. )
  2392. );
  2393. }
  2394. add_action( 'init', 'mc_register_meta' );
  2395. /**
  2396. * Verify if a user can edit meta fields.
  2397. */
  2398. function mc_can_update_meta() {
  2399. return current_user_can( 'edit_posts' );
  2400. }
  2401. /**
  2402. * Set an option indicating that a job has been scheduled for promoting My Calendar Pro.
  2403. */
  2404. function mc_schedule_promotion() {
  2405. if ( ! function_exists( 'mcs_submissions' ) && '1' === get_option( 'mc_promotion_scheduled' ) ) {
  2406. update_option( 'mc_promotion_scheduled', '2' );
  2407. }
  2408. }
  2409. add_action( 'mc_schedule_promotion_action', 'mc_schedule_promotion' );
  2410. /**
  2411. * Dismiss promotion notice.
  2412. */
  2413. function mc_dismiss_promotion() {
  2414. if ( isset( $_GET['dismiss'] ) && 'promotion' === $_GET['dismiss'] ) {
  2415. update_option( 'mc_promotion_scheduled', '3' );
  2416. }
  2417. }
  2418. add_action( 'admin_notices', 'mc_dismiss_promotion', 5 );
  2419. /**
  2420. * Display promotion notice to admin users who have not donated or purchased My Calendar Pro.
  2421. */
  2422. function mc_promotion_notice() {
  2423. if ( function_exists( 'mcs_submissions' ) ) {
  2424. return;
  2425. }
  2426. $is_calendar = isset( $_GET['page'] ) && false !== stripos( $_GET['page'], 'my-calendar' );
  2427. if ( current_user_can( 'activate_plugins' ) && '2' === get_option( 'mc_promotion_scheduled' ) && $is_calendar ) {
  2428. $upgrade = 'https://www.joedolson.com/awesome/my-calendar-pro/';
  2429. $dismiss = admin_url( 'admin.php?page=my-calendar-config&dismiss=promotion' );
  2430. // Translators: URL to upgrade.
  2431. echo "<div class='notice mc-promotion'><p><img src='" . esc_url( plugins_url( 'images/awd-logo-disc.png', __FILE__ ) ) . "' alt='Joe Dolson Accessible Web Design' /><span>" . wp_kses_post( sprintf( __( 'I hope you\'ve enjoyed <strong>My Calendar</strong>! Take a look at <a href=\'%1$s\'>upgrading to My Calendar Pro</a> for advanced event management with WordPress! <a href=\'%2$s\' class="button-secondary">Dismiss</a>', 'my-calendar' ), esc_url( $upgrade ), esc_url( $dismiss ) ) ) . '</span></p></div>';
  2432. }
  2433. }
  2434. add_action( 'admin_notices', 'mc_promotion_notice', 10 );
  2435. /**
  2436. * Schedule a promotional banner for My Calendar Pro if not present.
  2437. */
  2438. function mc_schedule_promotions() {
  2439. if ( ! function_exists( 'mcs_submissions' ) ) {
  2440. if ( false === get_option( 'mc_promotion_scheduled', false ) ) {
  2441. // Promote Pro eight weeks after first event.
  2442. wp_schedule_single_event( time() + ( 60 * 60 * 24 * 7 * 8 ), 'mc_schedule_promotion_action' );
  2443. update_option( 'mc_promotion_scheduled', '1' );
  2444. }
  2445. if ( '3' === get_option( 'wpt_promotion_scheduled' ) ) {
  2446. // Schedule an additional promotion for 1 year after event created following previous promotion.
  2447. wp_schedule_single_event( YEAR_IN_SECONDS, 'mc_schedule_promotion_action' );
  2448. update_option( 'mc_promotion_scheduled', '1' );
  2449. }
  2450. }
  2451. }