PageRenderTime 26ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/all-in-one-event-calendar/app/helper/class-ai1ec-events-helper.php

https://github.com/jdickie/mithpressbeta
PHP | 988 lines | 494 code | 97 blank | 397 comment | 58 complexity | 276c8bcdc1ce73d03632fd171e3c1eb0 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, AGPL-1.0
  1. <?php
  2. //
  3. // class-ai1ec-events-helper.php
  4. // all-in-one-event-calendar
  5. //
  6. // Created by The Seed Studio on 2011-07-13.
  7. //
  8. /**
  9. * Ai1ec_Events_Helper class
  10. *
  11. * @package Helpers
  12. * @author The Seed Studio
  13. **/
  14. class Ai1ec_Events_Helper {
  15. /**
  16. * _instance class variable
  17. *
  18. * Class instance
  19. *
  20. * @var null | object
  21. **/
  22. private static $_instance = NULL;
  23. /**
  24. * Constructor
  25. *
  26. * Default constructor
  27. **/
  28. private function __construct() { }
  29. /**
  30. * get_instance function
  31. *
  32. * Return singleton instance
  33. *
  34. * @return object
  35. **/
  36. static function get_instance() {
  37. if( self::$_instance === NULL ) {
  38. self::$_instance = new self();
  39. }
  40. return self::$_instance;
  41. }
  42. /**
  43. * get_event function
  44. *
  45. * Fetches the event object with the given post ID. Uses the WP cache to
  46. * make this more efficient if possible.
  47. *
  48. * @param int $post_id The ID of the post associated with the event
  49. *
  50. * @return Ai1ec_Event The associated event object
  51. **/
  52. static function get_event( $post_id )
  53. {
  54. $event = wp_cache_get( $post_id, AI1EC_POST_TYPE );
  55. if( $event === false ) {
  56. $event = new Ai1ec_Event( $post_id );
  57. if( ! $event->post_id )
  58. throw new Ai1ec_Event_Not_Found( "Event with ID '$post_id' could not be retrieved from the database." );
  59. // Cache the event data
  60. wp_cache_add( $post_id, $event, AI1EC_POST_TYPE );
  61. }
  62. return $event;
  63. }
  64. /**
  65. * get_matching_event function
  66. *
  67. * Return event ID by iCalendar UID, feed url, start time and whether the
  68. * event has recurrence rules (to differentiate between an event with a UID
  69. * defining the recurrence pattern, and other events with with the same UID,
  70. * which are just RECURRENCE-IDs).
  71. *
  72. * @param int $uid iCalendar UID property
  73. * @param string $feed Feed URL
  74. * @param int $start Start timestamp (GMT)
  75. * @param bool $has_recurrence Whether the event has recurrence rules
  76. * @param int|null $exclude_post_id Do not match against this post ID
  77. *
  78. * @return object|null Matching event's post ID, or null if no match
  79. **/
  80. function get_matching_event_id( $uid, $feed, $start, $has_recurrence = false, $exclude_post_id = null ) {
  81. global $wpdb;
  82. $table_name = $wpdb->prefix . 'ai1ec_events';
  83. $query = "SELECT post_id FROM {$table_name} " .
  84. "WHERE ical_feed_url = %s " .
  85. "AND ical_uid = %s " .
  86. "AND start = FROM_UNIXTIME( %d ) " .
  87. ( $has_recurrence ? 'AND NOT ' : 'AND ' ) .
  88. "( recurrence_rules IS NULL OR recurrence_rules = '' )";
  89. $args = array( $feed, $uid, $start );
  90. if( ! is_null( $exclude_post_id ) ) {
  91. $query .= 'AND post_id <> %d';
  92. $args[] = $exclude_post_id;
  93. }
  94. return $wpdb->get_var( $wpdb->prepare( $query, $args ) );
  95. }
  96. /**
  97. * delete_event_cache function
  98. *
  99. * Delete cache of event
  100. *
  101. * @param int $pid Event post ID
  102. *
  103. * @return void
  104. **/
  105. function delete_event_cache( $pid ) {
  106. global $wpdb;
  107. $table_name = $wpdb->prefix . 'ai1ec_event_instances';
  108. $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE post_id = %d", $pid ) );
  109. }
  110. /**
  111. * cache_event function
  112. *
  113. * Creates a new entry in the cache table for each date that the event appears
  114. * (and does not already have an explicit RECURRENCE-ID instance, given its
  115. * iCalendar UID).
  116. *
  117. * @param object $event Event to generate cache table for
  118. *
  119. * @return void
  120. **/
  121. function cache_event( &$event ) {
  122. global $wpdb;
  123. // Convert event's timestamps to local for correct calculations of
  124. // recurrence. Need to also remove PHP timezone offset for each date for
  125. // SG_iCal to calculate correct recurring instances.
  126. $event->start = $this->gmt_to_local( $event->start ) - date( 'Z', $event->start );
  127. $event->end = $this->gmt_to_local( $event->end ) - date( 'Z', $event->end );
  128. $evs = array();
  129. $e = array(
  130. 'post_id' => $event->post_id,
  131. 'start' => $event->start,
  132. 'end' => $event->end,
  133. );
  134. $duration = $event->getDuration();
  135. // Always cache initial instance
  136. $evs[] = $e;
  137. if( $event->recurrence_rules )
  138. {
  139. $count = 0;
  140. $start = $event->start;
  141. $freq = $event->getFrequency();
  142. $freq->firstOccurrence();
  143. while( ( $next = $freq->nextOccurrence( $start ) ) > 0 &&
  144. $count < 1000 )
  145. {
  146. $count++;
  147. $start = $next;
  148. $e['start'] = $start;
  149. $e['end'] = $start + $duration;
  150. $evs[] = $e;
  151. }
  152. }
  153. // Make entries unique (sometimes recurrence generator creates duplicates?)
  154. $evs_unique = array();
  155. foreach( $evs as $ev ) {
  156. $evs_unique[ md5( serialize( $ev ) ) ] = $ev;
  157. }
  158. foreach( $evs_unique as $e )
  159. {
  160. // Find out if this event instance is already accounted for by an
  161. // overriding 'RECURRENCE-ID' of the same iCalendar feed (by comparing the
  162. // UID, start date, recurrence). If so, then do not create duplicate
  163. // instance of event.
  164. $matching_event_id = $event->ical_uid ?
  165. $this->get_matching_event_id(
  166. $event->ical_uid,
  167. $event->ical_feed_url,
  168. $start = $this->local_to_gmt( $e['start'] ) - date( 'Z', $e['start'] ),
  169. false, // Only search events that don't define recurrence (i.e. only search for RECURRENCE-ID events)
  170. $event->post_id
  171. )
  172. : null;
  173. // If no other instance was found
  174. if( is_null( $matching_event_id ) )
  175. {
  176. $start = getdate( $e['start'] );
  177. $end = getdate( $e['end'] );
  178. // If event spans a day and end time is not midnight, or spans more than
  179. // a day, then create instance for each spanning day
  180. if( ( $start['mday'] != $end['mday'] &&
  181. ( $end['hours'] || $end['minutes'] || $end['seconds'] ) )
  182. || $e['end'] - $e['start'] > 60 * 60 * 24 ) {
  183. $this->create_cache_table_entries( $e );
  184. // Else cache single instance of event
  185. } else {
  186. $this->insert_event_in_cache_table( $e );
  187. }
  188. }
  189. }
  190. }
  191. /**
  192. * insert_event_in_cache_table function
  193. *
  194. * Inserts a new record in the cache table
  195. *
  196. * @param array $event Event array
  197. *
  198. * @return void
  199. **/
  200. function insert_event_in_cache_table( $event ) {
  201. global $wpdb;
  202. // Return the start/end times to GMT zone
  203. $event['start'] = $this->local_to_gmt( $event['start'] ) + date( 'Z', $event['start'] );
  204. $event['end'] = $this->local_to_gmt( $event['end'] ) + date( 'Z', $event['end'] );
  205. $wpdb->query(
  206. $wpdb->prepare(
  207. "INSERT INTO {$wpdb->prefix}ai1ec_event_instances " .
  208. " ( post_id, start, end ) " .
  209. "VALUES ( %d, FROM_UNIXTIME( %d ), FROM_UNIXTIME( %d ) )",
  210. $event
  211. )
  212. );
  213. }
  214. /**
  215. * create_cache_table_entries function
  216. *
  217. * Create a new entry for each day that the event spans.
  218. *
  219. * @param array $e Event array
  220. *
  221. * @return void
  222. **/
  223. function create_cache_table_entries( $e )
  224. {
  225. global $ai1ec_events_helper;
  226. // Decompose start dates into components
  227. $start_bits = getdate( $e['start'] );
  228. // ============================================
  229. // = Calculate the time for event's first day =
  230. // ============================================
  231. // Start time is event's original start time
  232. $event_start = $e['start'];
  233. // End time is beginning of next day
  234. $event_end = mktime(
  235. 0, // hour
  236. 0, // minute
  237. 0, // second
  238. $start_bits['mon'], // month
  239. $start_bits['mday'] + 1, // day
  240. $start_bits['year'] // year
  241. );
  242. // Cache first day
  243. $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ) );
  244. // ====================================================
  245. // = Calculate the time for event's intermediate days =
  246. // ====================================================
  247. // Start time is previous end time
  248. $event_start = $event_end;
  249. // End time one day ahead
  250. $event_end += 60 * 60 * 24;
  251. // Cache intermediate days
  252. while( $event_end < $e['end'] ) {
  253. $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ) );
  254. $event_start = $event_end; // Start time is previous end time
  255. $event_end += 24 * 60 * 60; // Increment end time by 1 day
  256. }
  257. // ===========================================
  258. // = Calculate the time for event's last day =
  259. // ===========================================
  260. // Start time is already correct (previous end time)
  261. // End time is event end time
  262. // Only insert if the last event instance if span is > 0
  263. $event_end = $e['end'];
  264. if( $event_end > $event_start )
  265. // Cache last day
  266. $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ) );
  267. }
  268. /**
  269. * Returns the various preset recurrence options available (e.g.,
  270. * 'DAILY', 'WEEKENDS', etc.).
  271. *
  272. * @return string An associative array of pattern names to English
  273. * equivalents
  274. */
  275. function get_repeat_patterns() {
  276. // Calling functions when creating an array does not seem to work when
  277. // the assigned to variable is static. This is a workaround.
  278. static $options;
  279. if( !isset( $options ) ) {
  280. $temp = array(
  281. ' ' => __( 'No repeat', AI1EC_PLUGIN_NAME ),
  282. 'DAILY' => __( 'Daily', AI1EC_PLUGIN_NAME ),
  283. 'MO' => __( 'Mondays', AI1EC_PLUGIN_NAME ),
  284. 'TU' => __( 'Tuesdays', AI1EC_PLUGIN_NAME ),
  285. 'WE' => __( 'Wednesdays', AI1EC_PLUGIN_NAME ),
  286. 'TH' => __( 'Thursdays', AI1EC_PLUGIN_NAME ),
  287. 'FR' => __( 'Fridays', AI1EC_PLUGIN_NAME ),
  288. 'SA' => __( 'Saturdays', AI1EC_PLUGIN_NAME ),
  289. 'SU' => __( 'Sundays', AI1EC_PLUGIN_NAME ),
  290. 'TU+TH' => __( 'Tuesdays & Thursdays', AI1EC_PLUGIN_NAME ),
  291. 'MO+WE+FR' => __( 'Mondays, Wednesdays & Fridays', AI1EC_PLUGIN_NAME ),
  292. 'WEEKDAYS' => __( 'Weekdays', AI1EC_PLUGIN_NAME ),
  293. 'WEEKENDS' => __( 'Weekends', AI1EC_PLUGIN_NAME ),
  294. 'WEEKLY' => __( 'Weekly', AI1EC_PLUGIN_NAME ),
  295. 'MONTHLY' => __( 'Monthly', AI1EC_PLUGIN_NAME ),
  296. 'YEARLY' => __( 'Yearly', AI1EC_PLUGIN_NAME )
  297. );
  298. $options = $temp;
  299. }
  300. return $options;
  301. }
  302. /**
  303. * Generates and returns repeat dropdown
  304. *
  305. * @param Integer|NULL $selected Selected option
  306. *
  307. * @return String Repeat dropdown
  308. */
  309. function create_repeat_dropdown( $selected = null ) {
  310. ob_start();
  311. $options = $this->get_repeat_patterns();
  312. ?>
  313. <select name="ai1ec_repeat" id="ai1ec_repeat">
  314. <?php foreach( $options as $key => $val ): ?>
  315. <option value="<?php echo $key ?>" <?php if( $key === $selected ) echo 'selected="selected"' ?>>
  316. <?php _e( $val, AI1EC_PLUGIN_NAME ) ?>
  317. </option>
  318. <?php endforeach ?>
  319. </select>
  320. <?php
  321. $output = ob_get_contents();
  322. ob_end_clean();
  323. return $output;
  324. }
  325. /**
  326. * Returns an associative array containing the following information:
  327. * string 'repeat' => pattern of repetition ('DAILY', 'WEEKENDS', etc.)
  328. * int 'count' => end after 'count' times
  329. * int 'until' => repeat until date (as UNIX timestamp)
  330. * Elements are null if no such recurrence information is available.
  331. *
  332. * @param Ai1ec_Event Event object to parse recurrence rules of
  333. * @return array Array structured as described above
  334. **/
  335. function parse_recurrence_rules( &$event )
  336. {
  337. $repeat = null;
  338. $count = null;
  339. $until = null;
  340. $end = 0;
  341. if( ! is_null( $event ) ) {
  342. if( strlen( $event->recurrence_rules ) > 0 ) {
  343. $line = new SG_iCal_Line( $event->recurrence_rules );
  344. $rec = new SG_iCal_Recurrence( $line );
  345. switch( $rec->req ) {
  346. case 'DAILY':
  347. $by_day = $rec->getByDay();
  348. if( empty( $by_day ) ) {
  349. $repeat = 'DAILY';
  350. } elseif( $by_day[0] == 'SA+SU' ) {
  351. $repeat = 'WEEKENDS';
  352. } elseif( count( $by_day ) == 5 ) {
  353. $repeat = 'WEEKDAYS';
  354. } else {
  355. foreach( $by_day as $d ) {
  356. $repeat .= $d . '+';
  357. }
  358. $repeat = substr( $repeat, 0, -1 );
  359. }
  360. break;
  361. case 'WEEKLY':
  362. $repeat = 'WEEKLY';
  363. break;
  364. case 'MONTHLY':
  365. $repeat = 'MONTHLY';
  366. break;
  367. case 'YEARLY':
  368. $repeat = 'YEARLY';
  369. break;
  370. }
  371. $count = $rec->getCount();
  372. $until = $rec->getUntil();
  373. if( $until ) {
  374. $until = strtotime( $rec->getUntil() );
  375. $until += date( 'Z', $until ); // Add timezone offset
  376. $end = 2;
  377. } elseif( $count )
  378. $end = 1;
  379. else
  380. $end = 0;
  381. }
  382. }
  383. return array(
  384. 'repeat' => $repeat,
  385. 'count' => $count,
  386. 'until' => $until,
  387. 'end' => $end
  388. );
  389. }
  390. /**
  391. * Generates and returns "End after X times" input
  392. *
  393. * @param Integer|NULL $count Initial value of range input
  394. *
  395. * @return String Repeat dropdown
  396. */
  397. function create_count_input( $count = 100 ) {
  398. ob_start();
  399. if( ! $count ) $count = 100;
  400. ?>
  401. <input type="range" name="ai1ec_count" id="ai1ec_count" min="1" max="100"
  402. <?php if( $count ) echo 'value="' . $count . '"' ?> />
  403. <?php _e( 'times', AI1EC_PLUGIN_NAME ) ?>
  404. <?php
  405. $output = ob_get_contents();
  406. ob_end_clean();
  407. return $output;
  408. }
  409. /**
  410. * get_all_matching_posts function
  411. *
  412. * Gets existing event posts that are between the interval
  413. *
  414. * @param int $s_time Start time
  415. * @param int $e_time End time
  416. *
  417. * @return Array of matching event posts
  418. **/
  419. function get_all_matching_posts( $s_time, $e_time ) {
  420. global $ai1ec_calendar_helper;
  421. return $ai1ec_calendar_helper->get_events_between( $s_time, $e_time );
  422. }
  423. /**
  424. * get_matching_events function
  425. *
  426. * Get events that match with the arguments provided.
  427. *
  428. * @param int | bool $start Events start before this (GMT) time
  429. * @param int | bool $end Events end before this (GMT) time
  430. * @param int | array | bool $tags Tag(s) of events
  431. * @param int | array | bool $categories Category(ies) of events
  432. *
  433. * @return array Matching events
  434. **/
  435. function get_matching_events( $start = false, $end = false, $tags = false, $categories = false, $posts = false ) {
  436. global $wpdb;
  437. // holds event_categories sql
  438. $c_sql = '';
  439. $c_where_sql = '';
  440. // holds event_tags sql
  441. $t_sql = '';
  442. $t_where_sql ='';
  443. // holds posts sql
  444. $p_where_sql = '';
  445. // holds start sql
  446. $start_where_sql = '';
  447. // holds end sql
  448. $end_where_sql = '';
  449. // hold escape values
  450. $args = array();
  451. // =============================
  452. // = Generating start date sql =
  453. // =============================
  454. if( $start !== false ) {
  455. $start_where_sql = "AND e.start >= FROM_UNIXTIME( %d )";
  456. $args[] = $start;
  457. }
  458. // ===========================
  459. // = Generating end date sql =
  460. // ===========================
  461. if( $end !== false ) {
  462. $end_where_sql = "AND e.end <= FROM_UNIXTIME( %d )";
  463. $args[] = $end;
  464. }
  465. // ===================================
  466. // = Generating event_categories sql =
  467. // ===================================
  468. if( $categories !== false ) {
  469. // sanitize categories var
  470. if( strstr( $categories, ',' ) !== false ) {
  471. $tmp = array();
  472. // prevent sql injection
  473. foreach( explode( ',', $categories ) as $cat )
  474. $tmp[] = (int) $cat;
  475. $categories = $tmp;
  476. } else {
  477. // prevent sql injection
  478. $categories = (int) $categories;
  479. }
  480. $c_sql = "INNER JOIN $wpdb->term_relationships AS tr ON post_id = tr.object_id ";
  481. $c_where_sql = "AND tr.term_taxonomy_id IN ( $categories ) ";
  482. }
  483. // =============================
  484. // = Generating event_tags sql =
  485. // =============================
  486. if( $tags !== false ) {
  487. // sanitize tags var
  488. if( strstr( $tags, ',' ) !== false ) {
  489. $tmp = array();
  490. // prevent sql injection
  491. foreach( explode( ',', $tags ) as $tag )
  492. $tmp[] = (int) $tag;
  493. $tags = $tmp;
  494. } else {
  495. $tags = (int) $tags;
  496. }
  497. // if category sql is included then don't inner join term_relationships table
  498. if( ! empty( $c_sql ) ) {
  499. $t_where_sql = "AND tr.term_taxonomy_id IN ( $tags ) ";
  500. } else {
  501. $t_sql = "INNER JOIN $wpdb->term_relationships AS tr ON e.post_id = tr.object_id ";
  502. $t_where_sql = "AND tr.term_taxonomy_id IN ( $tags ) ";
  503. }
  504. }
  505. // ========================
  506. // = Generating posts sql =
  507. // ========================
  508. if( $posts !== false ) {
  509. // sanitize posts var
  510. if( strstr( $posts, ',' ) !== false ) {
  511. $tmp = array();
  512. // prevent sql injection
  513. foreach( explode( ',', $posts ) as $post )
  514. $tmp[] = $post;
  515. $posts = $tmp;
  516. }
  517. $p_where_sql = "AND ID IN ( $posts ) ";
  518. }
  519. $query = $wpdb->prepare(
  520. "SELECT *, e.post_id, UNIX_TIMESTAMP( e.start ) as start, UNIX_TIMESTAMP( e.end ) as end, e.allday, e.recurrence_rules, e.exception_rules,
  521. e.recurrence_dates, e.exception_dates, e.venue, e.country, e.address, e.city, e.province, e.postal_code,
  522. e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, e.ical_feed_url, e.ical_source_url,
  523. e.ical_organizer, e.ical_contact, e.ical_uid " .
  524. "FROM $wpdb->posts " .
  525. "INNER JOIN {$wpdb->prefix}ai1ec_events AS e ON e.post_id = ID " .
  526. $c_sql .
  527. $t_sql .
  528. "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
  529. $c_where_sql .
  530. $t_where_sql .
  531. $p_where_sql .
  532. $start_where_sql .
  533. $end_where_sql,
  534. $args );
  535. $events = $wpdb->get_results( $query, ARRAY_A );
  536. foreach( $events as &$event ) {
  537. try{
  538. $event = new Ai1ec_Event( $event );
  539. } catch( Ai1ec_Event_Not_Found $n ) {
  540. unset( $event );
  541. // The event is not found, continue to the next event
  542. continue;
  543. }
  544. // if there are recurrence rules, include the event, else...
  545. if( empty( $event->recurrence_rules ) ) {
  546. // if start time is set, and event start time is before the range
  547. // it, continue to the next event
  548. if( $start !== false && $event->start < $start ) {
  549. unset( $event );
  550. continue;
  551. }
  552. // if end time is set, and event end time is after
  553. // it, continue to the next event
  554. if( $end !== false && $ev->end < $end ) {
  555. unset( $event );
  556. continue;
  557. }
  558. }
  559. }
  560. return $events;
  561. }
  562. /**
  563. * fuzzy_string_compare function
  564. *
  565. * Compares string A to string B using fuzzy comparison algorithm
  566. *
  567. * @param String $a String to compare
  568. * @param String $b String to compare
  569. *
  570. * @return boolean True if the two strings match, false otherwise
  571. **/
  572. function fuzzy_string_compare( $a, $b ) {
  573. $percent = 0;
  574. similar_text( $a, $b, &$percent );
  575. return ( $percent > 50 );
  576. }
  577. /**
  578. * get_short_time function
  579. *
  580. * Format a short-form time for use in compressed (e.g. month) views;
  581. * this is also converted to the local timezone.
  582. *
  583. * @param int $timestamp
  584. * @param bool $convert_from_gmt Whether to convert from GMT time to local
  585. *
  586. * @return string
  587. **/
  588. function get_short_time( $timestamp, $convert_from_gmt = true ) {
  589. $time_format = get_option( 'time_format', 'g:ia' );
  590. if( $convert_from_gmt )
  591. $timestamp = $this->gmt_to_local( $timestamp );
  592. return date_i18n( $time_format, $timestamp, true );
  593. }
  594. /**
  595. * get_short_date function
  596. *
  597. * Format a short-form date for use in compressed (e.g. month) views;
  598. * this is also converted to the local timezone.
  599. *
  600. * @param int $timestamp
  601. * @param bool $convert_from_gmt Whether to convert from GMT time to local
  602. *
  603. * @return string
  604. **/
  605. function get_short_date( $timestamp, $convert_from_gmt = true ) {
  606. if( $convert_from_gmt )
  607. $timestamp = $this->gmt_to_local( $timestamp );
  608. return date_i18n( 'M j', $timestamp, true );
  609. }
  610. /**
  611. * get_medium_time function
  612. *
  613. * Format a medium-length time for use in other views (e.g., Agenda);
  614. * this is also converted to the local timezone.
  615. *
  616. * @param int $timestamp
  617. *
  618. * @return string
  619. **/
  620. function get_medium_time( $timestamp, $convert_from_gmt = true ) {
  621. $time_format = get_option( 'time_format', 'g:ia' );
  622. if( $convert_from_gmt )
  623. $timestamp = $this->gmt_to_local( $timestamp );
  624. return date_i18n( $time_format, $timestamp, true );
  625. }
  626. /**
  627. * get_long_time function
  628. *
  629. * Format a long-length time for use in other views (e.g., single event);
  630. * this is also converted to the local timezone.
  631. *
  632. * @param int $timestamp
  633. * @param bool $convert_from_gmt Whether to convert from GMT time to local
  634. *
  635. * @return string
  636. **/
  637. function get_long_time( $timestamp, $convert_from_gmt = true ) {
  638. $date_format = get_option( 'date_format', 'D, F j' );
  639. $time_format = get_option( 'time_format', 'g:i' );
  640. if( $convert_from_gmt )
  641. $timestamp = $this->gmt_to_local( $timestamp );
  642. return date_i18n( $date_format, $timestamp, true ) . ' @ ' . date_i18n( $time_format, $timestamp, true );
  643. }
  644. /**
  645. * get_long_date function
  646. *
  647. * Format a long-length date for use in other views (e.g., single event);
  648. * this is also converted to the local timezone if desired.
  649. *
  650. * @param int $timestamp
  651. * @param bool $convert_from_gmt Whether to convert from GMT time to local
  652. *
  653. * @return string
  654. **/
  655. function get_long_date( $timestamp, $convert_from_gmt = true ) {
  656. $date_format = get_option( 'date_format', 'D, F j' );
  657. if( $convert_from_gmt )
  658. $timestamp = $this->gmt_to_local( $timestamp );
  659. return date_i18n( $date_format, $timestamp, true );
  660. }
  661. /**
  662. * gmt_to_local function
  663. *
  664. * Returns the UNIX timestamp adjusted to the local timezone.
  665. *
  666. * @param int $timestamp
  667. *
  668. * @return int
  669. **/
  670. function gmt_to_local( $timestamp ) {
  671. return $timestamp + get_option( 'gmt_offset' ) * 3600;
  672. }
  673. /**
  674. * local_to_gmt function
  675. *
  676. * Returns the UNIX timestamp adjusted from the local timezone to GMT.
  677. *
  678. * @param int $timestamp
  679. *
  680. * @return int
  681. **/
  682. function local_to_gmt( $timestamp ) {
  683. return $timestamp - get_option( 'gmt_offset' ) * 3600;
  684. }
  685. /**
  686. * A GMT-version of PHP getdate().
  687. *
  688. * @param int $timestamp UNIX timestamp
  689. * @return array Same result as getdate(), but based in GMT time.
  690. **/
  691. function gmgetdate( $timestamp = null ) {
  692. if( ! $timestamp ) $timestamp = time();
  693. $bits = explode( ',', gmdate( 's,i,G,j,w,n,Y,z,l,F,U', $timestamp ) );
  694. $bits = array_combine(
  695. array( 'seconds', 'minutes', 'hours', 'mday', 'wday', 'mon', 'year', 'yday', 'weekday', 'month', 0 ),
  696. $bits
  697. );
  698. return $bits;
  699. }
  700. /**
  701. * time_to_gmt function
  702. *
  703. * Converts time to GMT
  704. *
  705. * @param int $timestamp
  706. *
  707. * @return int
  708. **/
  709. function time_to_gmt( $timestamp ) {
  710. return strtotime( gmdate( 'M d Y H:i:s', $timestamp ) );
  711. }
  712. /**
  713. * get_gmap_url function
  714. *
  715. * Returns the URL to the Google Map for the given event object.
  716. *
  717. * @param Ai1ec_Event $event The event object to display a map for
  718. *
  719. * @return string
  720. **/
  721. function get_gmap_url( &$event ) {
  722. $location_arg = urlencode( $event->address );
  723. return "http://www.google.com/maps?f=q&hl=en&source=embed&q=$location_arg";
  724. }
  725. /**
  726. * trim_excerpt function
  727. *
  728. * Generates an excerpt from the given content string. Adapted from
  729. * WordPress's wp_trim_excerpt function that is not useful for applying
  730. * to custom content.
  731. *
  732. * @param string $text The content to trim.
  733. *
  734. * @return string The excerpt.
  735. **/
  736. function trim_excerpt( $text )
  737. {
  738. $raw_excerpt = $text;
  739. $text = strip_shortcodes( $text );
  740. $text = str_replace(']]>', ']]&gt;', $text);
  741. $text = strip_tags($text);
  742. $excerpt_length = apply_filters('excerpt_length', 55);
  743. $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
  744. $words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
  745. if ( count($words) > $excerpt_length ) {
  746. array_pop($words);
  747. $text = implode(' ', $words);
  748. $text = $text . $excerpt_more;
  749. } else {
  750. $text = implode(' ', $words);
  751. }
  752. return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
  753. }
  754. /**
  755. * filter_by_terms function
  756. *
  757. * Returns a subset of post IDs from the given set of post IDs that have any
  758. * of the given taxonomy term IDs. This is actually useful for all posts and
  759. * taxonomies in general, not just event posts and event-specific taxonomies.
  760. *
  761. * @param array|string $post_ids Post IDs as an array of ints or
  762. * comma-separated string
  763. * @param array|string $term_ids Term IDs as an array of ints or
  764. * comma-separated string
  765. *
  766. * @return array Filtered post IDs as an array of ints
  767. */
  768. function filter_by_terms( $post_ids, $term_ids )
  769. {
  770. global $wpdb;
  771. // ===============================================
  772. // = Sanitize provided IDs against SQL injection =
  773. // ===============================================
  774. if( ! is_array( $post_ids ) )
  775. $post_ids = explode( ',', $post_ids );
  776. foreach( $post_ids as &$post_id ) {
  777. $post_id = intval( $post_id );
  778. }
  779. $post_ids = join( ',', $post_ids );
  780. if( ! is_array( $term_ids ) )
  781. $term_ids = explode( ',', $term_ids );
  782. foreach( $term_ids as &$term_id ) {
  783. $term_id = intval( $term_id );
  784. }
  785. $term_ids = join( ',', $term_ids );
  786. $query =
  787. "SELECT DISTINCT p.ID " .
  788. "FROM $wpdb->posts p " .
  789. "INNER JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id " .
  790. "INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id " .
  791. "WHERE p.ID IN ( " . $post_ids . " ) " .
  792. "AND tt.term_id IN ( " . $term_ids . " )";
  793. return $wpdb->get_col( $query );
  794. }
  795. /**
  796. * get_category_color function
  797. *
  798. * Returns the color of the Event Category having the given term ID.
  799. *
  800. * @param int $term_id The ID of the Event Category
  801. * @return string
  802. */
  803. function get_category_color( $term_id ) {
  804. global $wpdb;
  805. $term_id = (int) $term_id;
  806. $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
  807. $color = $wpdb->get_var( "SELECT term_color FROM {$table_name} WHERE term_id = {$term_id}" );
  808. return $color;
  809. }
  810. /**
  811. * get_category_color_square function
  812. *
  813. * Returns the HTML markup for the category color square of the given Event
  814. * Category term ID.
  815. *
  816. * @param int $term_id The Event Category's term ID
  817. * @return string
  818. **/
  819. function get_category_color_square( $term_id ) {
  820. $color = $this->get_category_color( $term_id );
  821. $cat = get_term( $term_id, 'events_categories' );
  822. if( ! is_null( $color ) && ! empty( $color ) )
  823. return '<div class="ai1ec-category-color" style="background:' . $color . '" title="' . esc_attr( $cat->name ) . '"></div>';
  824. return '';
  825. }
  826. /**
  827. * get_event_category_color_style function
  828. *
  829. * Returns the style attribute assigning the category color style to an event.
  830. *
  831. * @param int $term_id The Event Category's term ID
  832. * @param bool $allday Whether the event is all-day
  833. * @return string
  834. **/
  835. function get_event_category_color_style( $term_id, $allday = false ) {
  836. $color = $this->get_category_color( $term_id );
  837. if( ! is_null( $color ) && ! empty( $color ) ) {
  838. if( $allday )
  839. return ' style="background:' . $color . '"';
  840. else
  841. return ' style="color:' . $color . ' !important"';
  842. }
  843. return '';
  844. }
  845. /**
  846. * get_event_category_colors function
  847. *
  848. * Returns category color squares for the list of Event Category objects.
  849. *
  850. * @param array $cats The Event Category objects as returned by get_terms()
  851. * @return string
  852. **/
  853. function get_event_category_colors( $cats ) {
  854. $sqrs = '';
  855. foreach( $cats as $cat ) {
  856. $tmp = $this->get_category_color_square( $cat->term_id );
  857. if( ! empty( $tmp ) )
  858. $sqrs .= $tmp;
  859. }
  860. return $sqrs;
  861. }
  862. /**
  863. * create_end_dropdown function
  864. *
  865. * Outputs the dropdown list for the recurrence end option.
  866. *
  867. * @param int $selected The index of the selected option, if any
  868. * @return void
  869. **/
  870. function create_end_dropdown( $selected = null ) {
  871. ob_start();
  872. $options = array(
  873. 0 => __( 'Never', AI1EC_PLUGIN_NAME ),
  874. 1 => __( 'After', AI1EC_PLUGIN_NAME ),
  875. 2 => __( 'On date', AI1EC_PLUGIN_NAME )
  876. );
  877. ?>
  878. <select name="ai1ec_end" id="ai1ec_end">
  879. <?php foreach( $options as $key => $val ): ?>
  880. <option value="<?php echo $key ?>" <?php if( $key === $selected ) echo 'selected="selected"' ?>>
  881. <?php echo $val ?>
  882. </option>
  883. <?php endforeach ?>
  884. </select>
  885. <?php
  886. $output = ob_get_contents();
  887. ob_end_clean();
  888. return $output;
  889. }
  890. }
  891. // END class