879 lines
34 KiB
PHP
879 lines
34 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Search Event.
|
|
*
|
|
* @author Time.ly Network Inc.
|
|
* @since 2.0
|
|
*
|
|
* @package AI1EC
|
|
* @subpackage AI1EC.search
|
|
*/
|
|
class Ai1ec_Event_Search extends Ai1ec_Base {
|
|
|
|
/**
|
|
* @var Ai1ec_Dbi instance
|
|
*/
|
|
private $_dbi = null;
|
|
|
|
/**
|
|
* Caches the ids of the last 'between' query
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_ids_between_cache = array();
|
|
|
|
/**
|
|
* Creates local DBI instance.
|
|
*/
|
|
public function __construct( Ai1ec_Registry_Object $registry ){
|
|
parent::__construct( $registry );
|
|
$this->_dbi = $this->_registry->get( 'dbi.dbi' );
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function get_cached_between_ids() {
|
|
return $this->_ids_between_cache;
|
|
}
|
|
|
|
/**
|
|
* Fetches the event object with the given post ID.
|
|
*
|
|
* Uses the WP cache to make this more efficient if possible.
|
|
*
|
|
* @param int $post_id The ID of the post associated.
|
|
* @param bool|int $instance_id Instance ID, to fetch post details for.
|
|
*
|
|
* @return Ai1ec_Event The associated event object.
|
|
*/
|
|
public function get_event( $post_id, $instance_id = false ) {
|
|
$post_id = (int)$post_id;
|
|
$instance_id = (int)$instance_id;
|
|
if ( $instance_id < 1 ) {
|
|
$instance_id = false;
|
|
}
|
|
return $this->_registry->get( 'model.event', $post_id, $instance_id );
|
|
}
|
|
|
|
/**
|
|
* Return events falling within some time range.
|
|
*
|
|
* Return all events starting after the given start time and before the
|
|
* given end time that the currently logged in user has permission to view.
|
|
* If $spanning is true, then also include events that span this
|
|
* period. All-day events are returned first.
|
|
*
|
|
* @param Ai1ec_Date_Time $start Limit to events starting after this.
|
|
* @param Ai1ec_Date_Time $end Limit to events starting before this.
|
|
* @param array $filter Array of filters for the events returned:
|
|
* ['cat_ids'] => list of category IDs;
|
|
* ['tag_ids'] => list of tag IDs;
|
|
* ['post_ids'] => list of post IDs;
|
|
* ['auth_ids'] => list of author IDs;
|
|
* ['instance_ids'] => list of events
|
|
* instance ids;
|
|
* @param bool $spanning Also include events that span this period.
|
|
* @param bool $single_day This parameter is added for oneday view.
|
|
* Query should find events lasting in
|
|
* particular day instead of checking dates
|
|
* range. If you need to call this method
|
|
* with $single_day set to true consider
|
|
* using method get_events_for_day. This
|
|
* parameter matters only if $spanning is set
|
|
* to false.
|
|
*
|
|
* @return array List of matching event objects.
|
|
*/
|
|
public function get_events_between(
|
|
Ai1ec_Date_Time $start,
|
|
Ai1ec_Date_Time $end,
|
|
array $filter = array(),
|
|
$spanning = false,
|
|
$single_day = false
|
|
) {
|
|
// Query arguments
|
|
$args = array(
|
|
$start->format_to_gmt(),
|
|
$end->format_to_gmt(),
|
|
);
|
|
|
|
// Get post status Where snippet and associated SQL arguments
|
|
$where_parameters = $this->_get_post_status_sql();
|
|
$post_status_where = $where_parameters['post_status_where'];
|
|
$args = array_merge( $args, $where_parameters['args'] );
|
|
|
|
// Get the Join (filter_join) and Where (filter_where) statements based
|
|
// on $filter elements specified
|
|
$filter = $this->_get_filter_sql( $filter );
|
|
|
|
$ai1ec_localization_helper = $this->_registry->get( 'p28n.wpml' );
|
|
|
|
$wpml_join_particle = $ai1ec_localization_helper
|
|
->get_wpml_table_join( 'p.ID' );
|
|
|
|
$wpml_where_particle = $ai1ec_localization_helper
|
|
->get_wpml_table_where();
|
|
|
|
if ( $spanning ) {
|
|
$spanning_string = 'i.end > %d AND i.start < %d ';
|
|
} elseif ( $single_day ) {
|
|
$spanning_string = 'i.end >= %d AND i.start <= %d ';
|
|
} else {
|
|
$spanning_string = 'i.start BETWEEN %d AND %d ';
|
|
}
|
|
|
|
$sql = '
|
|
SELECT
|
|
`p`.*,
|
|
`e`.`post_id`,
|
|
`i`.`id` AS `instance_id`,
|
|
`i`.`start` AS `start`,
|
|
`i`.`end` AS `end`,
|
|
`e`.`timezone_name` AS `timezone_name`,
|
|
`e`.`allday` AS `event_allday`,
|
|
`e`.`recurrence_rules`,
|
|
`e`.`exception_rules`,
|
|
`e`.`recurrence_dates`,
|
|
`e`.`exception_dates`,
|
|
`e`.`venue`,
|
|
`e`.`country`,
|
|
`e`.`address`,
|
|
`e`.`city`,
|
|
`e`.`province`,
|
|
`e`.`postal_code`,
|
|
`e`.`instant_event`,
|
|
`e`.`show_map`,
|
|
`e`.`contact_name`,
|
|
`e`.`contact_phone`,
|
|
`e`.`contact_email`,
|
|
`e`.`contact_url`,
|
|
`e`.`cost`,
|
|
`e`.`ticket_url`,
|
|
`e`.`ical_feed_url`,
|
|
`e`.`ical_source_url`,
|
|
`e`.`ical_organizer`,
|
|
`e`.`ical_contact`,
|
|
`e`.`ical_uid`,
|
|
`e`.`longitude`,
|
|
`e`.`latitude`
|
|
FROM
|
|
' . $this->_dbi->get_table_name( 'ai1ec_events' ) . ' e
|
|
INNER JOIN
|
|
' . $this->_dbi->get_table_name( 'posts' ) . ' p
|
|
ON ( `p`.`ID` = `e`.`post_id` )
|
|
' . $wpml_join_particle . '
|
|
INNER JOIN
|
|
' . $this->_dbi->get_table_name( 'ai1ec_event_instances' ) . ' i
|
|
ON ( `e`.`post_id` = `i`.`post_id` )
|
|
' . $filter['filter_join'] . '
|
|
WHERE
|
|
post_type = \'' . AI1EC_POST_TYPE . '\'
|
|
' . $wpml_where_particle . '
|
|
AND
|
|
' . $spanning_string . '
|
|
' . $filter['filter_where'] . '
|
|
' . $post_status_where . '
|
|
GROUP BY
|
|
`i`.`id`
|
|
ORDER BY
|
|
`i` . `start` DESC,
|
|
`e` . `allday` DESC,
|
|
`p` . `post_title` ASC';
|
|
|
|
$query = $this->_dbi->prepare( $sql, $args );
|
|
$events = $this->_dbi->get_results( $query, ARRAY_A );
|
|
|
|
$id_list = array();
|
|
$id_instance_list = array();
|
|
foreach ( $events as $event ) {
|
|
|
|
if ( ! in_array( $event['post_id'], $id_list, true ) ) {
|
|
$id_list[] = $event['post_id'];
|
|
}
|
|
|
|
$id_instance_list[] = array(
|
|
'id' => $event['post_id'],
|
|
'instance_id' => $event['instance_id'],
|
|
);
|
|
}
|
|
|
|
if ( ! empty( $id_list ) ) {
|
|
update_meta_cache( 'post', $id_list );
|
|
$this->_ids_between_cache = $id_instance_list;
|
|
}
|
|
|
|
foreach ( $events as &$event ) {
|
|
$event['allday'] = $this->_is_all_day( $event );
|
|
$event = $this->_registry->get( 'model.event', $event );
|
|
}
|
|
|
|
return $events;
|
|
}
|
|
|
|
/**
|
|
* get_events_relative_to function
|
|
*
|
|
* Return all events starting after the given reference time, limiting the
|
|
* result set to a maximum of $limit items, offset by $page_offset. A
|
|
* negative $page_offset can be provided, which will return events *before*
|
|
* the reference time, as expected.
|
|
*
|
|
* @param int $time limit to events starting after this (local) UNIX time
|
|
* @param int $limit return a maximum of this number of items
|
|
* @param int $page_offset offset the result set by $limit times this number
|
|
* @param array $filter Array of filters for the events returned.
|
|
* ['cat_ids'] => non-associatative array of category IDs
|
|
* ['tag_ids'] => non-associatative array of tag IDs
|
|
* ['post_ids'] => non-associatative array of post IDs
|
|
* ['auth_ids'] => non-associatative array of author IDs
|
|
* ['instance_ids'] => non-associatative array of author IDs
|
|
* @param int $last_day Last day (time), that was displayed.
|
|
* NOTE FROM NICOLA: be careful, if you want a query with events
|
|
* that have a start date which is greater than today, pass 0 as
|
|
* this parameter. If you pass false ( or pass nothing ) you end up with a query
|
|
* with events that finish before today. I don't know the rationale
|
|
* behind this but that's how it works
|
|
* @param bool $unique Whether display only unique events and don't
|
|
* duplicate results with other instances or not.
|
|
*
|
|
* @return array five-element array:
|
|
* ['events'] an array of matching event objects
|
|
* ['prev'] true if more previous events
|
|
* ['next'] true if more next events
|
|
* ['date_first'] UNIX timestamp (date part) of first event
|
|
* ['date_last'] UNIX timestamp (date part) of last event
|
|
*/
|
|
public function get_events_relative_to(
|
|
$time,
|
|
$limit = 0,
|
|
$page_offset = 0,
|
|
$filter = array(),
|
|
$last_day = false,
|
|
$unique = false
|
|
) {
|
|
$localization_helper = $this->_registry->get( 'p28n.wpml' );
|
|
$settings = $this->_registry->get( 'model.settings' );
|
|
|
|
|
|
// Even if there ARE more than 5 times the limit results - we shall not
|
|
// try to fetch and display these, as it would crash system
|
|
$limit = preg_replace('/\D/', '', $limit);
|
|
$upper_boundary = $limit;
|
|
if (
|
|
$settings->get( 'agenda_include_entire_last_day' ) &&
|
|
( false !== $last_day )
|
|
) {
|
|
$upper_boundary *= 5;
|
|
}
|
|
|
|
// Convert timestamp to GMT time
|
|
$time = $this->_registry->get(
|
|
'date.system'
|
|
)->get_current_rounded_time();
|
|
// Get post status Where snippet and associated SQL arguments
|
|
$where_parameters = $this->_get_post_status_sql();
|
|
$post_status_where = $where_parameters['post_status_where'];
|
|
|
|
// Get the Join (filter_join) and Where (filter_where) statements based
|
|
// on $filter elements specified
|
|
$filter = $this->_get_filter_sql( $filter );
|
|
|
|
// Query arguments
|
|
$args = array( $time );
|
|
$args = array_merge( $args, $where_parameters['args'] );
|
|
|
|
if( $page_offset >= 0 ) {
|
|
$first_record = $page_offset * $limit;
|
|
} else {
|
|
$first_record = ( -$page_offset - 1 ) * $limit;
|
|
}
|
|
|
|
|
|
$wpml_join_particle = $localization_helper
|
|
->get_wpml_table_join( 'p.ID' );
|
|
|
|
$wpml_where_particle = $localization_helper
|
|
->get_wpml_table_where();
|
|
|
|
$filter_date_clause = ( $page_offset >= 0 )
|
|
? 'i.end >= %d '
|
|
: 'i.start < %d ';
|
|
$order_direction = ( $page_offset >= 0 ) ? 'ASC' : 'DESC';
|
|
if ( false !== $last_day ) {
|
|
if ( 0 == $last_day ) {
|
|
$last_day = $time;
|
|
}
|
|
$filter_date_clause = ' i.end ';
|
|
if ( $page_offset < 0 ) {
|
|
$filter_date_clause .= '<';
|
|
$order_direction = 'DESC';
|
|
} else {
|
|
$filter_date_clause .= '>';
|
|
$order_direction = 'ASC';
|
|
}
|
|
$filter_date_clause .= ' %d ';
|
|
$args[0] = $last_day;
|
|
$first_record = 0;
|
|
}
|
|
$query = $this->_dbi->prepare(
|
|
'SELECT DISTINCT p.*, e.post_id, i.id AS instance_id, ' .
|
|
'i.start AS start, ' .
|
|
'i.end AS end, ' .
|
|
'e.allday AS event_allday, ' .
|
|
'e.recurrence_rules, e.exception_rules, e.ticket_url, e.instant_event, e.recurrence_dates, e.exception_dates, ' .
|
|
'e.venue, e.country, e.address, e.city, e.province, e.postal_code, ' .
|
|
'e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, ' .
|
|
'e.ical_feed_url, e.ical_source_url, e.ical_organizer, e.ical_contact, e.ical_uid, e.timezone_name, e.longitude, e.latitude ' .
|
|
'FROM ' . $this->_dbi->get_table_name( 'ai1ec_events' ) . ' e ' .
|
|
'INNER JOIN ' . $this->_dbi->get_table_name( 'posts' ) . ' p ON e.post_id = p.ID ' .
|
|
$wpml_join_particle .
|
|
' INNER JOIN ' . $this->_dbi->get_table_name( 'ai1ec_event_instances' ) . ' i ON e.post_id = i.post_id ' .
|
|
$filter['filter_join'] .
|
|
" WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
|
|
' AND ' . $filter_date_clause .
|
|
$wpml_where_particle .
|
|
$filter['filter_where'] .
|
|
$post_status_where .
|
|
( $unique ? ' GROUP BY e.post_id' : '' ) .
|
|
// Reverse order when viewing negative pages, to get correct set of
|
|
// records. Then reverse results later to order them properly.
|
|
' ORDER BY i.start ' . $order_direction .
|
|
', post_title ' . $order_direction .
|
|
' LIMIT ' . $first_record . ', ' . $upper_boundary,
|
|
$args
|
|
);
|
|
|
|
$events = $this->_dbi->get_results( $query, ARRAY_A );
|
|
|
|
// Limit the number of records to convert to data-object
|
|
$events = $this->_limit_result_set(
|
|
$events,
|
|
$limit,
|
|
( false !== $last_day )
|
|
);
|
|
|
|
// Reorder records if in negative page offset
|
|
if( $page_offset < 0 ) {
|
|
$events = array_reverse( $events );
|
|
}
|
|
|
|
$date_first = $date_last = NULL;
|
|
|
|
foreach ( $events as &$event ) {
|
|
$event['allday'] = $this->_is_all_day( $event );
|
|
$event = $this->_registry->get( 'model.event', $event );
|
|
if ( null === $date_first ) {
|
|
$date_first = $event->get( 'start' );
|
|
}
|
|
$date_last = $event->get( 'start' );
|
|
}
|
|
$date_first = $this->_registry->get( 'date.time', $date_first );
|
|
$date_last = $this->_registry->get( 'date.time', $date_last );
|
|
// jus show next/prev links, in case no event found is shown.
|
|
$next = true;
|
|
$prev = true;
|
|
|
|
return array(
|
|
'events' => $events,
|
|
'prev' => $prev,
|
|
'next' => $next,
|
|
'date_first' => $date_first,
|
|
'date_last' => $date_last,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* get_events_relative_to_reference function
|
|
*
|
|
* Return all events starting after the given date reference, limiting the
|
|
* result set to a maximum of $limit items, offset by $page_offset. A
|
|
* negative $page_offset can be provided, which will return events *before*
|
|
* the reference time, as expected.
|
|
*
|
|
* @param int $date_reference if page_offset is greater than or equal to zero, events with start date greater than the date_reference will be returned
|
|
* otherwise events with start date less than the date_reference will be returned.
|
|
* @param int $limit return a maximum of this number of items
|
|
* @param int $page_offset offset the result set by $limit times this number
|
|
* @param array $filter Array of filters for the events returned.
|
|
* ['cat_ids'] => non-associatative array of category IDs
|
|
* ['tag_ids'] => non-associatative array of tag IDs
|
|
* ['post_ids'] => non-associatative array of post IDs
|
|
* ['auth_ids'] => non-associatative array of author IDs
|
|
* ['instance_ids'] => non-associatative array of author IDs
|
|
* @param bool $unique Whether display only unique events and don't
|
|
* duplicate results with other instances or not.
|
|
*
|
|
* @return array five-element array:
|
|
* ['events'] an array of matching event objects
|
|
* ['prev'] true if more previous events
|
|
* ['next'] true if more next events
|
|
* ['date_first'] UNIX timestamp (date part) of first event
|
|
* ['date_last'] UNIX timestamp (date part) of last event
|
|
*/
|
|
public function get_events_relative_to_reference( $date_reference, $limit = 0, $page_offset = 0, $filter = array(), $unique = false ) {
|
|
$localization_helper = $this->_registry->get( 'p28n.wpml' );
|
|
$settings = $this->_registry->get( 'model.settings' );
|
|
|
|
// Even if there ARE more than 5 times the limit results - we shall not
|
|
// try to fetch and display these, as it would crash system
|
|
$limit = preg_replace( '/\D/', '', $limit );
|
|
|
|
// Convert timestamp to GMT time
|
|
if ( 0 == $date_reference ) {
|
|
$timezone = $this->_registry->get( 'date.timezone' )->get( $settings->get( 'timezone_string' ) );
|
|
$current_time = new DateTime( 'now' );
|
|
$current_time->setTimezone( $timezone );
|
|
$time = $current_time->format( 'U' );
|
|
} else {
|
|
$time = $date_reference;
|
|
}
|
|
|
|
// Get post status Where snippet and associated SQL arguments
|
|
$where_parameters = $this->_get_post_status_sql();
|
|
$post_status_where = $where_parameters['post_status_where'];
|
|
|
|
// Get the Join (filter_join) and Where (filter_where) statements based
|
|
// on $filter elements specified
|
|
$filter = $this->_get_filter_sql( $filter );
|
|
|
|
// Query arguments
|
|
$args = array( $time );
|
|
$args = array_merge( $args, $where_parameters['args'] );
|
|
|
|
if ( 0 == $date_reference ) {
|
|
if ( $page_offset >= 0 ) {
|
|
$filter_date_clause = 'i.end >= %d ';
|
|
$order_direction = 'ASC';
|
|
} else {
|
|
$filter_date_clause = 'i.start < %d ';
|
|
$order_direction = 'DESC';
|
|
}
|
|
} else {
|
|
if ( $page_offset < 0 ) {
|
|
$filter_date_clause = 'i.end < %d ';
|
|
$order_direction = 'DESC';
|
|
} else {
|
|
$filter_date_clause = 'i.end >= %d ';
|
|
$order_direction = 'ASC';
|
|
}
|
|
}
|
|
if ( $page_offset >= 0 ) {
|
|
$first_record = $page_offset * $limit;
|
|
} else {
|
|
$first_record = ( - $page_offset - 1 ) * $limit;
|
|
}
|
|
$wpml_join_particle = $localization_helper->get_wpml_table_join( 'p.ID' );
|
|
$wpml_where_particle = $localization_helper->get_wpml_table_where();
|
|
|
|
$query = $this->_dbi->prepare(
|
|
'SELECT DISTINCT p.*, e.post_id, i.id AS instance_id, ' . 'i.start AS start, ' . 'i.end AS end, ' .
|
|
'e.allday AS event_allday, ' .
|
|
'e.recurrence_rules, e.exception_rules, e.ticket_url, e.instant_event, e.recurrence_dates, e.exception_dates, ' .
|
|
'e.venue, e.country, e.address, e.city, e.province, e.postal_code, ' .
|
|
'e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, ' .
|
|
'e.ical_feed_url, e.ical_source_url, e.ical_organizer, e.ical_contact, e.ical_uid, e.timezone_name, e.longitude, e.latitude ' .
|
|
'FROM ' . $this->_dbi->get_table_name( 'ai1ec_events' ) . ' e ' . 'INNER JOIN ' .
|
|
$this->_dbi->get_table_name( 'posts' ) . ' p ON e.post_id = p.ID ' . $wpml_join_particle .
|
|
' INNER JOIN ' . $this->_dbi->get_table_name( 'ai1ec_event_instances' ) . ' i ON e.post_id = i.post_id ' .
|
|
$filter['filter_join'] . " WHERE post_type = '" . AI1EC_POST_TYPE . "' " . ' AND ' . $filter_date_clause .
|
|
$wpml_where_particle . $filter['filter_where'] . $post_status_where .
|
|
( $unique ? ' GROUP BY e.post_id' : '' ) .
|
|
// Reverse order when viewing negative pages, to get correct set of
|
|
// records. Then reverse results later to order them properly.
|
|
' ORDER BY i.start ' . $order_direction . ', post_title ' . $order_direction . ' LIMIT ' . $first_record .
|
|
', ' . ( $limit + 1 ),
|
|
$args );
|
|
|
|
$events = $this->_dbi->get_results( $query, ARRAY_A );
|
|
|
|
if ( $page_offset >= 0 ) {
|
|
$prev = true;
|
|
$next = ( count( $events ) > $limit );
|
|
if ( $next ) {
|
|
array_pop( $events );
|
|
}
|
|
} else {
|
|
$prev = ( count( $events ) > $limit );
|
|
if ( $prev ) {
|
|
array_pop( $events );
|
|
}
|
|
$next = true;
|
|
}
|
|
|
|
// Reorder records if in negative page offset
|
|
if ( $page_offset < 0 ) {
|
|
$events = array_reverse( $events );
|
|
}
|
|
|
|
$date_first = $date_last = NULL;
|
|
|
|
foreach ( $events as &$event ) {
|
|
$event['allday'] = $this->_is_all_day( $event );
|
|
$event = $this->_registry->get( 'model.event', $event );
|
|
if ( null === $date_first ) {
|
|
$date_first = $event->get( 'start' );
|
|
}
|
|
$date_last = $event->get( 'start' );
|
|
}
|
|
$date_first = $this->_registry->get( 'date.time', $date_first );
|
|
$date_last = $this->_registry->get( 'date.time', $date_last );
|
|
|
|
return array(
|
|
'events' => $events,
|
|
'prev' => $prev,
|
|
'next' => $next,
|
|
'date_first' => $date_first,
|
|
'date_last' => $date_last );
|
|
}
|
|
|
|
/**
|
|
* Returns events for given day. Event must start before end of day and must
|
|
* ends after beginning of day.
|
|
*
|
|
* @param Ai1ec_Date_Time $day Date object.
|
|
* @param array $filter Search filters;
|
|
*
|
|
* @return array List of events.
|
|
*/
|
|
public function get_events_for_day(
|
|
Ai1ec_Date_Time $day,
|
|
array $filter = array()
|
|
) {
|
|
$end_of_day = $this->_registry->get( 'date.time', $day )
|
|
->set_time( 23, 59, 59 );
|
|
$start_of_day = $this->_registry->get( 'date.time', $day )
|
|
->set_time( 0, 0, 0 );
|
|
return $this->get_events_between(
|
|
$start_of_day,
|
|
$end_of_day,
|
|
$filter,
|
|
false,
|
|
true
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get ID of event in database, matching imported one.
|
|
*
|
|
* Return event ID by iCalendar UID, feed url, start time and whether the
|
|
* event has recurrence rules (to differentiate between an event with a UID
|
|
* defining the recurrence pattern, and other events with with the same UID,
|
|
* which are just RECURRENCE-IDs).
|
|
*
|
|
* @param int $uid iCalendar UID property
|
|
* @param string $feed Feed URL
|
|
* @param int $start Start timestamp (GMT)
|
|
* @param bool $has_recurrence Whether the event has recurrence rules
|
|
* @param int|null $exclude_post_id Do not match against this post ID
|
|
*
|
|
* @return object|null ID of matching event post, or NULL if no match
|
|
*/
|
|
public function get_matching_event_id(
|
|
$uid,
|
|
$feed,
|
|
$start,
|
|
$has_recurrence = false,
|
|
$exclude_post_id = null
|
|
) {
|
|
$dbi = $this->_registry->get( 'dbi.dbi' );
|
|
$table_name = $dbi->get_table_name( 'ai1ec_events' );
|
|
$query = 'SELECT `post_id` FROM ' . $table_name . '
|
|
WHERE
|
|
ical_feed_url = %s
|
|
AND ical_uid = %s
|
|
AND start = %d ' .
|
|
( $has_recurrence ? 'AND NOT ' : 'AND ' ) .
|
|
' ( recurrence_rules IS NULL OR recurrence_rules = \'\' )';
|
|
$args = array( $feed, $uid );
|
|
if ( $start instanceof Ai1ec_Date_Time ) {
|
|
$args[] = $start->format();
|
|
} else {
|
|
$args[] = (int)$start;
|
|
}
|
|
if ( null !== $exclude_post_id ) {
|
|
$query .= ' AND post_id <> %d';
|
|
$args[] = $exclude_post_id;
|
|
}
|
|
|
|
return $dbi->get_var( $dbi->prepare( $query, $args ) );
|
|
}
|
|
|
|
/**
|
|
* Get event by UID. UID must be unique.
|
|
*
|
|
* NOTICE: deletes events with that UID if they have different URLs.
|
|
*
|
|
* @param string $uid UID from feed.
|
|
* @param string $uid Feed URL.
|
|
*
|
|
* @return int|null Matching Event ID or NULL if none found.
|
|
*/
|
|
public function get_matching_event_by_uid_and_url( $uid, $url ) {
|
|
if ( ! isset( $uid{1} ) ) {
|
|
return null;
|
|
}
|
|
$dbi = $this->_registry->get( 'dbi.dbi' );
|
|
$table_name = $dbi->get_table_name( 'ai1ec_events' );
|
|
$argv = array( $url, $uid, $url );
|
|
// fix issue where invalid feed URLs were assigned
|
|
$update = 'UPDATE ' . $table_name . ' SET `ical_feed_url` = %s' .
|
|
' WHERE `ical_uid` = %s AND `ical_feed_url` != %s';
|
|
$query = $dbi->prepare( $update, $argv);
|
|
$success = $dbi->query( $query );
|
|
|
|
// retrieve actual feed ID if any
|
|
$select = 'SELECT `post_id` FROM `' . $table_name .
|
|
'` WHERE `ical_uid` = %s';
|
|
return $dbi->get_var( $dbi->prepare( $select, array( $uid ) ) );
|
|
}
|
|
|
|
/**
|
|
* Get event ids for the passed feed url
|
|
*
|
|
* @param string $feed_url
|
|
*/
|
|
public function get_event_ids_for_feed( $feed_url ) {
|
|
$dbi = $this->_registry->get( 'dbi.dbi' );
|
|
$table_name = $dbi->get_table_name( 'ai1ec_events' );
|
|
$query = 'SELECT `post_id` FROM ' . $table_name .
|
|
' WHERE ical_feed_url = %s';
|
|
return $dbi->get_col( $dbi->prepare( $query, array( $feed_url ) ) );
|
|
}
|
|
|
|
/**
|
|
* Returns events instances closest to today.
|
|
*
|
|
* @param array $events_ids Events ids filter.
|
|
*
|
|
* @return array Events collection.
|
|
* @throws Ai1ec_Bootstrap_Exception
|
|
*/
|
|
public function get_instances_closest_to_today( array $events_ids = array() ) {
|
|
$where_events_ids = '';
|
|
if ( ! empty( $events_ids ) ) {
|
|
$where_events_ids = 'i.post_id IN ('
|
|
. implode( ',', $events_ids ) . ') AND ';
|
|
}
|
|
$query = 'SELECT i.id, i.post_id FROM ' .
|
|
$this->_dbi->get_table_name( 'ai1ec_event_instances' ) .
|
|
' i WHERE ' .
|
|
$where_events_ids .
|
|
' i.start > %d ' .
|
|
' GROUP BY i.post_id';
|
|
/** @var $today Ai1ec_Date_Time */
|
|
$today = $this->_registry->get( 'date.time', 'now', 'sys.default' );
|
|
$today->set_time( 0, 0, 0 );
|
|
$query = $this->_dbi->prepare( $query, $today->format( 'U' ) );
|
|
$results = $this->_dbi->get_results( $query );
|
|
$events = array();
|
|
foreach ( $results as $result ) {
|
|
$events[] = $this->get_event(
|
|
$result->post_id,
|
|
$result->id
|
|
);
|
|
}
|
|
|
|
return $events;
|
|
}
|
|
|
|
/**
|
|
* Check if given event must be treated as all-day event.
|
|
*
|
|
* Event instances that span 24 hours are treated as all-day.
|
|
* NOTICE: event is passed in before being transformed into
|
|
* Ai1ec_Event object, with Ai1ec_Date_Time fields.
|
|
*
|
|
* @param array $event Event data returned from database.
|
|
*
|
|
* @return bool True if event is all-day event.
|
|
*/
|
|
protected function _is_all_day( array $event ) {
|
|
if ( isset( $event['event_allday'] ) && $event['event_allday'] ) {
|
|
return true;
|
|
}
|
|
|
|
if ( ! isset( $event['start'] ) || ! isset( $event['end'] ) ) {
|
|
return false;
|
|
}
|
|
|
|
return ( 86400 === $event['end'] - $event['start'] );
|
|
}
|
|
|
|
/**
|
|
* _limit_result_set function
|
|
*
|
|
* Slice given number of events from list, with exception when all
|
|
* events from last day shall be included.
|
|
*
|
|
* @param array $events List of events to slice
|
|
* @param int $limit Number of events to slice-off
|
|
* @param bool $last_day Set to true to include all events from last day ignoring {$limit}
|
|
*
|
|
* @return array Sliced events list
|
|
*/
|
|
protected function _limit_result_set(
|
|
array $events,
|
|
$limit,
|
|
$last_day
|
|
) {
|
|
$limited_events = array();
|
|
$start_day_previous = 0;
|
|
foreach ( $events as $event ) {
|
|
$start_day = date(
|
|
'Y-m-d',
|
|
$event['start']
|
|
);
|
|
--$limit; // $limit = $limit - 1;
|
|
if ( $limit < 0 ) {
|
|
if ( true === $last_day ) {
|
|
if ( $start_day != $start_day_previous ) {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
$limited_events[] = $event;
|
|
$start_day_previous = $start_day;
|
|
}
|
|
return $limited_events;
|
|
}
|
|
|
|
/**
|
|
* _get_post_status_sql function
|
|
*
|
|
* Returns SQL snippet for properly matching event posts, as well as array
|
|
* of arguments to pass to $this_dbi->prepare, in function argument
|
|
* references.
|
|
* Nothing is returned by the function.
|
|
*
|
|
* @return array An array containing post_status_where: the sql string,
|
|
* args: the arguments for prepare()
|
|
*/
|
|
protected function _get_post_status_sql() {
|
|
$args = array();
|
|
|
|
// Query the correct post status
|
|
if (
|
|
current_user_can( 'administrator' ) ||
|
|
current_user_can( 'editor' ) ||
|
|
current_user_can( 'read_private_ai1ec_events' )
|
|
) {
|
|
// User has privilege of seeing all published and private
|
|
$post_status_where = 'AND post_status IN ( %s, %s ) ';
|
|
$args[] = 'publish';
|
|
$args[] = 'private';
|
|
} elseif ( is_user_logged_in() ) {
|
|
// User has privilege of seeing all published and only their own
|
|
// private posts.
|
|
|
|
// Get user ID
|
|
$user_id = 0;
|
|
if ( is_callable( 'wp_get_current_user' ) ) {
|
|
$user = wp_get_current_user();
|
|
$user_id = (int)$user->ID;
|
|
unset( $user );
|
|
}
|
|
|
|
// include post_status = published
|
|
// OR
|
|
// post_status = private AND post_author = userID
|
|
$post_status_where =
|
|
'AND ( ' .
|
|
'post_status = %s ' .
|
|
'OR ( post_status = %s AND post_author = %d ) ' .
|
|
') ';
|
|
|
|
$args[] = 'publish';
|
|
$args[] = 'private';
|
|
$args[] = $user_id;
|
|
} else {
|
|
// User can only see published posts.
|
|
$post_status_where = 'AND post_status = %s ';
|
|
$args[] = 'publish';
|
|
}
|
|
|
|
return array(
|
|
'post_status_where' => $post_status_where,
|
|
'args' => $args
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Take filter and return SQL options.
|
|
*
|
|
* Takes an array of filtering options and turns it into JOIN and WHERE
|
|
* statements for running an SQL query limited to the specified options.
|
|
*
|
|
* @param array $filter Array of filters for the events returned:
|
|
* ['cat_ids'] => list of category IDs
|
|
* ['tag_ids'] => list of tag IDs
|
|
* ['post_ids'] => list of event post IDs
|
|
* ['auth_ids'] => list of event author IDs
|
|
* ['instance_ids'] => list of event instance IDs
|
|
*
|
|
* @return array The modified filter array to having:
|
|
* ['filter_join'] the Join statements for the SQL
|
|
* ['filter_where'] the Where statements for the SQL
|
|
*/
|
|
protected function _get_filter_sql( $filter ) {
|
|
$filter_join = $filter_where = array();
|
|
foreach ( $filter as $filter_type => $filter_ids ) {
|
|
$filter_object = null;
|
|
try {
|
|
if ( empty( $filter_ids ) ) {
|
|
$filter_ids = array();
|
|
}
|
|
$filter_object = $this->_registry->get(
|
|
'model.filter.' . $filter_type,
|
|
$filter_ids
|
|
);
|
|
if ( ! ( $filter_object instanceof Ai1ec_Filter_Interface ) ) {
|
|
throw new Ai1ec_Bootstrap_Exception(
|
|
'Filter \'' . get_class( $filter_object ) .
|
|
'\' is not instance of Ai1ec_Filter_Interface'
|
|
);
|
|
}
|
|
} catch ( Ai1ec_Bootstrap_Exception $exception ) {
|
|
continue;
|
|
}
|
|
$filter_join[] = $filter_object->get_join();
|
|
$filter_where[] = $filter_object->get_where();
|
|
}
|
|
|
|
$filter_join = array_filter( $filter_join );
|
|
$filter_where = array_filter( $filter_where );
|
|
$filter_join = join( ' ', $filter_join );
|
|
if ( count( $filter_where ) > 0 ) {
|
|
$operator = $this->get_distinct_types_operator();
|
|
$filter_where = $operator . '( ' .
|
|
implode( ' ) ' . $operator . ' ( ', $filter_where ) .
|
|
' ) ';
|
|
} else {
|
|
$filter_where = '';
|
|
}
|
|
|
|
return $filter + compact( 'filter_where', 'filter_join' );
|
|
}
|
|
|
|
/**
|
|
* Get operator for joining distinct filters in WHERE.
|
|
*
|
|
* @return string SQL operator.
|
|
*/
|
|
public function get_distinct_types_operator() {
|
|
static $operators = array( 'AND' => 1, 'OR' => 2 );
|
|
$default = 'AND';
|
|
$where_operator = strtoupper( trim( (string)apply_filters(
|
|
'ai1ec_filter_distinct_types_logic',
|
|
$default
|
|
) ) );
|
|
if ( ! isset( $operators[$where_operator] ) ) {
|
|
$where_operator = $default;
|
|
}
|
|
return $where_operator;
|
|
}
|
|
|
|
}
|