Source: my-calendar-events.php

  1. <?php
  2. /**
  3. * Get event data. Queries to fetch events and create or modify objects.
  4. *
  5. * @category Events
  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. * Processes objects to add needed properties.
  16. *
  17. * @param object $event Event object.
  18. *
  19. * @return object $event Modifed event.
  20. */
  21. function mc_event_object( $event ) {
  22. if ( is_object( $event ) ) {
  23. if ( ! property_exists( $event, 'categories' ) ) {
  24. $event->categories = mc_get_categories( $event, 'objects' );
  25. }
  26. if ( ! property_exists( $event, 'location' ) && is_numeric( $event->event_location ) && 0 !== (int) $event->event_location ) {
  27. $event->location = mc_get_location( $event->event_location );
  28. }
  29. if ( ! property_exists( $event, 'uid' ) ) {
  30. $guid = (string) get_post_meta( $event->event_post, '_mc_guid', true );
  31. if ( '' === $guid ) {
  32. $guid = mc_create_guid( $event );
  33. }
  34. $event->uid = $guid;
  35. }
  36. /**
  37. * Customize the My Calendar event object.
  38. *
  39. * @hook mc_event_object
  40. *
  41. * @param {object} $object A My Calendar event.
  42. *
  43. * @return {object}
  44. */
  45. $event = apply_filters( 'mc_event_object', $event );
  46. }
  47. return $event;
  48. }
  49. /**
  50. * Get the first and last event stored in the database.
  51. *
  52. * @param int|false $site Optional. Site to check.
  53. *
  54. * @return array Array containing the date of the first and last event.
  55. */
  56. function mc_get_date_bounds( $site = false ) {
  57. $return = get_transient( 'mc_get_date_bounds' );
  58. if ( ! $return ) {
  59. $mcdb = mc_is_remote_db();
  60. $first = $mcdb->get_var( 'SELECT occur_begin FROM ' . my_calendar_event_table( $site ) . ' ORDER BY occur_begin ASC LIMIT 0, 1' );
  61. $last = $mcdb->get_var( 'SELECT occur_end FROM ' . my_calendar_event_table( $site ) . ' ORDER BY occur_end DESC LIMIT 0, 1' );
  62. $return = array(
  63. 'first' => $first,
  64. 'last' => $last,
  65. );
  66. set_transient( 'mc_get_date_bounds', $return, DAY_IN_SECONDS );
  67. }
  68. return $return;
  69. }
  70. /**
  71. * Create a GUID for an event.
  72. *
  73. * @param object $event Event object.
  74. *
  75. * @return string GUID
  76. */
  77. function mc_create_guid( $event ) {
  78. $guid = md5( home_url() . $event->event_post . $event->event_id . $event->event_title );
  79. update_post_meta( $event->event_post, '_mc_guid', $guid );
  80. return $guid;
  81. }
  82. /**
  83. * Function for extracting event timestamps from MySQL. My Calendar expects the MySQL server to be in UTC.
  84. *
  85. * @param bool $test Test offset time.
  86. *
  87. * @return string|array
  88. */
  89. function mc_ts( $test = false ) {
  90. $db_engine = mc_get_db_type();
  91. $ts_sql = get_transient( 'mc_ts_string' );
  92. $ts_db = get_transient( 'mc_ts_db' );
  93. if ( $ts_db && $test ) {
  94. return $ts_db;
  95. }
  96. if ( $test || ! $ts_sql || ! $ts_db ) {
  97. global $wpdb;
  98. if ( 'sqlite' === $db_engine ) {
  99. $offset = $wpdb->get_var( "SELECT STRFTIME('%Y-%m-%dT%H:%M:%S', DATETIME('now', 'localtime')) || PRINTF('%+.2d:%.2d', ROUND((JULIANDAY('now', 'localtime') - JULIANDAY('now')) * 24), ABS(ROUND((JULIANDAY('now', 'localtime') - JULIANDAY('now')) * 24 * 60) % 60));" );
  100. } else {
  101. $offset = $wpdb->get_var( 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);' );
  102. }
  103. /**
  104. * Filter timezone offset applied when displaying events. Can fix issues with an incorrect server time.
  105. *
  106. * @hook mc_filter_offset
  107. *
  108. * @param {string} $offset Timezone offset format -HH:MM:SS.
  109. *
  110. * @return {string}
  111. */
  112. $offset = apply_filters( 'mc_filter_offset', $offset );
  113. $offset = substr( $offset, 0, -3 );
  114. if ( strpos( $offset, '-' ) !== 0 ) {
  115. $offset = '+' . $offset;
  116. }
  117. $wp_time = get_option( 'gmt_offset', '0' );
  118. $wp_time = ( $wp_time < 0 ) ? '-' . str_pad( absint( $wp_time ), 2, 0, STR_PAD_LEFT ) : '+' . str_pad( $wp_time, 2, 0, STR_PAD_LEFT );
  119. $wp_time .= ':00';
  120. if ( $test ) {
  121. $return = array(
  122. 'db' => $offset,
  123. 'wp' => $wp_time,
  124. );
  125. set_transient( 'mc_ts_db', $return, WEEK_IN_SECONDS );
  126. return $return;
  127. }
  128. // Converts occur_begin value from the WordPress timezone to the db timezone.
  129. // Has weakness that if an event was entered during DST, it's wrong during ST and vice versa.
  130. $ts_sql = "UNIX_TIMESTAMP( CONVERT_TZ( `occur_begin`, '$wp_time', '$offset' ) ) AS ts_occur_begin, UNIX_TIMESTAMP( CONVERT_TZ( `occur_end`, '$wp_time', '$offset' ) ) AS ts_occur_end ";
  131. if ( 'sqlite' === $db_engine ) {
  132. $ts_sql = "datetime( `occur_begin`, '$offset hour' ) || '+$wp_time' AS ts_occur_begin, datetime( `occur_end`, '$offset hour' ) || '+$wp_time' AS ts_occur_end ";
  133. }
  134. set_transient( 'mc_ts_string', $ts_sql, WEEK_IN_SECONDS );
  135. }
  136. return $ts_sql;
  137. }
  138. /**
  139. * Grab all events for the requested dates from calendar
  140. *
  141. * This function needs to be able to react to URL parameters for most factors, with the arguments being the default shown.
  142. *
  143. * @param array $args parameters to use for selecting events.
  144. *
  145. * @return array qualified events
  146. */
  147. function my_calendar_get_events( $args ) {
  148. $get = map_deep( $_GET, 'sanitize_text_field' );
  149. $from = isset( $args['from'] ) ? $args['from'] : '';
  150. $to = isset( $args['to'] ) ? $args['to'] : '';
  151. $category = isset( $args['category'] ) ? $args['category'] : 'all';
  152. $ltype = isset( $args['ltype'] ) ? $args['ltype'] : 'all';
  153. $lvalue = isset( $args['lvalue'] ) ? $args['lvalue'] : 'all';
  154. $author = isset( $args['author'] ) ? $args['author'] : 'all';
  155. $host = isset( $args['host'] ) ? $args['host'] : 'all';
  156. $search = isset( $args['search'] ) ? $args['search'] : '';
  157. $holidays = isset( $args['holidays'] ) ? $args['holidays'] : null;
  158. $site = isset( $args['site'] ) ? $args['site'] : false;
  159. $site = ! is_array( $site ) ? array( $site ) : $site;
  160. $mcdb = mc_is_remote_db();
  161. if ( 'holidays' === $holidays && '' === $category ) {
  162. return array();
  163. }
  164. if ( null === $holidays ) {
  165. $ccategory = ( isset( $get['mcat'] ) && '' !== trim( $get['mcat'] ) ) ? $get['mcat'] : $category;
  166. } else {
  167. $ccategory = $category;
  168. }
  169. $cltype = ( isset( $get['ltype'] ) ) ? $get['ltype'] : $ltype;
  170. $clvalue = ( isset( $get['loc'] ) ) ? $get['loc'] : $lvalue;
  171. $clauth = ( isset( $get['mc_auth'] ) ) ? $get['mc_auth'] : $author;
  172. $clhost = ( isset( $get['mc_host'] ) ) ? $get['mc_host'] : $host;
  173. // If location value is not set, then location type shouldn't be set.
  174. if ( 'all' === $clvalue ) {
  175. $cltype = 'all';
  176. }
  177. $from = mc_checkdate( $from );
  178. $to = mc_checkdate( $to );
  179. if ( ! $from || ! $to ) {
  180. return array();
  181. } // Not valid dates.
  182. $cat_limit = ( 'all' !== $ccategory ) ? mc_select_category( $ccategory ) : array();
  183. $join = ( isset( $cat_limit[0] ) ) ? $cat_limit[0] : '';
  184. $select_category = ( isset( $cat_limit[1] ) ) ? $cat_limit[1] : '';
  185. $select_author = ( 'all' !== $clauth ) ? mc_select_author( $clauth ) : '';
  186. $select_host = ( 'all' !== $clhost ) ? mc_select_host( $clhost ) : '';
  187. $select_location = mc_select_location( $cltype, $clvalue );
  188. $select_access = ( isset( $get['access'] ) ) ? mc_access_limit( $get['access'] ) : '';
  189. $select_published = mc_select_published();
  190. $search = mc_prepare_search_query( $search );
  191. $exclude_categories = mc_private_categories();
  192. $arr_events = array();
  193. $ts_string = mc_ts();
  194. /**
  195. * Set primary sort for getting events. Default 'occur_begin'.
  196. *
  197. * @hook mc_primary_sort
  198. *
  199. * @param {string} $primary_sort SQL sort column.
  200. * @param {string} $context Current function.
  201. *
  202. * @return {string}
  203. */
  204. $primary_sort = apply_filters( 'mc_primary_sort', 'occur_begin', 'my_calendar_get_events' );
  205. /**
  206. * Set secondary sort for getting events. Default 'event_title ASC'.
  207. *
  208. * @hook mc_secondary_sort
  209. *
  210. * @param {string} $secondary_sort SQL sort column.
  211. * @param {string} $context Current function.
  212. *
  213. * @return {string}
  214. */
  215. $secondary_sort = apply_filters( 'mc_secondary_sort', 'event_title ASC', 'my_calendar_get_events' );
  216. $location_join = ( $select_location ) ? 'JOIN (SELECT location_id FROM ' . my_calendar_locations_table() . " WHERE $select_location) l on e.event_location = l.location_id" : '';
  217. /**
  218. * Filter site parameter in queries on a multisite network. Allows a query to show events merged from multiple sites using a single shortcode.
  219. *
  220. * @hook mc_get_events_sites
  221. *
  222. * @param {array} $site Array of sites or a single site if displaying events from a different site on the network.
  223. * @param {array} $args Shortcode arguments.
  224. *
  225. * @return {array}
  226. */
  227. $site = apply_filters( 'mc_get_events_sites', $site, $args );
  228. $site = ! is_array( $site ) ? array( $site ) : $site;
  229. foreach ( $site as $s ) {
  230. $event_query = '
  231. SELECT *, ' . $ts_string . '
  232. FROM ' . my_calendar_event_table( $s ) . ' AS o
  233. JOIN ' . my_calendar_table( $s ) . ' AS e
  234. ON (event_id=occur_event_id)
  235. JOIN ' . my_calendar_categories_table( $s ) . " AS c
  236. ON (event_category=category_id)
  237. $join
  238. $location_join
  239. WHERE $select_published $select_category $select_author $select_host $select_access $search
  240. AND ( DATE(occur_begin) BETWEEN '$from 00:00:00' AND '$to 23:59:59'
  241. OR DATE(occur_end) BETWEEN '$from 00:00:00' AND '$to 23:59:59'
  242. OR ( DATE('$from') BETWEEN DATE(occur_begin) AND DATE(occur_end) )
  243. OR ( DATE('$to') BETWEEN DATE(occur_begin) AND DATE(occur_end) ) )
  244. $exclude_categories
  245. GROUP BY o.occur_id ORDER BY $primary_sort, $secondary_sort";
  246. $events = $mcdb->get_results( $event_query );
  247. if ( ! empty( $events ) ) {
  248. $cats = array();
  249. $locs = array();
  250. foreach ( array_keys( $events ) as $key ) {
  251. $event =& $events[ $key ];
  252. $event->site_id = $s;
  253. $object_id = $event->event_id;
  254. $location_id = $event->event_location;
  255. if ( ! isset( $cats[ $object_id ] ) ) {
  256. $categories = mc_get_categories( $event, 'objects' );
  257. $event->categories = $categories;
  258. $cats[ $object_id ] = $categories;
  259. } else {
  260. $event->categories = $cats[ $object_id ];
  261. }
  262. if ( 0 !== (int) $location_id ) {
  263. if ( ! isset( $locs[ $object_id ] ) ) {
  264. $location = mc_get_location( $location_id );
  265. $event->location = $location;
  266. $locs[ $object_id ] = $location;
  267. } else {
  268. $event->location = $locs[ $object_id ];
  269. }
  270. }
  271. $object = mc_event_object( $event );
  272. if ( false !== $object ) {
  273. $arr_events[] = $object;
  274. }
  275. }
  276. }
  277. }
  278. /**
  279. * Filter events returned by my_calendar_get_events queries. Function returns a range of events between a start and end date.
  280. *
  281. * @hook mc_filter_events
  282. *
  283. * @param {array} $arr_events Array of event objects.
  284. * @param {array} $args Event query arguments.
  285. * @param {string} $context Current function context.
  286. *
  287. * @return {array}
  288. */
  289. return apply_filters( 'mc_filter_events', $arr_events, $args, 'my_calendar_get_events' );
  290. }
  291. /**
  292. * Fetch events for upcoming events list. Not date based; fetches the nearest events regardless of date.
  293. *
  294. * @param array $args array of event limit parameters.
  295. *
  296. * @return array Set of matched events.
  297. */
  298. function mc_get_all_events( $args ) {
  299. $category = isset( $args['category'] ) ? $args['category'] : 'default';
  300. $before = isset( $args['before'] ) ? $args['before'] : 0;
  301. $after = isset( $args['after'] ) ? $args['after'] : 6;
  302. $author = isset( $args['author'] ) ? $args['author'] : 'default';
  303. $host = isset( $args['host'] ) ? $args['host'] : 'default';
  304. $ltype = isset( $args['ltype'] ) ? $args['ltype'] : '';
  305. $lvalue = isset( $args['lvalue'] ) ? $args['lvalue'] : '';
  306. $site = isset( $args['site'] ) ? $args['site'] : false;
  307. $search = isset( $args['search'] ) ? $args['search'] : '';
  308. $offset = intval( get_option( 'gmt_offset', 0 ) ) * 60 * 60;
  309. $time = isset( $args['time'] ) && '' !== $args['time'] ? strtotime( $args['time'] ) + $offset : 'now';
  310. $mcdb = mc_is_remote_db();
  311. $now = ( 'now' === $time ) ? 'NOW()' : $time;
  312. $now_limit = ( 'now' === $time ) ? 'NOW()' : "from_unixtime($time)";
  313. $exclude_categories = mc_private_categories();
  314. $cat_limit = ( 'default' !== $category ) ? mc_select_category( $category ) : array();
  315. $join = ( isset( $cat_limit[0] ) ) ? $cat_limit[0] : '';
  316. $select_category = ( isset( $cat_limit[1] ) ) ? $cat_limit[1] : '';
  317. $select_location = mc_select_location( $ltype, $lvalue );
  318. $location_join = ( $select_location ) ? 'JOIN (SELECT location_id FROM ' . my_calendar_locations_table() . " WHERE $select_location) l on e.event_location = l.location_id" : '';
  319. $select_access = ( isset( $_GET['access'] ) ) ? mc_access_limit( $_GET['access'] ) : '';
  320. $select_published = mc_select_published();
  321. $select_author = ( 'default' !== $author ) ? mc_select_author( $author ) : '';
  322. $select_host = ( 'default' !== $host ) ? mc_select_host( $host ) : '';
  323. $ts_string = mc_ts();
  324. $select_window = ( ! $before ) ? 'AND occur_begin > ' . $now_limit : '';
  325. $select_window = ( ! $after ) ? 'AND occur_end < ' . $now_limit : $select_window;
  326. $limit = "$select_published $select_category $select_author $select_host $select_access $search $select_window";
  327. // New Query style.
  328. $total = absint( $before ) + absint( $after ) + 30;
  329. $db_engine = mc_get_db_type();
  330. $ordering = ( 'sqlite' === $db_engine ) ? 'ABS( ( SELECT unixepoch() ) - ( SELECT unixepoch(occur_begin) ))' : 'ABS(TIMESTAMPDIFF(SECOND, ' . $now . ', occur_begin))';
  331. $events = $mcdb->get_results(
  332. 'SELECT *, ' . $ts_string . '
  333. FROM ' . my_calendar_event_table( $site ) . '
  334. JOIN ' . my_calendar_table( $site ) . " AS e
  335. ON (event_id=occur_event_id)
  336. $join
  337. $location_join
  338. JOIN " . my_calendar_categories_table( $site ) . " as c
  339. ON (e.event_category=c.category_id)
  340. WHERE $limit
  341. $exclude_categories
  342. ORDER BY $ordering ASC LIMIT 0,$total"
  343. );
  344. $cats = array();
  345. foreach ( array_keys( $events ) as $key ) {
  346. $event =& $events[ $key ];
  347. $event->site_id = $site;
  348. $object_id = $event->event_id;
  349. if ( ! isset( $fetched[ $object_id ] ) ) {
  350. $cats = mc_get_categories( $event, 'objects' );
  351. $event->categories = $cats;
  352. $fetched[ $object_id ] = $cats;
  353. } else {
  354. $event->categories = $fetched[ $object_id ];
  355. }
  356. $object = mc_event_object( $event );
  357. if ( false !== $object ) {
  358. $events[ $key ] = $object;
  359. }
  360. }
  361. /**
  362. * Filter events returned by mc_get_all_events queries. Function returns a range of events based on proximity to the current date using parameters for number of days/events before or after today.
  363. *
  364. * @hook mc_filter_events
  365. *
  366. * @param {array} $events Array of event objects.
  367. * @param {array} $args Event query arguments.
  368. * @param {string} $context Current function context.
  369. *
  370. * @return {array}
  371. */
  372. return apply_filters( 'mc_filter_events', $events, $args, 'mc_get_all_events' );
  373. }
  374. /**
  375. * Fetch only the defined holiday category
  376. *
  377. * @param int $before Number of events before.
  378. * @param int $after Number of events after.
  379. * @param string $time Time/date to center collection on.
  380. *
  381. * @return array events
  382. */
  383. function mc_get_all_holidays( $before, $after, $time = '' ) {
  384. if ( ! mc_get_option( 'skip_holidays_category' ) ) {
  385. return array();
  386. } else {
  387. $category = absint( mc_get_option( 'skip_holidays_category' ) );
  388. $args = array(
  389. 'category' => $category,
  390. 'before' => $before,
  391. 'after' => $after,
  392. 'time' => $time,
  393. );
  394. return mc_get_all_events( $args );
  395. }
  396. }
  397. /**
  398. * Get most recently added events.
  399. *
  400. * @param integer|false $cat_id Category ID.
  401. *
  402. * @return array Event objects, limited by category if category ID passed.
  403. */
  404. function mc_get_new_events( $cat_id = false ) {
  405. $mcdb = mc_is_remote_db();
  406. $ts_string = mc_ts();
  407. $public = implode( ',', mc_event_states_by_type( 'public' ) );
  408. if ( $cat_id ) {
  409. $cat = "WHERE event_category = $cat_id AND event_approved IN (' . $public . ') AND event_flagged <> 1";
  410. } else {
  411. $cat = 'WHERE event_approved IN (' . $public . ') AND event_flagged <> 1';
  412. }
  413. $exclude_categories = mc_private_categories();
  414. /**
  415. * Filter how many days of newly added events will be included in ICS subscription links.
  416. *
  417. * @hook mc_rss_feed_date_range
  418. *
  419. * @param {int} $limit Number of days. Default 7.
  420. *
  421. * @return {int}
  422. */
  423. $limit = apply_filters( 'mc_rss_feed_date_range', 7 );
  424. $events = $mcdb->get_results(
  425. 'SELECT *, ' . $ts_string . '
  426. FROM ' . my_calendar_event_table() . '
  427. JOIN ' . my_calendar_table() . ' ON (event_id=occur_event_id)
  428. JOIN ' . my_calendar_categories_table() . " AS c ON (event_category=category_id) $cat
  429. AND event_added > NOW() - INTERVAL $limit DAY
  430. $exclude_categories
  431. ORDER BY event_added DESC"
  432. );
  433. if ( empty( $events ) ) {
  434. $events = $mcdb->get_results(
  435. 'SELECT *, ' . $ts_string . '
  436. FROM ' . my_calendar_event_table() . '
  437. JOIN ' . my_calendar_table() . ' ON (event_id=occur_event_id)
  438. JOIN ' . my_calendar_categories_table() . " AS c ON (event_category=category_id) $cat
  439. $exclude_categories
  440. ORDER BY event_added DESC LIMIT 0,30"
  441. );
  442. }
  443. $groups = array();
  444. $output = array();
  445. foreach ( array_keys( $events ) as $key ) {
  446. $event =& $events[ $key ];
  447. if ( ! in_array( $event->occur_group_id, $groups, true ) ) {
  448. $output[ $event->event_begin ][] = $event;
  449. }
  450. if ( 1 === (int) $event->event_span ) {
  451. $groups[] = $event->occur_group_id;
  452. }
  453. }
  454. return $output;
  455. }
  456. /**
  457. * Get all existing instances of an ID. Assemble into array with dates as keys
  458. *
  459. * @param int $id Event ID.
  460. *
  461. * @return array of event dates & instance IDs
  462. */
  463. function mc_get_instances( $id ) {
  464. global $wpdb;
  465. $id = (int) $id;
  466. $results = $wpdb->get_results( $wpdb->prepare( 'SELECT occur_id, occur_begin FROM ' . my_calendar_event_table() . ' WHERE occur_event_id = %d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  467. $return = array();
  468. foreach ( $results as $result ) {
  469. $key = sanitize_key( mc_date( 'Y-m-d H:i:s', strtotime( $result->occur_begin ), false ) );
  470. $return[ $key ] = $result->occur_id;
  471. }
  472. return $return;
  473. }
  474. /**
  475. * Fetch results of an event search.
  476. *
  477. * @param array|string $search array (PRO) or string (Simple).
  478. * @param string $time A date/time string used as the baseline for relative date results. Optional.
  479. *
  480. * @return array of event objects
  481. */
  482. function mc_get_search_results( $search, $time = '' ) {
  483. /**
  484. * Filter number of past search results to return. Default 0.
  485. *
  486. * @hook mc_past_search_results
  487. *
  488. * @param {int} $before Number of results.
  489. *
  490. * @return {int}
  491. */
  492. $before = apply_filters( 'mc_past_search_results', 0 );
  493. /**
  494. * Filter number of future search results to return. Default 15.
  495. *
  496. * @hook mc_future_search_results
  497. *
  498. * @param {int} $after Number of results.
  499. *
  500. * @return {int}
  501. */
  502. $after = apply_filters( 'mc_future_search_results', 15 );
  503. if ( is_array( $search ) ) {
  504. // If from & to are set, we need to use a date-based event query.
  505. $from = mc_checkdate( $search['from'] );
  506. $to = mc_checkdate( $search['to'] );
  507. $category = ( isset( $search['category'] ) ) ? $search['category'] : null;
  508. $ltype = ( isset( $search['ltype'] ) ) ? $search['ltype'] : null;
  509. $lvalue = ( isset( $search['lvalue'] ) ) ? $search['lvalue'] : null;
  510. $author = ( isset( $search['author'] ) ) ? $search['author'] : null;
  511. $host = ( isset( $search['host'] ) ) ? $search['host'] : null;
  512. $search = ( isset( $search['search'] ) ) ? $search['search'] : '';
  513. $args = array(
  514. 'from' => $from,
  515. 'to' => $to,
  516. 'category' => $category,
  517. 'ltype' => $ltype,
  518. 'lvalue' => $lvalue,
  519. 'author' => $author,
  520. 'host' => $host,
  521. 'search' => $search,
  522. 'source' => 'search',
  523. );
  524. /**
  525. * Filter advanced search query arguments.
  526. *
  527. * @hook mc_search_attributes
  528. *
  529. * @param {array} $args Search query arguments.
  530. * @param {string} $search Search term.
  531. *
  532. * @return {array}
  533. */
  534. $args = apply_filters( 'mc_search_attributes', $args, $search );
  535. $event_array = my_calendar_events( $args );
  536. } else {
  537. // If not, we use relational event queries.
  538. $args = array(
  539. 'before' => $before,
  540. 'after' => $after,
  541. 'search' => $search,
  542. );
  543. if ( $time ) {
  544. $args['time'] = $time;
  545. }
  546. $arr_events = mc_get_all_events( $args );
  547. $holidays = mc_get_all_holidays( $before, $after );
  548. $holiday_array = mc_set_date_array( $holidays );
  549. if ( is_array( $arr_events ) && ! empty( $arr_events ) ) {
  550. $event_array = mc_set_date_array( $arr_events );
  551. if ( is_array( $holidays ) && count( $holidays ) > 0 ) {
  552. $event_array = mc_holiday_limit( $event_array, $holiday_array ); // if there are holidays, rejigger.
  553. }
  554. }
  555. }
  556. /**
  557. * Filter search events.
  558. *
  559. * @hook mc_searched_events
  560. *
  561. * @param {array} $event_array Array of found event objects.
  562. * @param {array} $args Search query arguments.
  563. *
  564. * @return {array}
  565. */
  566. return (array) apply_filters( 'mc_searched_events', $event_array, $args );
  567. }
  568. /**
  569. * Get event basic info
  570. *
  571. * @param int $id Event ID in my_calendar db.
  572. * @param boolean $rebuild Get core data only if doing an event rebuild.
  573. *
  574. * @return object|false My Calendar Event
  575. */
  576. function mc_get_event_core( $id, $rebuild = false ) {
  577. if ( ! is_numeric( $id ) ) {
  578. return false;
  579. }
  580. $mcdb = mc_is_remote_db();
  581. $ts_string = mc_ts();
  582. if ( $rebuild ) {
  583. $event = $mcdb->get_row( $mcdb->prepare( 'SELECT * FROM ' . my_calendar_table() . ' JOIN ' . my_calendar_categories_table() . ' ON (event_category=category_id) WHERE event_id=%d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  584. } else {
  585. $event = $mcdb->get_row( $mcdb->prepare( 'SELECT *, ' . $ts_string . ' FROM ' . my_calendar_event_table() . ' JOIN ' . my_calendar_table() . ' ON (event_id=occur_event_id) JOIN ' . my_calendar_categories_table() . ' ON (event_category=category_id) WHERE event_id = %d ORDER BY occur_id ASC LIMIT 1', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  586. $event = mc_event_object( $event );
  587. }
  588. return $event;
  589. }
  590. /**
  591. * Fetches the first fully-realized event object with all parameters even if the specific instance ID isn't available.
  592. *
  593. * @param int $id Event core ID.
  594. *
  595. * @return object|boolean Event or false if no event found.
  596. */
  597. function mc_get_first_event( $id ) {
  598. $mcdb = mc_is_remote_db();
  599. $ts_string = mc_ts();
  600. $event = ( ! is_admin() ) ? get_transient( 'mc_first_event_cache_' . $id ) : false;
  601. if ( $event ) {
  602. return $event;
  603. } else {
  604. $event = $mcdb->get_row( $mcdb->prepare( 'SELECT *, ' . $ts_string . 'FROM ' . my_calendar_event_table() . ' JOIN ' . my_calendar_table() . ' ON (event_id=occur_event_id) JOIN ' . my_calendar_categories_table() . ' ON (event_category=category_id) WHERE occur_event_id=%d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  605. if ( $event ) {
  606. $event = mc_event_object( $event );
  607. set_transient( 'mc_first_event_cache_' . $id, $event, WEEK_IN_SECONDS );
  608. } else {
  609. $event = false;
  610. }
  611. }
  612. return $event;
  613. }
  614. /**
  615. * Get the instance-specific information about a single event instance.
  616. *
  617. * @param int $instance_id Event instance ID.
  618. *
  619. * @return object|null query result
  620. */
  621. function mc_get_instance_data( $instance_id ) {
  622. $mcdb = mc_is_remote_db();
  623. $result = $mcdb->get_row( $mcdb->prepare( 'SELECT * FROM ' . my_calendar_event_table() . ' WHERE occur_id = %d', $instance_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  624. return $result;
  625. }
  626. /**
  627. * Fetch the instance of an event closest to today.
  628. *
  629. * @param int $id Event core ID.
  630. * @param bool $next If true, return closest event that's in the future. If false, return the event object for $id.
  631. *
  632. * @return object Event
  633. */
  634. function mc_get_nearest_event( $id, $next = false ) {
  635. $next_event = false;
  636. $mcdb = mc_is_remote_db();
  637. $ts_string = mc_ts();
  638. $event = $mcdb->get_row( $mcdb->prepare( 'SELECT *, ' . $ts_string . ' FROM ' . my_calendar_event_table() . ' JOIN ' . my_calendar_table() . ' ON (event_id=occur_event_id) JOIN ' . my_calendar_categories_table() . ' ON (event_category=category_id) WHERE occur_event_id=%d ORDER BY ABS( DATEDIFF( occur_begin, NOW() ) )', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  639. if ( true === $next ) {
  640. $next_event = $mcdb->get_row( $mcdb->prepare( 'SELECT *, ' . $ts_string . ' FROM ' . my_calendar_event_table() . ' JOIN ' . my_calendar_table() . ' ON (event_id=occur_event_id) JOIN ' . my_calendar_categories_table() . ' ON (event_category=category_id) WHERE occur_event_id=%d AND occur_begin > NOW() ORDER BY ABS( DATEDIFF( occur_begin, NOW() ) )', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  641. }
  642. $event = ( $next_event ) ? mc_event_object( $next_event ) : mc_event_object( $event );
  643. return $event;
  644. }
  645. /**
  646. * Returns the event object for a specific instance of an event. Use mc_get_first_event() if the instance ID is unknown.
  647. *
  648. * @param int $id Event instance ID.
  649. * @param string $type 'object' or 'html'.
  650. *
  651. * @return object|string
  652. */
  653. function mc_get_event( $id, $type = 'object' ) {
  654. if ( ! is_numeric( $id ) ) {
  655. return false;
  656. }
  657. $ts_string = mc_ts();
  658. $mcdb = mc_is_remote_db();
  659. $event = $mcdb->get_row( $mcdb->prepare( 'SELECT *, ' . $ts_string . ' FROM ' . my_calendar_event_table() . ' JOIN ' . my_calendar_table() . ' ON (event_id=occur_event_id) JOIN ' . my_calendar_categories_table() . ' ON (event_category=category_id) WHERE occur_id=%d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  660. if ( 'object' === $type ) {
  661. $event = mc_event_object( $event );
  662. return $event;
  663. } else {
  664. $date = mc_date( 'Y-m-d', strtotime( $event->occur_begin ), false );
  665. $time = mc_date( 'H:i:s', strtotime( $event->occur_begin ), false );
  666. $event_output = my_calendar_draw_event( $event, 'single', $date, $time, 'single' );
  667. $value = '<div id="mc_event">' . $event_output['html'] . '</div>';
  668. return $value;
  669. }
  670. }
  671. /**
  672. * Get a single data field from an event.
  673. *
  674. * @param string $field database column.
  675. * @param int $id Event core ID.
  676. *
  677. * @return mixed string/integer value
  678. */
  679. function mc_get_data( $field, $id ) {
  680. $mcdb = mc_is_remote_db();
  681. $result = $mcdb->get_var( $mcdb->prepare( "SELECT $field FROM " . my_calendar_table() . ' WHERE event_id = %d', $id ) );
  682. return $result;
  683. }
  684. /**
  685. * Fetch all events according to date parameters and supported limits.
  686. *
  687. * @since 2.3.0
  688. *
  689. * @param array $args array of My Calendar display & limit parameters.
  690. *
  691. * @return array Array of event objects with dates as keys.
  692. */
  693. function my_calendar_events( $args ) {
  694. /**
  695. * Filter calendar event query arguments.
  696. *
  697. * @hook my_calendar_events_args
  698. *
  699. * @param {array} $args Array of arguments for display and limiting of events.
  700. *
  701. * @return {array}
  702. */
  703. $args = apply_filters( 'my_calendar_events_args', $args );
  704. $events = my_calendar_get_events( $args );
  705. $event_array = array();
  706. $holiday_array = array();
  707. $holidays = array();
  708. // Get holidays to filter out.
  709. if ( mc_get_option( 'skip_holidays_category' ) ) {
  710. $args['category'] = mc_get_option( 'skip_holidays_category' );
  711. $args['holidays'] = 'holidays';
  712. $holidays = my_calendar_get_events( $args );
  713. $holiday_array = mc_set_date_array( $holidays );
  714. }
  715. // Get events into an easily parseable set, keyed by date.
  716. if ( is_array( $events ) && ! empty( $events ) ) {
  717. $event_array = mc_set_date_array( $events );
  718. if ( is_array( $holidays ) && count( $holidays ) > 0 ) {
  719. $event_array = mc_holiday_limit( $event_array, $holiday_array ); // if there are holidays, rejigger.
  720. }
  721. }
  722. return $event_array;
  723. }
  724. /**
  725. * Get one event currently happening.
  726. *
  727. * @param string|integer $category category ID or 'default'.
  728. * @param string $template display Template.
  729. * @param integer|string|bool $site Site ID if fetching events from a different multisite instance.
  730. *
  731. * @return string output HTML
  732. */
  733. function my_calendar_events_now( $category = 'default', $template = '<strong>{link_title}</strong> {timerange}', $site = false ) {
  734. if ( $site ) {
  735. $site = ( 'global' === $site ) ? BLOG_ID_CURRENT_SITE : $site;
  736. switch_to_blog( $site );
  737. }
  738. $mcdb = mc_is_remote_db();
  739. $arr_events = array();
  740. $select_published = mc_select_published();
  741. $cat_limit = ( 'default' !== $category ) ? mc_select_category( $category ) : array();
  742. $join = ( isset( $cat_limit[0] ) ) ? $cat_limit[0] : '';
  743. $select_category = ( isset( $cat_limit[1] ) ) ? $cat_limit[1] : '';
  744. $exclude_categories = mc_private_categories();
  745. $ts_string = mc_ts();
  746. // May add support for location/author/host later.
  747. $select_location = '';
  748. $select_author = '';
  749. $select_host = '';
  750. /**
  751. * Set primary sort for getting today's events. Default 'occur_begin'.
  752. *
  753. * @hook mc_primary_sort
  754. *
  755. * @param {string} $primary_sort SQL sort column.
  756. * @param {string} $context Current function.
  757. *
  758. * @return {string}
  759. */
  760. $primary_sort = apply_filters( 'mc_primary_sort', 'occur_begin', 'my_calendar_events_now' );
  761. /**
  762. * Set secondary sort for getting today's events. Default 'event_title ASC'.
  763. *
  764. * @hook mc_secondary_sort
  765. *
  766. * @param {string} $secondary_sort SQL sort column.
  767. * @param {string} $context Current function.
  768. *
  769. * @return {string}
  770. */
  771. $secondary_sort = apply_filters( 'mc_secondary_sort', 'event_title ASC', 'my_calendar_events_now' );
  772. $now = current_time( 'Y-m-d H:i:s' );
  773. $event_query = 'SELECT *, ' . $ts_string . '
  774. FROM ' . my_calendar_event_table( $site ) . ' AS o
  775. JOIN ' . my_calendar_table( $site ) . " AS e
  776. ON (event_id=occur_event_id)
  777. $join
  778. JOIN " . my_calendar_categories_table( $site ) . " AS c
  779. ON (event_category=category_id)
  780. WHERE $select_published $select_category $select_location $select_author $select_host
  781. $exclude_categories
  782. AND ( CAST('$now' AS DATETIME) BETWEEN occur_begin AND occur_end )
  783. ORDER BY $primary_sort, $secondary_sort";
  784. $events = $mcdb->get_results( $event_query );
  785. if ( ! empty( $events ) ) {
  786. foreach ( array_keys( $events ) as $key ) {
  787. $event =& $events[ $key ];
  788. $arr_events[] = $event;
  789. }
  790. }
  791. if ( ! empty( $arr_events ) ) {
  792. $event = mc_create_tags( $arr_events[0] );
  793. if ( mc_key_exists( $template ) ) {
  794. $template = mc_get_custom_template( $template );
  795. }
  796. $args = array(
  797. 'event' => $arr_events[0],
  798. 'tags' => $event,
  799. 'template' => $template,
  800. 'class' => ( str_contains( $template, 'list_preset_' ) ) ? "list-preset $template" : '',
  801. );
  802. $details = mc_load_template( 'event/now', $args );
  803. if ( $details ) {
  804. $return = $details;
  805. } else {
  806. /**
  807. * Customize the template used to draw the "happening now" shortcode output.
  808. *
  809. * @hook mc_happening_next_template
  810. *
  811. * @param {string} $template HTML and template tags.
  812. * @param {object} $event Event object to draw.
  813. *
  814. * @return {string}
  815. */
  816. $output = mc_draw_template( $event, apply_filters( 'mc_happening_now_template', $template, $event ) );
  817. $return = mc_run_shortcodes( $output );
  818. }
  819. $return = '<div class="mc-event-list">' . $return . '</div>';
  820. } else {
  821. $return = '';
  822. }
  823. if ( $site ) {
  824. restore_current_blog();
  825. }
  826. return $return;
  827. }
  828. /**
  829. * Get the next scheduled event, not currently happening.
  830. *
  831. * @param string|integer $category category ID or 'default'.
  832. * @param string $template display Template.
  833. * @param integer $skip Number of events to skip.
  834. * @param integer|string|bool $site Site ID if fetching events from a different multisite instance.
  835. *
  836. * @return string output HTML
  837. */
  838. function my_calendar_events_next( $category = 'default', $template = '<strong>{link_title}</strong> {timerange}', $skip = 0, $site = false ) {
  839. if ( $site ) {
  840. $site = ( 'global' === $site ) ? BLOG_ID_CURRENT_SITE : $site;
  841. switch_to_blog( $site );
  842. }
  843. $mcdb = mc_is_remote_db();
  844. $arr_events = array();
  845. $select_published = mc_select_published();
  846. $cat_limit = ( 'default' !== $category ) ? mc_select_category( $category ) : array();
  847. $join = ( isset( $cat_limit[0] ) ) ? $cat_limit[0] : '';
  848. $select_category = ( isset( $cat_limit[1] ) ) ? $cat_limit[1] : '';
  849. $exclude_categories = mc_private_categories();
  850. $ts_string = mc_ts();
  851. // May add support for location/author/host later.
  852. $select_location = '';
  853. $select_author = '';
  854. $select_host = '';
  855. $now = current_time( 'Y-m-d H:i:s' );
  856. $event_query = 'SELECT *, ' . $ts_string . '
  857. FROM ' . my_calendar_event_table( $site ) . '
  858. JOIN ' . my_calendar_table( $site ) . " AS e
  859. ON (event_id=occur_event_id)
  860. $join
  861. JOIN " . my_calendar_categories_table( $site ) . " as c
  862. ON (e.event_category=c.category_id)
  863. WHERE $select_published $select_category $select_location $select_author $select_host
  864. $exclude_categories
  865. AND DATE(occur_begin) > CAST('$now' as DATETIME) ORDER BY occur_begin LIMIT $skip,1";
  866. $events = $mcdb->get_results( $event_query );
  867. if ( ! empty( $events ) ) {
  868. foreach ( array_keys( $events ) as $key ) {
  869. $event =& $events[ $key ];
  870. $arr_events[] = $event;
  871. }
  872. }
  873. if ( ! empty( $arr_events ) ) {
  874. $event = mc_create_tags( $arr_events[0] );
  875. if ( mc_key_exists( $template ) ) {
  876. $template = mc_get_custom_template( $template );
  877. }
  878. $args = array(
  879. 'event' => $arr_events[0],
  880. 'tags' => $event,
  881. 'template' => $template,
  882. 'class' => ( str_contains( $template, 'list_preset_' ) ) ? "list-preset $template" : '',
  883. );
  884. $details = mc_load_template( 'event/next', $args );
  885. if ( $details ) {
  886. $return = $details;
  887. } else {
  888. /**
  889. * Customize the template used to draw the next event shortcode output.
  890. *
  891. * @hook mc_happening_next_template
  892. *
  893. * @param {string} $template HTML and template tags.
  894. * @param {object} $event Event object to draw.
  895. *
  896. * @return {string}
  897. */
  898. $output = mc_draw_template( $event, apply_filters( 'mc_happening_next_template', $template, $event ) );
  899. $return = mc_run_shortcodes( $output );
  900. }
  901. $return = '<div class="mc-event-list">' . $return . '</div>';
  902. } else {
  903. $return = '';
  904. }
  905. if ( $site ) {
  906. restore_current_blog();
  907. }
  908. return $return;
  909. }
  910. /**
  911. * Get all occurrences associated with an event.
  912. *
  913. * @param int $id Event ID.
  914. *
  915. * @return array of objects with instance and event IDs.
  916. */
  917. function mc_get_occurrences( $id ) {
  918. $mcdb = mc_is_remote_db();
  919. $id = absint( $id );
  920. if ( 0 === $id ) {
  921. return array();
  922. }
  923. $results = $mcdb->get_results( $mcdb->prepare( 'SELECT occur_id, occur_event_id FROM ' . my_calendar_event_table() . ' WHERE occur_event_id=%d ORDER BY occur_begin ASC', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  924. return $results;
  925. }
  926. /**
  927. * Return all instances of a given event.
  928. *
  929. * @param array $args Arguments describing the output type.
  930. *
  931. * @return string HTML list of instance data & single event view
  932. */
  933. function mc_instance_list( $args ) {
  934. $id = isset( $args['event'] ) ? $args['event'] : false;
  935. if ( ! $id ) {
  936. return '';
  937. }
  938. $template = isset( $args['template'] ) ? $args['template'] : '<h3>{title}</h3>{description}';
  939. $list = isset( $args['list'] ) ? $args['list'] : '<li>{date}, {time}</li>';
  940. $before = isset( $args['before'] ) ? $args['before'] : '<ul>';
  941. $after = isset( $args['after'] ) ? $args['after'] : '</ul>';
  942. $instance = isset( $args['instance'] ) ? $args['instance'] : false;
  943. global $wpdb;
  944. $output = '';
  945. if ( true === $instance || '1' === $instance ) {
  946. $sql = 'SELECT * FROM ' . my_calendar_event_table() . ' WHERE occur_id=%d ORDER BY occur_begin ASC';
  947. } else {
  948. $sql = 'SELECT * FROM ' . my_calendar_event_table() . ' WHERE occur_event_id=%d ORDER BY occur_begin ASC';
  949. }
  950. $results = $wpdb->get_results( $wpdb->prepare( $sql, $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  951. if ( is_array( $results ) ) {
  952. $details = '';
  953. foreach ( $results as $result ) {
  954. $event_id = $result->occur_id;
  955. $event = mc_get_event( $event_id );
  956. $array = mc_create_tags( $event );
  957. if ( in_array( $template, array( 'details', 'grid', 'list', 'mini', 'card' ), true ) || mc_key_exists( $template ) ) {
  958. if ( 1 === (int) mc_get_option( 'use_' . $template . '_template' ) ) {
  959. $template = mc_get_template( $template );
  960. } elseif ( mc_key_exists( $template ) ) {
  961. $template = mc_get_custom_template( $template );
  962. } else {
  963. $details = my_calendar_draw_event( $event, 'single', $event->event_begin, $event->event_time, '', '', $array );
  964. }
  965. }
  966. $item = ( '' !== $list ) ? mc_draw_template( $array, $list ) : '';
  967. if ( '' === $details ) {
  968. $details = ( '' !== $template ) ? mc_draw_template( $array, $template ) : '';
  969. }
  970. $output .= $item;
  971. if ( '' === $list ) {
  972. break;
  973. }
  974. }
  975. $output = $details . $before . $output . $after;
  976. }
  977. return mc_run_shortcodes( $output );
  978. }
  979. /**
  980. * Generate a list of instances for the currently edited event
  981. *
  982. * @param int $id Event ID.
  983. * @param int $occur Specific occurrence ID.
  984. *
  985. * @return bool|string
  986. */
  987. function mc_admin_instances( $id, $occur = 0 ) {
  988. global $wpdb;
  989. $output = '';
  990. $ts_string = mc_ts();
  991. $results = $wpdb->get_results( $wpdb->prepare( 'SELECT *, ' . $ts_string . ' FROM ' . my_calendar_event_table() . ' WHERE occur_event_id=%d ORDER BY occur_begin ASC', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  992. if ( empty( $results ) ) {
  993. return false;
  994. }
  995. $count = count( $results );
  996. if ( is_array( $results ) && is_admin() ) {
  997. foreach ( $results as $result ) {
  998. $start = $result->ts_occur_begin;
  999. $end = $result->ts_occur_end;
  1000. if ( ( ( $end + 1 ) - $start ) === DAY_IN_SECONDS || ( $end - $start ) === DAY_IN_SECONDS ) {
  1001. $time = '';
  1002. } elseif ( ( $end - $start ) <= HOUR_IN_SECONDS ) {
  1003. $time = mc_date( mc_time_format(), $start );
  1004. } else {
  1005. $time = mc_date( mc_time_format(), $start ) . ' - ' . mc_date( mc_time_format(), $end );
  1006. }
  1007. // Omitting format from mc_date() returns timestamp.
  1008. $date = date_i18n( mc_date_format(), mc_date( '', $start ) );
  1009. $date = "<span id='occur_date_$result->occur_id'><strong>" . $date . '</strong><br />' . $time . '</span>';
  1010. $class = ( my_calendar_date_xcomp( mc_date( 'Y-m-d H:i:00', $start ), mc_date( 'Y-m-d H:i:00', time() ) ) ) ? 'past-event' : 'future-event';
  1011. if ( (int) $result->occur_id === (int) $occur || 1 === $count ) {
  1012. $control = '';
  1013. $control .= "<p>$date</p><p><em>" . __( 'Editing Now', 'my-calendar' ) . '</em></p>';
  1014. $class = 'current-event';
  1015. } else {
  1016. $control = "<p>$date</p>";
  1017. $control .= "<p class='instance-buttons'>";
  1018. $control .= "<button class='button delete_occurrence' type='button' data-event='$result->occur_event_id' data-begin='$result->occur_begin' data-end='$result->occur_end' data-value='$result->occur_id' aria-describedby='occur_date_$result->occur_id' />" . __( 'Delete', 'my-calendar' ) . '</button> ';
  1019. $control .= "<button class='button edit_occurrence' type='button' data-event='$result->occur_event_id' data-begin='$result->occur_begin' data-end='$result->occur_end' data-value='$result->occur_id' aria-describedby='occur_date_$result->occur_id' />" . __( 'Edit Date', 'my-calendar' ) . '</button>';
  1020. $control .= '</p><p>';
  1021. $control .= "<a href='" . admin_url( 'admin.php?page=my-calendar' ) . "&amp;mode=edit&amp;event_id=$id&amp;date=$result->occur_id' aria-describedby='occur_date_$result->occur_id'>" . __( 'Edit Details', 'my-calendar' ) . '</a>';
  1022. $control .= '</p>';
  1023. }
  1024. $output .= "<li class='$class'>$control</li>";
  1025. }
  1026. }
  1027. return $output;
  1028. }
  1029. /**
  1030. * Get all events with a grouped relationship with the current event.
  1031. *
  1032. * @param int $id Group ID.
  1033. *
  1034. * @return array Array event IDs of grouped events
  1035. */
  1036. function mc_get_grouped_events( $id ) {
  1037. global $wpdb;
  1038. $id = (int) $id;
  1039. if ( 0 === $id ) {
  1040. return array();
  1041. }
  1042. $results = $wpdb->get_results( $wpdb->prepare( 'SELECT event_id FROM ' . my_calendar_table() . ' WHERE event_group_id=%d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  1043. return $results;
  1044. }
  1045. /**
  1046. * Get the events adjacent to the currently displayed event.
  1047. *
  1048. * @param integer $mc_id ID of current event.
  1049. * @param string $adjacent Next/Previous.
  1050. * @param array $args Additional arguments to pass to query and return.
  1051. *
  1052. * @return array|object Event template array or object.
  1053. */
  1054. function mc_adjacent_event( $mc_id, $adjacent = 'previous', $args = array() ) {
  1055. $mcdb = mc_is_remote_db();
  1056. $adjacence = ( 'next' === $adjacent ) ? '>' : '<';
  1057. $order = ( 'next' === $adjacent ) ? 'ASC' : 'DESC';
  1058. $site = false;
  1059. $arr_events = array();
  1060. $select_published = mc_select_published();
  1061. $exclude_categories = mc_private_categories();
  1062. $category = ( isset( $args['category'] ) ) ? $args['category'] : 'default';
  1063. $ltype = ( isset( $args['ltype'] ) ) ? $args['ltype'] : '';
  1064. $lvalue = ( isset( $args['lvalue'] ) ) ? $args['lvalue'] : '';
  1065. $author = ( isset( $args['author'] ) ) ? $args['author'] : '';
  1066. $host = ( isset( $args['host'] ) ) ? $args['host'] : '';
  1067. $cat_limit = ( 'default' !== $category ) ? mc_select_category( $category ) : array();
  1068. $join = ( isset( $cat_limit[0] ) ) ? $cat_limit[0] : '';
  1069. $select_category = ( isset( $cat_limit[1] ) ) ? $cat_limit[1] : '';
  1070. $select_location = mc_select_location( $ltype, $lvalue );
  1071. $location_join = ( $select_location ) ? 'JOIN (SELECT location_id FROM ' . my_calendar_locations_table() . " WHERE $select_location) l on e.event_location = l.location_id" : '';
  1072. $select_author = ( 'default' !== $author ) ? mc_select_author( $author ) : '';
  1073. $select_host = ( 'default' !== $host ) ? mc_select_host( $host ) : '';
  1074. $ts_string = mc_ts();
  1075. $source = mc_get_event( $mc_id );
  1076. $return = array();
  1077. $offset = ( isset( $args['offset'] ) ) ? absint( $args['offset'] ) : 0;
  1078. if ( is_object( $source ) ) {
  1079. $date = mc_date( 'Y-m-d H:i:s', strtotime( $source->occur_begin ), false );
  1080. $event_query = 'SELECT *, ' . $ts_string . '
  1081. FROM ' . my_calendar_event_table( $site ) . '
  1082. JOIN ' . my_calendar_table( $site ) . ' AS e
  1083. ON (event_id=occur_event_id)
  1084. JOIN ' . my_calendar_categories_table( $site ) . " as c
  1085. ON (e.event_category=c.category_id)
  1086. $join
  1087. $location_join
  1088. WHERE $select_published $select_category $select_author $select_host $exclude_categories
  1089. AND occur_begin $adjacence CAST('$date' as DATETIME) ORDER BY occur_begin $order LIMIT $offset,1";
  1090. $events = $mcdb->get_results( $event_query );
  1091. if ( ! empty( $events ) ) {
  1092. foreach ( array_keys( $events ) as $key ) {
  1093. $event =& $events[ $key ];
  1094. $arr_events[] = $event;
  1095. }
  1096. }
  1097. if ( ! empty( $arr_events ) ) {
  1098. $return = ( isset( $args['return'] ) && 'object' === $args['return'] ) ? $arr_events[0] : mc_create_tags( $arr_events[0] );
  1099. } else {
  1100. $return = array();
  1101. }
  1102. }
  1103. return $return;
  1104. }
  1105. /**
  1106. * Remove non-holiday events from data if a holiday is present.
  1107. *
  1108. * @param array $events Array of event objects.
  1109. * @param array $holidays Array of event objects.
  1110. *
  1111. * @return array Array of event objects with conflicts removed.
  1112. */
  1113. function mc_holiday_limit( $events, $holidays ) {
  1114. foreach ( array_keys( $events ) as $key ) {
  1115. if ( ! empty( $holidays[ $key ] ) ) {
  1116. foreach ( $events[ $key ] as $k => $event ) {
  1117. if ( (int) mc_get_option( 'skip_holidays_category' ) !== (int) $event->event_category && 1 === (int) $event->event_holiday ) {
  1118. unset( $events[ $key ][ $k ] );
  1119. }
  1120. }
  1121. }
  1122. }
  1123. return $events;
  1124. }
  1125. /**
  1126. * For date-based views, manipulate array to be organized by dates
  1127. *
  1128. * @param array $events Array of event objects returned by query.
  1129. *
  1130. * @return array $events indexed by date
  1131. */
  1132. function mc_set_date_array( $events ) {
  1133. $event_array = array();
  1134. if ( is_array( $events ) && ! empty( $events ) ) {
  1135. foreach ( $events as $event ) {
  1136. $date = mc_date( 'Y-m-d', strtotime( $event->occur_begin ), false );
  1137. $end = mc_date( 'Y-m-d', strtotime( $event->occur_end ), false );
  1138. if ( $date !== $end ) {
  1139. $start = strtotime( $date );
  1140. $end = strtotime( $end );
  1141. do {
  1142. $date = mc_date( 'Y-m-d', $start, false );
  1143. $event_array[ $date ][] = $event;
  1144. $start = strtotime( '+1 day', $start );
  1145. } while ( $start <= $end );
  1146. } else {
  1147. $event_array[ $date ][] = $event;
  1148. }
  1149. }
  1150. }
  1151. return $event_array;
  1152. }
  1153. /**
  1154. * Verify that a given occurrence ID is valid.
  1155. *
  1156. * @param int $mc_id Occurrence ID.
  1157. *
  1158. * @return boolean|int Returns event ID on valid value.
  1159. */
  1160. function mc_valid_id( $mc_id ) {
  1161. $mcdb = mc_is_remote_db();
  1162. $result = $mcdb->get_var( $mcdb->prepare( 'SELECT occur_event_id FROM ' . my_calendar_event_table() . ' WHERE occur_id = %d', $mc_id ) );
  1163. if ( null !== $result ) {
  1164. return $result;
  1165. }
  1166. return false;
  1167. }
  1168. /**
  1169. * Get post associated with a given My Calendar event
  1170. *
  1171. * @param int $event_id Event ID.
  1172. *
  1173. * @return mixed int/boolean post ID if found; else false
  1174. */
  1175. function mc_get_event_post( $event_id ) {
  1176. $event = mc_get_first_event( $event_id );
  1177. if ( is_object( $event ) ) {
  1178. if ( property_exists( $event, 'event_post' ) && get_post_status( $event->event_post ) ) {
  1179. return $event->event_post;
  1180. }
  1181. }
  1182. return false;
  1183. }
  1184. /**
  1185. * Check the type of database, so handling of search queries is correct.
  1186. *
  1187. * @return string type of database engine in use;
  1188. */
  1189. function mc_get_db_type() {
  1190. // If running sqlite, return that.
  1191. $db_engine = defined( 'DB_ENGINE' ) && 'sqlite' === DB_ENGINE ? 'sqlite' : 'mysql';
  1192. if ( 'sqlite' === $db_engine ) {
  1193. return 'sqlite';
  1194. }
  1195. // This is unlikely to change, but it's not impossible.
  1196. $db_type = get_transient( 'mc_db_type' );
  1197. if ( ! $db_type ) {
  1198. $db_type = 'MyISAM';
  1199. $mcdb = mc_is_remote_db();
  1200. $my_calendar = my_calendar_table();
  1201. $dbs = $mcdb->get_results( $mcdb->prepare( 'SHOW TABLE STATUS WHERE name=%s', $my_calendar ) );
  1202. foreach ( $dbs as $db ) {
  1203. $db = (array) $db;
  1204. if ( my_calendar_table() === $db['Name'] ) {
  1205. $db_type = $db['Engine'];
  1206. }
  1207. }
  1208. set_transient( 'mc_db_type', $db_type, MONTH_IN_SECONDS );
  1209. }
  1210. return $db_type;
  1211. }
  1212. /**
  1213. * Produce list of statuses & counts for events manager.
  1214. *
  1215. * @param bool $allow_filters Whether current user can see spam filters.
  1216. *
  1217. * @return string
  1218. */
  1219. function mc_status_links( $allow_filters ) {
  1220. $counts = get_option( 'mc_count_cache' );
  1221. if ( empty( $counts ) ) {
  1222. $counts = mc_update_count_cache();
  1223. }
  1224. $all = isset( $counts['all'] ) ? $counts['all'] : $counts['published'] + $counts['draft'] + $counts['trash'];
  1225. $all_attributes = ( isset( $_GET['limit'] ) && 'all' === $_GET['limit'] ) ? ' aria-current="true"' : '';
  1226. // Translators: Number of total events.
  1227. $all_text = sprintf( __( 'All (%d)', 'my-calendar' ), $all );
  1228. $pub_attributes = ( isset( $_GET['limit'] ) && 'published' === $_GET['limit'] ) ? ' aria-current="true"' : '';
  1229. // Translators: Number of total events.
  1230. $pub_text = sprintf( __( 'Published (%d)', 'my-calendar' ), $counts['published'] );
  1231. $dra_attributes = ( isset( $_GET['limit'] ) && 'draft' === $_GET['limit'] ) ? ' aria-current="true"' : '';
  1232. // Translators: Number of total events.
  1233. $dra_text = sprintf( __( 'Drafts (%d)', 'my-calendar' ), $counts['draft'] );
  1234. $tra_attributes = ( isset( $_GET['limit'] ) && 'trashed' === $_GET['limit'] ) ? ' aria-current="true"' : '';
  1235. // Translators: Number of total events.
  1236. $tra_text = sprintf( __( 'Trashed (%d)', 'my-calendar' ), $counts['trash'] );
  1237. $arc_attributes = ( isset( $_GET['restrict'] ) && 'archived' === $_GET['restrict'] ) ? ' aria-current="true"' : '';
  1238. // Translators: Number of total events.
  1239. $arc_text = sprintf( __( 'Archived (%d)', 'my-calendar' ), $counts['archive'] );
  1240. $can_text = '';
  1241. if ( isset( $counts['cancel'] ) && 0 < (int) $counts['cancel'] ) {
  1242. $can_attributes = ( isset( $_GET['limit'] ) && 'cancelled' === $_GET['limit'] ) ? ' aria-current="true"' : '';
  1243. // Translators: Number of total events.
  1244. $can_text = sprintf( __( 'Cancelled (%d)', 'my-calendar' ), $counts['cancel'] );
  1245. }
  1246. $pri_text = '';
  1247. if ( isset( $counts['private'] ) && 0 < (int) $counts['private'] ) {
  1248. $pri_attributes = ( isset( $_GET['limit'] ) && 'private' === $_GET['limit'] ) ? ' aria-current="true"' : '';
  1249. // Translators: Number of total events.
  1250. $pri_text = sprintf( __( 'Private (%d)', 'my-calendar' ), $counts['private'] );
  1251. }
  1252. $spa_attributes = ( isset( $_GET['restrict'] ) && 'flagged' === $_GET['restrict'] ) ? ' aria-current="true"' : '';
  1253. // Translators: Number of total events.
  1254. $spa_text = sprintf( __( 'Spam (%d)', 'my-calendar' ), $counts['spam'] );
  1255. $output = '
  1256. <ul class="links">
  1257. <li>
  1258. <a ' . $all_attributes . ' href="' . mc_admin_url( 'admin.php?page=my-calendar-manage&amp;limit=all' ) . '">' . $all_text . '</a>
  1259. </li>
  1260. <li>
  1261. <a ' . $pub_attributes . ' href="' . mc_admin_url( 'admin.php?page=my-calendar-manage&amp;limit=published' ) . '">' . $pub_text . '</a>
  1262. </li>
  1263. <li>
  1264. <a ' . $dra_attributes . ' href="' . mc_admin_url( 'admin.php?page=my-calendar-manage&amp;limit=draft' ) . '">' . $dra_text . '</a>
  1265. </li>
  1266. <li>
  1267. <a ' . $tra_attributes . ' href="' . admin_url( 'admin.php?page=my-calendar-manage&amp;limit=trashed' ) . '">' . $tra_text . '</a>
  1268. </li>
  1269. <li>
  1270. <a ' . $arc_attributes . ' href="' . mc_admin_url( 'admin.php?page=my-calendar-manage&amp;restrict=archived' ) . '">' . $arc_text . '</a>
  1271. </li>';
  1272. if ( $can_text ) {
  1273. $output .= '
  1274. <li>
  1275. <a ' . $can_attributes . ' href="' . admin_url( 'admin.php?page=my-calendar-manage&amp;limit=cancelled' ) . '">' . $can_text . '</a>
  1276. </li>';
  1277. }
  1278. if ( $pri_text ) {
  1279. $output .= '
  1280. <li>
  1281. <a ' . $pri_attributes . ' href="' . admin_url( 'admin.php?page=my-calendar-manage&amp;limit=private' ) . '">' . $pri_text . '</a>
  1282. </li>';
  1283. }
  1284. if ( ( function_exists( 'akismet_http_post' ) || ( 0 < (int) $counts['spam'] ) ) && $allow_filters ) {
  1285. $output .= '
  1286. <li>
  1287. <a ' . $spa_attributes . ' href="' . mc_admin_url( 'admin.php?page=my-calendar-manage&amp;restrict=flagged&amp;filter=1' ) . '">' . $spa_text . '</a>
  1288. </li>';
  1289. }
  1290. $output .= '</ul>';
  1291. return $output;
  1292. }