Add upstream plugins

Signed-off-by: Adrian Nöthlich <git@promasu.tech>
This commit is contained in:
2019-10-25 22:42:20 +02:00
parent 5d3c2ec184
commit 290736650a
1186 changed files with 302577 additions and 0 deletions

View File

@@ -0,0 +1,330 @@
<?php
/**
* The abstract class for a view.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View
*/
abstract class Ai1ec_Calendar_View_Abstract extends Ai1ec_Base {
/**
* @var Ai1ec_Request_Parser The request object
*/
protected $_request;
/**
* @var Ai1ec_Compatibility_Check Theme compatibility check object.
*/
protected $_compatibility;
/**
* Public constructor
*
* @param Ai1ec_Registry_Object $registry
* @param Ai1ec_Request_Parser $request
*/
public function __construct( Ai1ec_Registry_Object $registry, Ai1ec_Request_Parser $request ) {
parent::__construct( $registry );
$this->_request = $request;
$this->_compatibility = $registry->get( 'compatibility.check' );
}
/**
* Get the machine name for the view
*
* @return string The machine name of the view.
*/
abstract public function get_name();
/**
* Get extra arguments specific for the view
*
* @param array $view_args
* @param int|bool $exact_date the exact date used to display the view.
*
* @return array The view arguments with the extra parameters added.
*/
public function get_extra_arguments( array $view_args, $exact_date ) {
$offset = $this->get_name() . '_offset';
$view_args[$offset] = $this->_request->get( $offset );
if( false !== $exact_date ) {
$view_args['exact_date'] = $exact_date;
}
return $view_args;
}
/**
* Get extra arguments specific for the view's template
*
* @param array $args
*
* @return array The template arguments with the extra parameters added.
*/
public function get_extra_template_arguments( array $args ) {
$loader = $this->_registry->get( 'theme.loader' );
$args['action_buttons'] = apply_filters(
'ai1ec_add_action_buttons',
$this->_action_buttons()
);
if (
true === apply_filters(
'ai1ec_buy_button_product',
false
)
) {
$args['has_product_buy_button'] = true;
}
return $args;
}
/**
* Render the view and return the content
*
* @param array $view_args
*
* @return string the html of the view
*/
abstract public function get_content( array $view_args );
/**
*
* @return string HTML of action buttons
*/
protected function _action_buttons() {
$loader = $this->_registry->get( 'theme.loader' );
$action_buttons = $loader->get_file(
'buttons.twig',
array(
'action_buttons' => apply_filters(
'ai1ec_action_buttons',
''
),
'tickets_button' => true,
'text_tickets' => __( 'Tickets', AI1EC_PLUGIN_NAME ),
'has_buy_tickets_product' => apply_filters(
'ai1ec_buy_button_product',
false
)
),
false
)->get_content();
return $action_buttons;
}
/**
*
* @param string $exact_date
*/
protected function _create_link_for_day_view( $exact_date ) {
$href = $this->_registry->get(
'html.element.href',
array(
'action' => 'oneday',
'exact_date' => $exact_date,
)
);
return $href->generate_href();
}
/**
* Get the view html
*
* @param array $view_args
*
* @return string
*/
protected function _get_view( array $view_args ) {
$loader = $this->_registry->get( 'theme.loader' );
$view = $this->get_name();
$file = $loader->get_file( $view . '.twig', $view_args, false );
return apply_filters(
'ai1ec_get_' . $view . '_view',
$file->get_content(),
$view_args
);
}
/**
* Applies filters to view args for front end rendering
*
* @param array $args
*/
protected function _apply_filters_to_args( array $args ) {
$loader = $this->_registry->get( 'theme.loader' );
$view = $this->get_name();
return $loader->apply_filters_to_args( $args, $view . '.twig', false );
}
/**
* Prepare week specific event start/end timestamps.
*
* @param Ai1ec_Event $event Instance of event.
*
* @return array Start and end respectively in 0 and 1 positions.
*/
protected function _get_view_specific_timestamps( Ai1ec_Event $event ) {
if ( $event->is_allday() ) {
// reset to be day-contained with respect to current timezone
$event_start = $this->_registry
->get( 'date.time', $event->get( 'start' ), 'sys.default' )
->set_time( 0, 0, 0 )
->format();
$event_end = $this->_registry
->get( 'date.time', $event->get( 'end' ), 'sys.default' )
->set_time( 0, 0, 0 )
->format();
} else {
$event_start = $event->get( 'start' )->format();
$event_end = $event->get( 'end' )->format();
}
return array( $event_start, $event_end );
}
/**
* Update metadata for retrieved events.
*
* This speeds up further meta data requests.
*
* @param array $events List of events retrieved.
*
* @return void
*/
protected function _update_meta( array $events ) {
$post_ids = array();
foreach ( $events as $event ) {
$post_ids[] = (int)$event->get( 'post_id' );
}
update_meta_cache( 'post', $post_ids );
$this->_registry->get( 'model.taxonomy' )
->update_meta( $post_ids );
}
/**
* Gets the navigation bar HTML.
*
* @param array $nav_args Args for the navigation bar template, including
* 'no_navigation' which determines whether to show it
* @return string
*/
protected function _get_navigation( array $nav_args ) {
$navigation = '';
$loader = $this->_registry->get( 'theme.loader' );
$nav_args['contribution_buttons'] = apply_filters(
'ai1ec_contribution_buttons',
'',
'html',
'render-command'
);
if ( true !== $nav_args['no_navigation'] ) {
$navigation = $loader->get_file(
'navigation.twig',
$nav_args,
false
)->get_content();
}
return $navigation;
}
/**
* Calls the get_*_pagination_links method for the current view type and
* renders its result, returning the rendered pagination links.
*
* @param array $args Current request arguments
* @param string $title Title to display in datepicker button
* @return string
*/
protected function _get_pagination( array $args, $title ) {
$method = 'get_' . $this->get_name() . '_pagination_links';
$pagination_links = $this->$method( $args, $title );
$loader = $this->_registry->get( 'theme.loader' );
$pagination_links = $loader->get_file(
'pagination.twig',
array(
'links' => $pagination_links,
'data_type' => $args['data_type'],
),
false
)->get_content();
return $pagination_links;
}
/**
* Adds runtime properties to the event.
*
* @param Ai1ec_Event $event
*/
protected function _add_runtime_properties( Ai1ec_Event $event ) {
global $post;
$original_post = $post;
$post = $event->get( 'post' );
$instance_permalink = get_permalink(
$event->get( 'post_id' )
);
$instance_permalink = add_query_arg(
'instance_id',
$event->get( 'instance_id' ),
$instance_permalink
);
$event->set_runtime( 'instance_permalink', $instance_permalink );
$event->set_runtime(
'filtered_title',
apply_filters(
'the_title',
$event->get( 'post' )->post_title,
$event->get( 'post_id' ),
true
)
);
$calendar_state = $this->_registry->get( 'calendar.state' );
$calendar_state->set_append_content( false );
$event->set_runtime(
'filtered_content',
apply_filters(
'ai1ec_the_content',
apply_filters(
'the_content',
$event->get( 'post' )->post_content
)
)
);
$calendar_state->set_append_content( true );
$taxonomy = $this->_registry->get( 'view.event.taxonomy' );
$ticket = $this->_registry->get( 'view.event.ticket' );
$event->set_runtime(
'color_style',
$taxonomy->get_color_style( $event )
);
$event->set_runtime( 'category_colors', $taxonomy->get_category_colors( $event ) );
$event->set_runtime( 'ticket_url_label', $ticket->get_tickets_url_label( $event, false ) );
$event->set_runtime( 'edit_post_link', get_edit_post_link( $event->get( 'post_id' ) ) );
$event_post = $this->_registry->get( 'view.event.post' );
$event->set_runtime( 'post_excerpt', $event_post->trim_excerpt( $event ) );
$color = $this->_registry->get( 'view.event.color' );
$event->set_runtime( 'faded_color', $color->get_faded_color( $event ) );
$event->set_runtime( 'rgba_color', $color->get_rgba_color( $event ) );
$event->set_runtime(
'short_start_time',
$this->_registry->get( 'view.event.time' )
->get_short_time( $event->get( 'start' ) )
);
$this->_add_view_specific_runtime_properties( $event );
$post = $original_post;
}
/**
* If some views have specific runtime properties they must extend this method
*
* @param Ai1ec_Event $event
*/
protected function _add_view_specific_runtime_properties( Ai1ec_Event $event ) {
}
}

View File

@@ -0,0 +1,582 @@
<?php
/**
* The concrete class for agenda view.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View
*/
class Ai1ec_Calendar_View_Agenda extends Ai1ec_Calendar_View_Abstract {
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_name()
*/
public function get_name() {
return 'agenda';
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_content()
*/
public function get_content( array $view_args ) {
$type = $this->get_name();
$time = $this->_registry->get( 'date.system' );
// Get localized time
$timestamp = $time->current_time();
// Get events, then classify into date array
$per_page_setting = $type . '_events_per_page';
$search = $this->_registry->get( 'model.search' );
$settings = $this->_registry->get( 'model.settings' );
$events_limit = is_numeric( $view_args['events_limit'] )
? $view_args['events_limit']
: $settings->get( $per_page_setting );
$events_limit = apply_filters(
'ai1ec_events_limit',
$events_limit
);
$relative_to_reference = in_array( $this->get_name(), array( 'agenda', 'posterboard', 'stream' ) );
if ( $relative_to_reference ) {
$results = $search->get_events_relative_to_reference(
$view_args['time_limit'],
$events_limit,
$view_args['page_offset'],
apply_filters(
'ai1ec_get_events_relative_to_filter',
array(
'post_ids' => $view_args['post_ids'],
'auth_ids' => $view_args['auth_ids'],
'cat_ids' => $view_args['cat_ids'],
'tag_ids' => $view_args['tag_ids'],
'instance_ids' => $view_args['instance_ids'],
),
$view_args
),
apply_filters(
'ai1ec_show_unique_events',
false
)
);
} else {
$results = $search->get_events_relative_to(
$timestamp,
$events_limit,
$view_args['page_offset'],
apply_filters(
'ai1ec_get_events_relative_to_filter',
array(
'post_ids' => $view_args['post_ids'],
'auth_ids' => $view_args['auth_ids'],
'cat_ids' => $view_args['cat_ids'],
'tag_ids' => $view_args['tag_ids'],
'instance_ids' => $view_args['instance_ids'],
),
$view_args
),
$view_args['time_limit'],
apply_filters(
'ai1ec_show_unique_events',
false
)
);
}
$this->_update_meta( $results['events'] );
$dates = $this->get_agenda_like_date_array(
$results['events'],
$view_args['request']
);
// Generate title of view based on date range month & year.
$range_start = $results['date_first'] &&
false === $results['date_first']->is_empty() ?
$results['date_first'] :
$this->_registry->get( 'date.time', $timestamp );
$range_end = $results['date_last'] &&
false === $results['date_last']->is_empty() ?
$results['date_last'] :
$this->_registry->get( 'date.time', $timestamp );
$range_start = $this->_registry->get( 'date.time', $range_start );
$range_end = $this->_registry->get( 'date.time', $range_end );
$start_year = $range_start->format_i18n( 'Y' );
$end_year = $range_end->format_i18n( 'Y' );
$start_month = $range_start->format_i18n( 'F' );
$start_month_short = $range_start->format_i18n( 'M' );
$end_month = $range_end->format_i18n( 'F' );
$end_month_short = $range_end->format_i18n( 'M' );
if ( $start_year === $end_year && $start_month === $end_month ) {
$title = "$start_month $start_year";
$title_short = "$start_month_short $start_year";
} elseif ( $start_year === $end_year ) {
$title = "$start_month $end_month $end_year";
$title_short = "$start_month_short $end_month_short $end_year";
} else {
$title = "$start_month $start_year $end_month $end_year";
$title_short = "$start_month_short $start_year $end_month_short $end_year";
}
// Create navigation bar if requested.
$navigation = '';
$loader = $this->_registry->get( 'theme.loader' );
$pagination_links = '';
if ( ! $view_args['no_navigation'] ) {
if ( $relative_to_reference ) {
$pagination_links = $this->_get_pagination_links(
$view_args,
$results['prev'],
$results['next'],
$results['date_first'],
$results['date_last'],
$title,
$title_short,
$view_args['page_offset'] + -1,
$view_args['page_offset'] + 1
);
} else {
$pagination_links = $this->_get_agenda_like_pagination_links(
$view_args,
$results['prev'],
$results['next'],
$results['date_first'],
$results['date_last'],
$title,
$title_short,
null === $view_args['time_limit'] ||
0 === $view_args['time_limit'] ?
$timestamp :
$view_args['time_limit']
);
}
$pagination_links = $loader->get_file(
'pagination.twig',
array(
'links' => $pagination_links,
'data_type' => $view_args['data_type'],
),
false
)->get_content();
// Get HTML for navigation bar.
$nav_args = array(
'no_navigation' => $view_args['no_navigation'],
'pagination_links' => $pagination_links,
'views_dropdown' => $view_args['views_dropdown'],
'below_toolbar' => apply_filters(
'ai1ec_below_toolbar',
'',
$type,
$view_args
),
);
// Add extra buttons to Agenda view's nav bar if events were returned.
if ( $type === 'agenda' && $dates ) {
$button_args = array(
'text_collapse_all' => __( 'Collapse All', AI1EC_PLUGIN_NAME ),
'text_expand_all' => __( 'Expand All', AI1EC_PLUGIN_NAME ),
);
$nav_args['after_pagination'] = $loader
->get_file( 'agenda-buttons.twig', $button_args, false )
->get_content();
}
$navigation = $this->_get_navigation( $nav_args );
}
$is_ticket_button_enabled = apply_filters( 'ai1ec_' . $type . '_ticket_button', false );
$args = array(
'title' => $title,
'dates' => $dates,
'type' => $type,
'show_year_in_agenda_dates' => $settings->get( 'show_year_in_agenda_dates' ),
'expanded' => $settings->get( 'agenda_events_expanded' ),
'show_location_in_title' => $settings->get( 'show_location_in_title' ),
'page_offset' => $view_args['page_offset'],
'navigation' => $navigation,
'pagination_links' => $pagination_links,
'post_ids' => join( ',', $view_args['post_ids'] ),
'data_type' => $view_args['data_type'],
'is_ticket_button_enabled' => $is_ticket_button_enabled,
'text_upcoming_events' => __( 'There are no upcoming events to display at this time.', AI1EC_PLUGIN_NAME ),
'text_edit' => __( 'Edit', AI1EC_PLUGIN_NAME ),
'text_read_more' => __( 'Read more', AI1EC_PLUGIN_NAME ),
'text_categories' => __( 'Categories:', AI1EC_PLUGIN_NAME ),
'text_tags' => __( 'Tags:', AI1EC_PLUGIN_NAME ),
'text_venue_separator' => __( '@ %s', AI1EC_PLUGIN_NAME ),
);
// Allow child views to modify arguments passed to template.
$args = $this->get_extra_template_arguments( $args );
return
$this->_registry->get( 'http.request' )->is_json_required(
$view_args['request_format'], $type
)
? $loader->apply_filters_to_args( $args, $type . '.twig', false )
: $this->_get_view( $args );
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_extra_arguments()
*/
public function get_extra_arguments( array $view_args, $exact_date ) {
$view_args += $this->_request->get_dict( array(
'page_offset',
'time_limit',
) );
if( false !== $exact_date ) {
$view_args['time_limit'] = $exact_date;
}
return $view_args;
}
/**
* Breaks down the given ordered array of event objects into dates, and
* outputs an ordered array of two-element associative arrays in the
* following format:
* key: localized UNIX timestamp of date
* value:
* ['events'] => two-element associatative array broken down thus:
* ['allday'] => all-day events occurring on this day
* ['notallday'] => all other events occurring on this day
* ['today'] => whether or not this date is today
*
* @param array $events Event results
* @param Ai1ec_Abstract_Query|null $query Current calendar page request, if
* any (null for widget)
*
* @return array
*/
public function get_agenda_like_date_array(
$events,
Ai1ec_Abstract_Query $query = null
) {
$dates = array();
$time = $this->_registry->get( 'date.system' );
$settings = $this->_registry->get( 'model.settings' );
$this->_registry->get( 'controller.content-filter' )
->clear_the_content_filters();
// Classify each event into a date/allday category
foreach ( $events as $event ) {
$start_time = $this->_registry
->get(
'date.time',
$event->get( 'start' )->format( 'Y-m-d\T00:00:00' ),
'sys.default'
);
$exact_date = $time->format_datetime_for_url(
$start_time,
$settings->get( 'input_date_format' )
);
$href_for_date = $this->_create_link_for_day_view( $exact_date );
// timestamp is used to have correctly sorted array as UNIX
// timestamp never goes in decreasing order for increasing dates.
$timestamp = $start_time->format();
// Ensure all-day & non all-day categories are created in correct
// order: "allday" preceding "notallday".
if ( ! isset( $dates[$timestamp]['events'] ) ) {
$dates[$timestamp]['events'] = array(
'allday' => array(),
'notallday' => array(),
);
}
$this->_add_runtime_properties( $event );
// Add the event.
$category = $event->is_allday()
? 'allday'
: 'notallday';
$event_props = array();
$event_props['post_id'] = $event->get( 'post_id' );
$event_props['instance_id'] = $event->get( 'instance_id' );
$event_props['venue'] = $event->get( 'venue' );
$event_props['ticket_url'] = $event->get( 'ticket_url' );
$event_props['filtered_title'] = $event->get_runtime( 'filtered_title' );
$event_props['edit_post_link'] = $event->get_runtime( 'edit_post_link' );
$event_props['content_img_url'] = $event->get_runtime( 'content_img_url' );
$event_props['filtered_content'] = $event->get_runtime( 'filtered_content' );
$event_props['ticket_url_label'] = $event->get_runtime( 'ticket_url_label' );
$event_props['permalink'] = $event->get_runtime( 'instance_permalink' );
$event_props['categories_html'] = $event->get_runtime( 'categories_html' );
$event_props['category_bg_color'] = $event->get_runtime( 'category_bg_color' );
$event_props['category_text_color'] = $event->get_runtime( 'category_text_color' );
$event_props['tags_html'] = $event->get_runtime( 'tags_html' );
$event_props['post_excerpt'] = $event->get_runtime( 'post_excerpt' );
$event_props['short_start_time'] = $event->get_runtime( 'short_start_time' );
$event_props['is_allday'] = $event->is_allday();
$event_props['is_multiday'] = $event->is_multiday();
$event_props['enddate_info'] = $event->getenddate_info();
$event_props['timespan_short'] = $event->_registry->
get( 'view.event.time' )->get_timespan_html( $event, 'short' );
$event_props['avatar'] = $event->getavatar();
$event_props['avatar_not_wrapped'] = $event->getavatar( false );
$event_props['avatar_url'] = $this->_registry
->get( 'view.event.avatar' )->get_event_avatar_url( $event );
$event_props['category_divider_color'] = $event->get_runtime(
'category_divider_color'
);
$meta = $this->_registry->get( 'model.meta-post' );
if ( ! $event_props['ticket_url'] ) {
$timely_tickets = $meta->get(
$event->get( 'post_id' ),
'_ai1ec_timely_tickets_url',
null
);
if ( $timely_tickets ) {
$event_props['ticket_url'] = $timely_tickets;
$event->set( 'ticket_url', $event_props['ticket_url'] );
}
}
if (
true === apply_filters(
'ai1ec_buy_button_product',
false
)
) {
$full_details = $meta->get(
$event->get( 'post_id' ),
'_ai1ec_ep_product_details',
null
);
if (
is_array( $full_details ) &&
isset( $full_details['show_buy_button'] ) &&
true === $full_details['show_buy_button']
&& $event_props['ticket_url']
) {
// Tickets button is shown by default in this case.
} else {
// Otherwise not.
$event_props['ticket_url'] = false;
}
$event->set( 'ticket_url', $event_props['ticket_url'] );
}
$event_object = $event_props;
if (
$this->_compatibility->use_backward_compatibility()
) {
$event_object = $event;
}
$months = apply_filters( 'ai1ec_i18n_months', array() );
$weekdays = apply_filters( 'ai1ec_i18n_weekdays', array() );
$dates[$timestamp]['events'][$category][] = $event_object;
$dates[$timestamp]['href'] = $href_for_date;
$dates[$timestamp]['day'] = $this->_registry->
get( 'date.time', $timestamp )->format_i18n( 'j' );
$w = $this->
_registry->get( 'date.time', $timestamp )->format_i18n( 'D' );
$dates[$timestamp]['weekday'] = array_key_exists( $w, $weekdays ) ? $weekdays[$w] : $w;
$m = $this->
_registry->get( 'date.time', $timestamp )->format_i18n( 'M' );
$dates[$timestamp]['month'] = array_key_exists( $m, $months ) ? $months[$m] : $m;
$this->_registry->
get( 'date.time', $timestamp )->format_i18n( 'M' );
$dates[$timestamp]['full_month'] = $this->_registry->
get( 'date.time', $timestamp )->format_i18n( 'F' );
$dates[$timestamp]['full_weekday'] = $this->_registry->
get( 'date.time', $timestamp )->format_i18n( 'l' );
$dates[$timestamp]['year'] = $this->_registry->
get( 'date.time', $timestamp )->format_i18n( 'Y' );
}
$this->_registry->get( 'controller.content-filter' )
->restore_the_content_filters();
// Flag today
$today = $this->_registry->get( 'date.time', 'now', 'sys.default' )
->set_time( 0, 0, 0 )
->format();
if ( isset( $dates[$today] ) ) {
$dates[$today]['today'] = true;
}
return $dates;
}
/**
* Returns an associative array of two links for any agenda-like view of the
* calendar:
* previous page (if previous events exist),
* next page (if next events exist).
* Each element is an associative array containing the link's enabled status
* ['enabled'], CSS class ['class'], text ['text'] and value to assign to
* link's href ['href'].
*
* @param array $args Current request arguments
*
* @param bool $prev Whether there are more events before
* the current page
* @param bool $next Whether there are more events after
* the current page
* @param Ai1ec_Date_Time|null $date_first
* @param Ai1ec_Date_Time|null $date_last
* @param string $title Title to display in datepicker button
* @param string $title_short Short month names.
* @param int|null $default_time_limit The default time limit in the case of pagination ends.
* @return array Array of links
*/
protected function _get_agenda_like_pagination_links(
$args,
$prev = false,
$next = false,
$date_first = null,
$date_last = null,
$title = '',
$title_short = '',
$default_time_limit = 0
) {
$links = array();
if (
$this->_registry->get(
'model.settings'
)->get( 'ai1ec_use_frontend_rendering' )
) {
$args['request_format'] = 'json';
}
$args['page_offset'] = -1;
if ( null === $date_first || $date_first->is_empty() ) {
$args['time_limit'] = $default_time_limit;
} else {
$args['time_limit'] = $this->_registry
->get( 'date.time', $date_first )->set_time(
$date_first->format( 'H' ),
$date_first->format( 'i' ),
$date_first->format( 's' ) - 1
)->format_to_gmt();
}
$href = $this->_registry->get(
'html.element.href',
$args
);
$links[] = array(
'class' => 'ai1ec-prev-page',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-left"></i>',
'href' => $href->generate_href(),
'enabled' => $prev,
);
// Minical datepicker.
$factory = $this->_registry->get( 'factory.html' );
$links[] = $factory->create_datepicker_link(
$args,
$date_first->format_to_gmt(),
$title,
$title_short
);
$args['page_offset'] = 1;
if ( null === $date_last || $date_last->is_empty() ) {
$args['time_limit'] = $default_time_limit;
} else {
$args['time_limit'] = $this->_registry
->get( 'date.time', $date_last )->set_time(
$date_last->format( 'H' ),
$date_last->format( 'i' ),
$date_last->format( 's' ) + 1
)->format_to_gmt();
}
$href = $this->_registry->get(
'html.element.href',
$args
);
$links[] = array(
'class' => 'ai1ec-next-page',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-right"></i>',
'href' => $href->generate_href(),
'enabled' => $next,
);
return $links;
}
/**
* Returns an associative array of two links for any agenda-like view of the
* calendar:
* previous page (if previous events exist),
* next page (if next events exist).
* Each element is an associative array containing the link's enabled status
* ['enabled'], CSS class ['class'], text ['text'] and value to assign to
* link's href ['href'].
*
* @param array $args Current request arguments
*
* @param bool $prev Whether there are more events before
* the current page
* @param bool $next Whether there are more events after
* the current page
* @param Ai1ec_Date_Time|null $date_first
* @param Ai1ec_Date_Time|null $date_last
* @param string $title Title to display in datepicker button
* @param string $title_short Short month names.
* @param int|null $default_time_limit The default time limit in the case of pagination ends.
* @return array Array of links
*/
protected function _get_pagination_links(
$args,
$prev = false,
$next = false,
$date_first = null,
$date_last = null,
$title = '',
$title_short = '',
$prev_offset = -1,
$next_offset = 1) {
$links = array();
if ( $this->_registry->get( 'model.settings' )->get( 'ai1ec_use_frontend_rendering' ) ) {
$args['request_format'] = 'json';
}
$args['page_offset'] = $prev_offset;
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'class' => 'ai1ec-prev-page',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-left"></i>',
'href' => $href->generate_href(),
'enabled' => $prev );
// Minical datepicker.
$factory = $this->_registry->get( 'factory.html' );
$links[] = $factory->create_datepicker_link( $args, $date_first->format_to_gmt(), $title, $title_short );
$args['page_offset'] = $next_offset;
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'class' => 'ai1ec-next-page',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-right"></i>',
'href' => $href->generate_href(),
'enabled' => $next );
return $links;
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::_add_view_specific_runtime_properties()
*/
protected function _add_view_specific_runtime_properties( Ai1ec_Event $event ) {
$taxonomy = $this->_registry->get( 'view.event.taxonomy' );
$avatar = $this->_registry->get( 'view.event.avatar' );
$event->set_runtime(
'categories_html',
$taxonomy->get_categories_html( $event )
);
$event->set_runtime(
'tags_html',
$taxonomy->get_tags_html( $event )
);
$event->set_runtime(
'content_img_url',
$avatar->get_content_img_url( $event )
);
}
}

View File

@@ -0,0 +1,573 @@
<?php
/**
* The concrete class for month view.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View
*/
class Ai1ec_Calendar_View_Month extends Ai1ec_Calendar_View_Abstract {
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_name()
*/
public function get_name() {
return 'month';
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_content()
*/
public function get_content( array $view_args ) {
$date_system = $this->_registry->get( 'date.system' );
$settings = $this->_registry->get( 'model.settings' );
$defaults = array(
'month_offset' => 0,
'cat_ids' => array(),
'auth_ids' => array(),
'tag_ids' => array(),
'post_ids' => array(),
'instance_ids' => array(),
'exact_date' => $date_system->current_time(),
);
$args = wp_parse_args( $view_args, $defaults );
$local_date = $this->_registry
->get( 'date.time', $args['exact_date'], 'sys.default' );
$local_date->set_date(
$local_date->format( 'Y' ),
$local_date->format( 'm' ) + $args['month_offset'],
1
)
->set_time( 0, 0, 0 );
$days_events = $this->get_events_for_month(
$local_date,
apply_filters(
'ai1ec_get_events_relative_to_filter',
array(
'cat_ids' => $args['cat_ids'],
'tag_ids' => $args['tag_ids'],
'post_ids' => $args['post_ids'],
'auth_ids' => $args['auth_ids'],
'instance_ids' => $args['instance_ids'],
),
$view_args,
apply_filters(
'ai1ec_show_unique_events',
false
)
)
);
$cell_array = $this->get_month_cell_array(
$local_date,
$days_events
);
// Create pagination links.
$title = $local_date->format_i18n( 'F Y' );
$pagination_links = $this->_get_pagination( $args, $title );
$is_ticket_button_enabled = apply_filters(
'ai1ec_month_ticket_button',
false
);
$view_args = array(
'title' => $title,
'type' => 'month',
'weekdays' => $this->get_weekdays(),
'cell_array' => $cell_array,
'show_location_in_title' => $settings->get( 'show_location_in_title' ),
'month_word_wrap' => $settings->get( 'month_word_wrap' ),
'post_ids' => join( ',', $args['post_ids'] ),
'data_type' => $args['data_type'],
'is_ticket_button_enabled' => $is_ticket_button_enabled,
'text_venue_separator' => __( '@ %s', AI1EC_PLUGIN_NAME ),
'pagination_links' => $pagination_links,
);
// Add navigation if requested.
$view_args['navigation'] = $this->_get_navigation(
array(
'no_navigation' => $args['no_navigation'],
'pagination_links' => $pagination_links,
'views_dropdown' => $args['views_dropdown'],
'below_toolbar' => apply_filters(
'ai1ec_below_toolbar',
'',
$this->get_name(),
$args
),
)
);
$view_args = $this->get_extra_template_arguments( $view_args );
return
$this->_registry->get( 'http.request' )->is_json_required(
$args['request_format'], 'month'
)
? $this->_apply_filters_to_args( $view_args )
: $this->_get_view( $view_args );
}
/**
* Returns a non-associative array of four links for the month view of the
* calendar:
* previous year, previous month, next month, and next year.
* Each element is an associative array containing the link's enabled status
* ['enabled'], CSS class ['class'], text ['text'] and value to assign to
* link's href ['href'].
*
* @param array $args Current request arguments
* @param string $title Title to display in datepicker button
*
* @return array Array of links
*/
function get_month_pagination_links( $args, $title ) {
$links = array();
$local_date = $this->_registry
->get( 'date.time', $args['exact_date'], 'sys.default' );
$orig_date = $this->_registry->get( 'date.time', $local_date );
$default_tz = $this->_registry->get( 'date.timezone' )->get_default_timezone();
// =================
// = Previous year =
// =================
// Align date to first of month, month offset applied, 1 year behind.
$local_date
->set_timezone( $default_tz )
->set_date(
$local_date->format( 'Y' ) -1,
$local_date->format( 'm' ) + $args['month_offset'],
1
)
->set_time( 0, 0, 0 );
$args['exact_date'] = $local_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'enabled' => true,
'class'=> 'ai1ec-prev-year',
'text' => '<i class="ai1ec-fa ai1ec-fa-angle-double-left"></i> ' .
$local_date->format_i18n( 'Y' ),
'href' => $href->generate_href(),
);
// ==================
// = Previous month =
// ==================
// Align date to first of month, month offset applied, 1 month behind.
$local_date
->set_date(
$local_date->format( 'Y' ) + 1,
$local_date->format( 'm' ) - 1,
1
);
$args['exact_date'] = $local_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$months = apply_filters( 'ai1ec_i18n_months', array() );
$m = $local_date->format_i18n( 'M' );
$month_text = array_key_exists( $m, $months ) ? $months[$m] : $m;
$links[] = array(
'enabled' => true,
'class'=> 'ai1ec-prev-month',
'text' => '<i class="ai1ec-fa ai1ec-fa-angle-left"></i> ' . $month_text,
'href' => $href->generate_href(),
);
// ======================
// = Minical datepicker =
// ======================
// Align date to first of month, month offset applied.
$orig_date
->set_timezone('UTC')
->set_date(
$orig_date->format( 'Y' ),
$orig_date->format( 'm' ) + $args['month_offset'],
1
);
$args['exact_date'] = $orig_date->format();
$factory = $this->_registry->get( 'factory.html' );
$links[] = $factory->create_datepicker_link(
$args,
$args['exact_date'],
$title
);
// ==============
// = Next month =
// ==============
// Align date to first of month, month offset applied, 1 month ahead.
$orig_date
->set_timezone( $default_tz )
->set_date(
$orig_date->format( 'Y' ),
$orig_date->format( 'm' ) + 1,
1
)
->set_time( 0, 0, 0 );
$args['exact_date'] = $orig_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$m = $orig_date->format_i18n( 'M' );
$links[] = array(
'enabled' => true,
'class'=> 'ai1ec-next-month',
'text' => ( array_key_exists( $m, $months ) ? $months[$m] : $m ) .
' <i class="ai1ec-fa ai1ec-fa-angle-right"></i>',
'href' => $href->generate_href(),
);
// =============
// = Next year =
// =============
// Align date to first of month, month offset applied, 1 year ahead.
$orig_date
->set_date(
$orig_date->format( 'Y' ) + 1,
$orig_date->format( 'm' ) - 1,
1
);
$args['exact_date'] = $orig_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'enabled' => true,
'class'=> 'ai1ec-next-year',
'text' => $orig_date->format_i18n( 'Y' ) .
' <i class="ai1ec-fa ai1ec-fa-angle-double-right"></i>',
'href' => $href->generate_href(),
);
return $links;
}
/**
* get_weekdays function
*
* Returns a list of abbreviated weekday names starting on the configured
* week start day setting.
*
* @return array
*/
protected function get_weekdays() {
$settings = $this->_registry->get( 'model.settings' );
static $weekdays;
if ( ! isset( $weekdays ) ) {
$time = $this->_registry->get(
'date.time',
'next Sunday',
'sys.default'
);
$time->adjust_day( $settings->get( 'week_start_day' ) );
$weekdays = array();
for( $i = 0; $i < 7; $i++ ) {
$weekdays[] = $time->format_i18n( 'D' );
$time->adjust_day( 1 );// Add a day
}
}
return $weekdays;
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::_add_view_specific_runtime_properties()
*/
protected function _add_view_specific_runtime_properties(
Ai1ec_Event $event
) {
$end_day = $this->_registry->get( 'date.time', $event->get( 'end' ) )
->adjust( -1, 'second' )
->format_i18n( 'd' );
$event->set_runtime( 'multiday_end_day', $end_day );
$event->set_runtime(
'start_day',
$event->get( 'start' )->format( 'j' )
);
}
/**
* get_month_cell_array function
*
* Return an array of weeks, each containing an array of days, each
* containing the date for the day ['date'] (if inside the month) and
* the events ['events'] (if any) for the day, and a boolean ['today']
* indicating whether that day is today.
*
* @param int $timestamp UNIX timestamp of the 1st day of the desired
* month to display
* @param array $days_events list of events for each day of the month in
* the format returned by get_events_for_month()
*
* @return void
*/
protected function get_month_cell_array( Ai1ec_Date_Time $timestamp, $days_events ) {
$settings = $this->_registry->get( 'model.settings' );
$date_system = $this->_registry->get( 'date.system' );
$today = $this->_registry->get( 'date.time' );// Used to flag today's cell
// Figure out index of first table cell
$first_cell_index = $timestamp->format( 'w' );
// Modify weekday based on start of week setting
$first_cell_index = ( 7 + $first_cell_index - $settings->get( 'week_start_day' ) ) % 7;
// Get the last day of the month
$last_day = $timestamp->format( 't' );
$last_timestamp = $this->_registry->get( 'date.time', $timestamp );
$last_timestamp->set_date(
$timestamp->format( 'Y' ),
$timestamp->format( 'm' ),
$last_day
)->set_time( 0, 0, 0 );
// Figure out index of last table cell
$last_cell_index = $last_timestamp->format( 'w' );
// Modify weekday based on start of week setting
$last_cell_index = ( 7 + $last_cell_index - $settings->get( 'week_start_day' ) ) % 7;
$weeks = array();
$week = 0;
$weeks[$week] = array();
// Insert any needed blank cells into first week
for( $i = 0; $i < $first_cell_index; $i++ ) {
$weeks[$week][] = array(
'date' => null,
'events' => array(),
'date_link' => null
);
}
// Insert each month's day and associated events
for( $i = 1; $i <= $last_day; $i++ ) {
$day = $this->_registry->get( 'date.time' )
->set_date(
$timestamp->format( 'Y' ),
$timestamp->format( 'm' ),
$i
)
->set_time( 0, 0, 0 )
->format();
$exact_date = $date_system->format_date_for_url(
$day,
$settings->get( 'input_date_format' )
);
$events = array();
foreach ( $days_events[$i] as $evt ){
$event_data = array(
'filtered_title' => $evt->get_runtime( 'filtered_title' ),
'post_excerpt' => $evt->get_runtime( 'post_excerpt' ),
'color_style' => $evt->get_runtime( 'color_style' ),
'category_colors' => $evt->get_runtime( 'category_colors' ),
'permalink' => $evt->get_runtime( 'instance_permalink' ),
'ticket_url_label' => $evt->get_runtime( 'ticket_url_label' ),
'edit_post_link' => $evt->get_runtime( 'edit_post_link' ),
'short_start_time' => $evt->get_runtime( 'short_start_time' ),
'multiday_end_day' => $evt->get_runtime( 'multiday_end_day' ),
'start_day' => $evt->get_runtime( 'start_day' ),
'short_start_time' => $evt->get_runtime( 'short_start_time' ),
'instance_id' => $evt->get( 'instance_id' ),
'post_id' => $evt->get( 'post_id' ),
'is_allday' => $evt->is_allday(),
'is_multiday' => $evt->is_multiday(),
'venue' => $evt->get( 'venue' ),
'ticket_url' => $evt->get( 'ticket_url' ),
'start_truncated' => $evt->get( 'start_truncated' ),
'end_truncated' => $evt->get( 'end_truncated' ),
'popup_timespan' => $this->_registry
->get( 'twig.ai1ec-extension')->timespan( $evt, 'short' ),
'avatar_not_wrapped' => $evt->getavatar( false ),
'avatar' => $this->_registry
->get( 'twig.ai1ec-extension')->avatar(
$evt,
array(
'post_thumbnail',
'content_img',
'location_avatar',
'category_avatar',
),
'',
false ),
);
$meta = $this->_registry->get( 'model.meta-post' );
if ( ! $event_data['ticket_url'] ) {
$timely_tickets = $meta->get(
$evt->get( 'post_id' ),
'_ai1ec_timely_tickets_url',
null
);
if ( $timely_tickets ) {
$event_data['ticket_url'] = $timely_tickets;
$evt->set( 'ticket_url', $event_data['ticket_url'] );
}
}
if (
true === apply_filters(
'ai1ec_buy_button_product',
false
)
) {
$full_details = $meta->get(
$evt->get( 'post_id' ),
'_ai1ec_ep_product_details',
null
);
if (
is_array( $full_details ) &&
isset( $full_details['show_buy_button'] ) &&
true === $full_details['show_buy_button']
&& $event_data['ticket_url']
) {
// Tickets button is shown by default in this case.
} else {
// Otherwise not.
$event_data['ticket_url'] = false;
}
$evt->set( 'ticket_url', $event_data['ticket_url'] );
}
if (
$this->_compatibility->use_backward_compatibility()
) {
$event_data = $evt;
}
$events[] = $event_data;
}
$weeks[$week][] = array(
'date' => $i,
'date_link' => $this->_create_link_for_day_view( $exact_date ),
'today' =>
$timestamp->format( 'Y' ) == $today->format( 'Y' ) &&
$timestamp->format( 'm' ) == $today->format( 'm' ) &&
$i == $today->format( 'j' ),
'events' => $events,
);
// If reached the end of the week, increment week
if( count( $weeks[$week] ) == 7 )
$week++;
}
// Insert any needed blank cells into last week
for( $i = $last_cell_index + 1; $i < 7; $i++ ) {
$weeks[$week][] = array( 'date' => null, 'events' => array() );
}
return $weeks;
}
/**
* get_events_for_month function
*
* Return an array of all dates for the given month as an associative
* array, with each element's value being another array of event objects
* representing the events occuring on that date.
*
* @param int $time the UNIX timestamp of a date within the desired month
* @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
*
* @return array array of arrays as per function's description
*/
protected function get_events_for_month(
Ai1ec_Date_Time $time,
$filter = array()
) {
$last_day = $time->format( 't' );
$day_entry = array(
'multi' => array(),
'allday' => array(),
'other' => array(),
);
$days_events = array_fill(
1,
$last_day,
$day_entry
);
unset( $day_entry );
$start_time = $this->_registry->get( 'date.time', $time );
$start_time->set_date(
$time->format( 'Y' ),
$time->format( 'm' ),
1
)->set_time( 0, 0, 0 );
$end_time = $this->_registry->get( 'date.time', $start_time );
$end_time->adjust_month( 1 );
$search = $this->_registry->get( 'model.search' );
$month_events = $search->get_events_between(
$start_time,
$end_time,
$filter,
true
);
$start_time = $start_time->format();
$end_time = $end_time->format();
$this->_update_meta( $month_events );
$this->_registry->get( 'controller.content-filter' )
->clear_the_content_filters();
foreach ( $month_events as $event ) {
$event_start = $event->get( 'start' )->format();
$event_end = $event->get( 'end' )->format();
/**
* REASONING: we assume, that event spans multiple periods, one of
* which happens to be current (month). Thus we mark, that current
* event starts at the very first day of current month and further
* we will mark it as having truncated beginning (unless it is not
* overlapping period boundaries).
* Although, if event starts after the first second of this period
* it's start day will be decoded as time 'j' format (`int`-casted
* to increase map access time), of it's actual start time.
*/
$day = 1;
if ( $event_start > $start_time ) {
$day = (int)$event->get( 'start' )->format( 'j' );
}
// Set multiday properties. TODO: Should these be made event object
// properties? They probably shouldn't be saved to the DB, so I'm
// not sure. Just creating properties dynamically for now.
if ( $event_start < $start_time ) {
$event->set( 'start_truncated', true );
}
if ( $event_end >= $end_time ) {
$event->set( 'end_truncated', true );
}
// Categorize event.
$priority = 'other';
if ( $event->is_allday() ) {
$priority = 'allday';
} elseif ( $event->is_multiday() ) {
$priority = 'multi';
}
$this->_add_runtime_properties( $event );
$days_events[$day][$priority][] = $event;
}
$this->_registry->get( 'controller.content-filter' )
->restore_the_content_filters();
for ( $day = 1; $day <= $last_day; $day++ ) {
$days_events[$day] = array_merge(
$days_events[$day]['multi'],
$days_events[$day]['allday'],
$days_events[$day]['other']
);
}
return apply_filters(
'ai1ec_get_events_for_month',
$days_events,
$time,
$filter
);
}
}

View File

@@ -0,0 +1,437 @@
<?php
/**
* The concrete class for day view.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View
*/
class Ai1ec_Calendar_View_Oneday extends Ai1ec_Calendar_View_Abstract {
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_name()
*/
public function get_name() {
return 'oneday';
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_content()
*/
public function get_content( array $view_args ) {
$date_system = $this->_registry->get( 'date.system' );
$settings = $this->_registry->get( 'model.settings' );
$defaults = array(
'oneday_offset' => 0,
'cat_ids' => array(),
'tag_ids' => array(),
'auth_ids' => array(),
'post_ids' => array(),
'instance_ids' => array(),
'exact_date' => $date_system->current_time(),
);
$args = wp_parse_args( $view_args, $defaults );
$local_date = $this->_registry
->get( 'date.time', $args['exact_date'], 'sys.default' )
->adjust_day( 0 + $args['oneday_offset'] )
->set_time( 0, 0, 0 );
$cell_array = $this->get_oneday_cell_array(
$local_date,
apply_filters(
'ai1ec_get_events_relative_to_filter',
array(
'cat_ids' => $args['cat_ids'],
'tag_ids' => $args['tag_ids'],
'post_ids' => $args['post_ids'],
'auth_ids' => $args['auth_ids'],
'instance_ids' => $args['instance_ids'],
),
$view_args,
apply_filters(
'ai1ec_show_unique_events',
false
)
)
);
// Create pagination links.
$title = $local_date->format_i18n(
$this->_registry->get( 'model.option' )
->get( 'date_format', 'l, M j, Y' )
);
$pagination_links = $this->_get_pagination( $args, $title );
// Calculate today marker's position.
$midnight = $this->_registry->get( 'date.time', 'now', 'sys.default' )
->set_time( 0, 0, 0 );
$now = $this->_registry->get( 'date.time', 'now', 'sys.default' );
$now_text = $this->_registry->get( 'view.event.time' )
->get_short_time( $now );
$now = (int) ( $now->diff_sec( $midnight ) / 60 );
$is_ticket_button_enabled = apply_filters( 'ai1ec_oneday_ticket_button', false );
$show_reveal_button = apply_filters( 'ai1ec_oneday_reveal_button', false );
$time_format = $this->_registry->get( 'model.option' )
->get( 'time_format', Ai1ec_I18n::__( 'g a' ) );
$hours = array();
$today = $this->_registry->get( 'date.time', 'now', 'sys.default' );
for ( $hour = 0; $hour < 24; $hour++ ) {
$hours[] = $today
->set_time( $hour, 0, 0 )
->format_i18n( $time_format );
}
$view_args = array(
'title' => $title,
'type' => 'oneday',
'cell_array' => $cell_array,
'show_location_in_title' => $settings->get( 'show_location_in_title' ),
'now_top' => $now,
'now_text' => $now_text,
'time_format' => $time_format,
'done_allday_label' => false,// legacy
'done_grid' => false,// legacy
'data_type' => $args['data_type'],
'is_ticket_button_enabled' => $is_ticket_button_enabled,
'show_reveal_button' => $show_reveal_button,
'text_full_day' => __( 'Reveal full day', AI1EC_PLUGIN_NAME ),
'text_all_day' => __( 'All-day', AI1EC_PLUGIN_NAME ),
'text_now_label' => __( 'Now:', AI1EC_PLUGIN_NAME ),
'text_venue_separator' => __( '@ %s', AI1EC_PLUGIN_NAME ),
'hours' => $hours,
'indent_multiplier' => 16,
'indent_offset' => 54,
'pagination_links' => $pagination_links,
);
$view_args = $this->get_extra_template_arguments( $view_args );
// Add navigation if requested.
$view_args['navigation'] = $this->_get_navigation(
array(
'no_navigation' => $args['no_navigation'],
'pagination_links' => $pagination_links,
'views_dropdown' => $args['views_dropdown'],
'below_toolbar' => apply_filters(
'ai1ec_below_toolbar',
'',
$this->get_name(),
$args
),
)
);
return
$this->_registry->get( 'http.request' )->is_json_required(
$args['request_format'], 'oneday'
)
? $this->_apply_filters_to_args( $view_args )
: $this->_get_view( $view_args );
}
/**
* Produce an array of three links for the day view of the calendar.
*
* Each element is an associative array containing the link's enabled status
* ['enabled'], CSS class ['class'], text ['text'] and value to assign to
* link's href ['href'].
*
* @param array $args Current request arguments.
* @param string $title Title to display in datepicker button
*
* @return array Array of links.
*/
function get_oneday_pagination_links( $args, $title ) {
$links = array();
$orig_date = $args['exact_date'];
// ================
// = Previous day =
// ================
$local_date = $this->_registry
->get( 'date.time', $args['exact_date'], 'sys.default' )
->adjust_day( $args['oneday_offset'] - 1 )
->set_time( 0, 0, 0 );
$args['exact_date'] = $local_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'enabled' => true,
'class'=> 'ai1ec-prev-day',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-left"></i>',
'href' => $href->generate_href(),
);
// ======================
// = Minical datepicker =
// ======================
$args['exact_date'] = $orig_date;
$factory = $this->_registry->get( 'factory.html' );
$links[] = $factory->create_datepicker_link(
$args,
$args['exact_date'],
$title
);
// ============
// = Next day =
// ============
$local_date->adjust_day( +2 ); // above was (-1), (+2) is to counteract
$args['exact_date'] = $local_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'enabled' => true,
'class' => 'ai1ec-next-day',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-right"></i>',
'href' => $href->generate_href(),
);
return $links;
}
/**
* get_oneday_cell_array function
*
* Return an associative array of weekdays, indexed by the day's date,
* starting the day given by $timestamp, each element an associative array
* containing three elements:
* ['today'] => whether the day is today
* ['allday'] => non-associative ordered array of events that are all-day
* ['notallday'] => non-associative ordered array of non-all-day events to
* display for that day, each element another associative
* array like so:
* ['top'] => how many minutes offset from the start of the day
* ['height'] => how many minutes this event spans
* ['indent'] => how much to indent this event to accommodate multiple
* events occurring at the same time (0, 1, 2, etc., to
* be multiplied by whatever desired px/em amount)
* ['event'] => event data object
*
* @param int $timestamp the UNIX timestamp of the first day of the week
* @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 event instance IDs
*
* @return array array of arrays as per function description
*/
function get_oneday_cell_array(
Ai1ec_Date_Time $start_time,
array $filter = array(),
$legacy = false
) {
$search = $this->_registry->get( 'model.search' );
$loc_start_time = $this->_registry
->get( 'date.time', $start_time, 'sys.default' )
->set_time( 0, 0, 0 );
$loc_end_time = $this->_registry
->get( 'date.time', $start_time, 'sys.default' )
->adjust_day( +1 )
->set_time( 0, 0, 0 );
$day_events = $search->get_events_for_day( $loc_start_time, $filter );
$this->_update_meta( $day_events );
// Split up events on a per-day basis
$all_events = array();
$day_start_ts = $loc_start_time->format();
$day_end_ts = $loc_end_time->format();
$this->_registry->get( 'controller.content-filter' )
->clear_the_content_filters();
foreach ( $day_events as $evt ) {
list( $evt_start, $evt_end ) = $this->
_get_view_specific_timestamps( $evt );
// If event falls on this day, make a copy.
if ( $evt_end > $day_start_ts && $evt_start < $day_end_ts ) {
$_evt = clone $evt;
if ( $evt_start < $day_start_ts ) {
// If event starts before this day, adjust copy's start time
$_evt->set( 'start', $day_start_ts );
$_evt->set( 'start_truncated', true );
}
if ( $evt_end > $day_end_ts ) {
// If event ends after this day, adjust copy's end time
$_evt->set( 'end', $day_end_ts );
$_evt->set( 'end_truncated', true );
}
// Store reference to original, unmodified event, required by view.
$_evt->set( '_orig', $evt );
$this->_add_runtime_properties( $_evt );
// Place copy of event in appropriate category
if ( $_evt->is_allday() ) {
$all_events[$day_start_ts]['allday'][] = $_evt;
} else {
$all_events[$day_start_ts]['notallday'][] = $_evt;
}
}
}
$this->_registry->get( 'controller.content-filter' )
->restore_the_content_filters();
// This will store the returned array
$days = array();
// Initialize empty arrays for this day if no events to minimize warnings
if ( ! isset( $all_events[$day_start_ts]['allday'] ) ) {
$all_events[$day_start_ts]['allday'] = array();
}
if ( ! isset( $all_events[$day_start_ts]['notallday'] ) ) {
$all_events[$day_start_ts]['notallday'] = array();
}
$today_ymd = $this->_registry->get(
'date.time',
$this->_registry->get( 'date.system' )->current_time()
)->format( 'Y-m-d' );
$evt_stack = array( 0 ); // Stack to keep track of indentation
foreach ( $all_events[$day_start_ts] as $event_type => &$events ) {
foreach ( $events as &$evt ) {
$event = array(
'filtered_title' => $evt->get_runtime( 'filtered_title' ),
'post_excerpt' => $evt->get_runtime( 'post_excerpt' ),
'color_style' => $evt->get_runtime( 'color_style' ),
'category_colors' => $evt->get_runtime( 'category_colors' ),
'permalink' => $evt->get_runtime( 'instance_permalink' ),
'ticket_url_label' => $evt->get_runtime( 'ticket_url_label' ),
'edit_post_link' => $evt->get_runtime( 'edit_post_link' ),
'faded_color' => $evt->get_runtime( 'faded_color' ),
'rgba_color' => $evt->get_runtime( 'rgba_color' ),
'short_start_time' => $evt->get_runtime( 'short_start_time' ),
'instance_id' => $evt->get( 'instance_id' ),
'post_id' => $evt->get( 'post_id' ),
'is_multiday' => $evt->get( 'is_multiday' ),
'venue' => $evt->get( 'venue' ),
'ticket_url' => $evt->get( 'ticket_url' ),
'start_truncated' => $evt->get( 'start_truncated' ),
'end_truncated' => $evt->get( 'end_truncated' ),
'popup_timespan' => $this->_registry
->get( 'twig.ai1ec-extension')->timespan( $evt, 'short' ),
'avatar_not_wrapped' => $evt->getavatar( false ),
'avatar' => $this->_registry
->get( 'twig.ai1ec-extension')->avatar(
$evt,
array(
'post_thumbnail',
'content_img',
'location_avatar',
'category_avatar',
),
'',
false ),
);
$meta = $this->_registry->get( 'model.meta-post' );
if ( ! $event['ticket_url'] ) {
$timely_tickets = $meta->get(
$evt->get( 'post_id' ),
'_ai1ec_timely_tickets_url',
null
);
if ( $timely_tickets ) {
$event['ticket_url'] = $timely_tickets;
$evt->set( 'ticket_url', $event['ticket_url'] );
}
}
if (
true === apply_filters(
'ai1ec_buy_button_product',
false
)
) {
$full_details = $meta->get(
$evt->get( 'post_id' ),
'_ai1ec_ep_product_details',
null
);
if (
is_array( $full_details ) &&
isset( $full_details['show_buy_button'] ) &&
true === $full_details['show_buy_button']
&& $event['ticket_url']
) {
// Tickets button is shown by default in this case.
} else {
// Otherwise not.
$event['ticket_url'] = false;
}
$evt->set( 'ticket_url', $event['ticket_url'] );
}
if (
$this->_compatibility->use_backward_compatibility()
) {
$event = $evt;
}
if ( 'notallday' === $event_type) {
// Calculate top and bottom edges of current event
$top = (int)(
$evt->get( 'start' )->diff_sec( $loc_start_time ) / 60
);
$bottom = min(
$top + ( $evt->get_duration() / 60 ),
1440
);
// While there's more than one event in the stack and this event's
// top position is beyond the last event's bottom, pop the stack
while ( count( $evt_stack ) > 1 && $top >= end( $evt_stack ) ) {
array_pop( $evt_stack );
}
// Indentation is number of stacked events minus 1
$indent = count( $evt_stack ) - 1;
// Push this event onto the top of the stack
array_push( $evt_stack, $bottom );
$evt = array(
'top' => $top,
'height' => $bottom - $top,
'indent' => $indent,
'event' => $event,
);
} else {
$evt = $event;
}
}
}
$days[$day_start_ts] = array(
'today' => 0 === strcmp(
$today_ymd,
$start_time->format( 'Y-m-d' )
),
'allday' => $all_events[$day_start_ts]['allday'],
'notallday' => $all_events[$day_start_ts]['notallday'],
'day' => $this->_registry->
get( 'date.time', $day_start_ts )->format_i18n( 'j' ),
'weekday' => $this->_registry->
get( 'date.time', $day_start_ts )->format_i18n( 'D' ),
);
return apply_filters(
'ai1ec_get_oneday_cell_array',
$days,
$start_time->format(),
$filter
);
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::_add_view_specific_runtime_properties()
*/
protected function _add_view_specific_runtime_properties( Ai1ec_Event $event ) {
$event->set_runtime(
'multiday',
$event->get( '_orig' )->is_multiday()
);
}
}

View File

@@ -0,0 +1,513 @@
<?php
/**
* The concrete class for day view.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View
*/
class Ai1ec_Calendar_View_Week extends Ai1ec_Calendar_View_Abstract {
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_name()
*/
public function get_name() {
return 'week';
}
/* (non-PHPdoc)
* @see Ai1ec_Calendar_View_Abstract::get_content()
*/
public function get_content( array $view_args ) {
$date_system = $this->_registry->get( 'date.system' );
$settings = $this->_registry->get( 'model.settings' );
$defaults = array(
'week_offset' => 0,
'cat_ids' => array(),
'tag_ids' => array(),
'auth_ids' => array(),
'post_ids' => array(),
'instance_ids' => array(),
'exact_date' => $date_system->current_time(),
);
$args = wp_parse_args( $view_args, $defaults );
// Localize requested date and get components.
$local_date = $this->_registry
->get( 'date.time', $args['exact_date'], 'sys.default' );
$start_day_offset = $this->get_week_start_day_offset( $local_date->format( 'w' ) );
// get the first day of week
$local_date->adjust_day( 0 + $start_day_offset + ( $args['week_offset'] * 7 ) )
->set_time( 0, 0, 0 );
$cell_array = $this->get_week_cell_array(
$local_date,
apply_filters(
'ai1ec_get_events_relative_to_filter',
array(
'cat_ids' => $args['cat_ids'],
'tag_ids' => $args['tag_ids'],
'post_ids' => $args['post_ids'],
'auth_ids' => $args['auth_ids'],
'instance_ids' => $args['instance_ids'],
),
$view_args,
apply_filters(
'ai1ec_show_unique_events',
false
)
)
);
// Create pagination links. (Translators: '%s' = week's start date.)
$title = sprintf(
__( 'Week of %s', AI1EC_PLUGIN_NAME ),
$local_date->format_i18n( 'F j' )
);
$pagination_links = $this->_get_pagination( $args, $title );
$time_format = $this->_registry->get( 'model.option' )
->get( 'time_format', Ai1ec_I18n::__( 'g a' ) );
// Calculate today marker's position.
$now = $this->_registry->get( 'date.time', 'now', 'sys.default' );
$now_text = $now->format_i18n( 'M j h:i a' );
$now = $now->format( 'G' ) * 60 + $now->format( 'i' );
// Find out if the current week view contains "now" and thus should display
// the "now" marker.
$show_now = false;
foreach ( $cell_array as $day ) {
if ( $day['today'] ) {
$show_now = true;
break;
}
}
$is_ticket_button_enabled = apply_filters( 'ai1ec_week_ticket_button', false );
$show_reveal_button = apply_filters( 'ai1ec_week_reveal_button', false );
$hours = array();
$today = $this->_registry->get( 'date.time', 'now', 'sys.default' );
for ( $hour = 0; $hour < 24; $hour++ ) {
$hours[] = $today
->set_time( $hour, 0, 0 )
->format_i18n( $time_format );
}
$view_args = array(
'title' => $title,
'type' => 'week',
'cell_array' => $cell_array,
'show_location_in_title' => $settings->get( 'show_location_in_title' ),
'now_top' => $now,
'now_text' => $now_text,
'show_now' => $show_now,
'post_ids' => join( ',', $args['post_ids'] ),
'time_format' => $time_format,
'done_allday_label' => false,
'done_grid' => false,
'data_type' => $args['data_type'],
'is_ticket_button_enabled' => $is_ticket_button_enabled,
'show_reveal_button' => $show_reveal_button,
'text_full_day' => __( 'Reveal full day', AI1EC_PLUGIN_NAME ),
'text_all_day' => __( 'All-day', AI1EC_PLUGIN_NAME ),
'text_now_label' => __( 'Now:', AI1EC_PLUGIN_NAME ),
'text_venue_separator' => __( '@ %s', AI1EC_PLUGIN_NAME ),
'hours' => $hours,
'indent_multiplier' => 8,
'indent_offset' => 0,
'pagination_links' => $pagination_links,
);
// Add navigation if requested.
$view_args['navigation'] = $this->_get_navigation(
array(
'no_navigation' => $args['no_navigation'],
'pagination_links' => $pagination_links,
'views_dropdown' => $args['views_dropdown'],
'below_toolbar' => apply_filters(
'ai1ec_below_toolbar',
'',
$this->get_name(),
$args
),
)
);
$view_args = $this->get_extra_template_arguments( $view_args );
return
$this->_registry->get( 'http.request' )->is_json_required(
$args['request_format'], 'week'
)
? $this->_apply_filters_to_args( $view_args )
: $this->_get_view( $view_args );
}
/**
* Returns a non-associative array of two links for the week view of the
* calendar:
* previous week, and next week.
* Each element is an associative array containing the link's enabled status
* ['enabled'], CSS class ['class'], text ['text'] and value to assign to
* link's href ['href'].
*
* @param array $args Current request arguments
* @param string $title Title to display in datepicker button
*
* @return array Array of links
*/
protected function get_week_pagination_links( $args, $title ) {
$links = array();
$orig_date = $args['exact_date'];
$negative_offset = $args['week_offset'] * 7 - 7;
$positive_offset = $args['week_offset'] * 7 + 7;
// =================
// = Previous week =
// =================
$local_date = $this->_registry
->get( 'date.time', $args['exact_date'], 'sys.default' )
->adjust_day( $negative_offset )
->set_time( 0, 0, 0 );
$args['exact_date'] = $local_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'enabled' => true,
'class'=> 'ai1ec-prev-week',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-left"></i>',
'href' => $href->generate_href(),
);
// ======================
// = Minical datepicker =
// ======================
$args['exact_date'] = $orig_date;
$factory = $this->_registry->get( 'factory.html' );
$links[] = $factory->create_datepicker_link(
$args,
$args['exact_date'],
$title
);
// =============
// = Next week =
// =============
$local_date->adjust_day( $positive_offset * 2 ); // above was (-1), (+2) is to counteract
$args['exact_date'] = $local_date->format();
$href = $this->_registry->get( 'html.element.href', $args );
$links[] = array(
'enabled' => true,
'class'=> 'ai1ec-next-week',
'text' => '<i class="ai1ec-fa ai1ec-fa-chevron-right"></i>',
'href' => $href->generate_href(),
);
return $links;
}
/**
* get_week_cell_array function
*
* Return an associative array of weekdays, indexed by the day's date,
* starting the day given by $timestamp, each element an associative array
* containing three elements:
* ['today'] => whether the day is today
* ['allday'] => non-associative ordered array of events that are all-day
* ['notallday'] => non-associative ordered array of non-all-day events to
* display for that day, each element another associative
* array like so:
* ['top'] => how many minutes offset from the start of the day
* ['height'] => how many minutes this event spans
* ['indent'] => how much to indent this event to accommodate multiple
* events occurring at the same time (0, 1, 2, etc., to
* be multiplied by whatever desired px/em amount)
* ['event'] => event data object
*
* @param int $start_of_week the UNIX timestamp of the first day of the week
* @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
*
* @return array array of arrays as per function description
*/
protected function get_week_cell_array( Ai1ec_Date_Time $start_of_week, $filter = array() ) {
$search = $this->_registry->get( 'model.search' );
$settings = $this->_registry->get( 'model.settings' );
$date_system = $this->_registry->get( 'date.system' );
$end_of_week = $this->_registry->get( 'date.time', $start_of_week );
$end_of_week->adjust_day( 7 );
// Do one SQL query to find all events for the week, including spanning
$week_events = $search->get_events_between(
$start_of_week,
$end_of_week,
$filter,
true
);
$this->_update_meta( $week_events );
// Split up events on a per-day basis
$all_events = array();
$this->_days_cache = $this->_registry->get( 'cache.memory' );
$this->_registry->get( 'controller.content-filter' )
->clear_the_content_filters();
foreach ( $week_events as $evt ) {
list( $evt_start, $evt_end ) = $this->
_get_view_specific_timestamps( $evt );
// Iterate through each day of the week and generate new event object
// based on this one for each day that it spans
for (
$day = $start_of_week->format( 'j' ),
$last_week_day_index = $start_of_week->format( 'j' ) + 7;
$day < $last_week_day_index;
$day++
) {
list( $day_start, $day_end ) = $this->
_get_wkday_start_end( $day, $start_of_week );
if ( $evt_end < $day_start ) {
break; // save cycles
}
// If event falls on this day, make a copy.
if ( $evt_end > $day_start && $evt_start < $day_end ) {
$_evt = clone $evt;
if ( $evt_start < $day_start ) {
// If event starts before this day, adjust copy's start time
$_evt->set( 'start', $day_start );
$_evt->set( 'start_truncated', true );
}
if ( $evt_end > $day_end ) {
// If event ends after this day, adjust copy's end time
$_evt->set( 'end', $day_end );
$_evt->set( 'end_truncated', true );
}
// Store reference to original, unmodified event, required by view.
$_evt->set( '_orig', $evt );
$this->_add_runtime_properties( $_evt );
// Place copy of event in appropriate category
if ( $_evt->is_allday() ) {
$all_events[$day_start]['allday'][] = $_evt;
} else {
$all_events[$day_start]['notallday'][] = $_evt;
}
}
}
}
$this->_registry->get( 'controller.content-filter' )
->restore_the_content_filters();
// This will store the returned array
$days = array();
$now = $this->_registry->get(
'date.time',
'now',
$start_of_week->get_timezone()
);
// =========================================
// = Iterate through each date of the week =
// =========================================
for (
$day = $start_of_week->format( 'j' ),
$last_week_day_index = $start_of_week->format( 'j' ) + 7;
$day < $last_week_day_index;
$day++
) {
list( $day_date, , $day_date_ob ) = $this->
_get_wkday_start_end( $day, $start_of_week );
$exact_date = $date_system->format_datetime_for_url(
$day_date_ob,
$settings->get( 'input_date_format' )
);
$href_for_date = $this->_create_link_for_day_view( $exact_date );
// Initialize empty arrays for this day if no events to minimize warnings
if ( ! isset( $all_events[$day_date]['allday'] ) ) {
$all_events[$day_date]['allday'] = array();
}
if ( ! isset( $all_events[$day_date]['notallday'] ) ) {
$all_events[$day_date]['notallday'] = array();
}
$evt_stack = array( 0 ); // Stack to keep track of indentation
foreach ( $all_events[$day_date] as $event_type => &$events ) {
foreach ( $events as &$evt ) {
$event = array(
'filtered_title' => $evt->get_runtime( 'filtered_title' ),
'post_excerpt' => $evt->get_runtime( 'post_excerpt' ),
'color_style' => $evt->get_runtime( 'color_style' ),
'category_colors' => $evt->get_runtime( 'category_colors' ),
'permalink' => $evt->get_runtime( 'instance_permalink' ),
'ticket_url_label' => $evt->get_runtime( 'ticket_url_label' ),
'edit_post_link' => $evt->get_runtime( 'edit_post_link' ),
'faded_color' => $evt->get_runtime( 'faded_color' ),
'rgba_color' => $evt->get_runtime( 'rgba_color' ),
'short_start_time' => $evt->get_runtime( 'short_start_time' ),
'instance_id' => $evt->get( 'instance_id' ),
'post_id' => $evt->get( 'post_id' ),
'is_multiday' => $evt->get( 'is_multiday' ),
'venue' => $evt->get( 'venue' ),
'ticket_url' => $evt->get( 'ticket_url' ),
'start_truncated' => $evt->get( 'start_truncated' ),
'end_truncated' => $evt->get( 'end_truncated' ),
'popup_timespan' => $this->_registry
->get( 'twig.ai1ec-extension')->timespan( $evt, 'short' ),
'avatar_not_wrapped' => $evt->getavatar( false ),
'avatar' => $this->_registry
->get( 'twig.ai1ec-extension')->avatar(
$evt,
array(
'post_thumbnail',
'content_img',
'location_avatar',
'category_avatar',
),
'',
false ),
);
$meta = $this->_registry->get( 'model.meta-post' );
if ( ! $event['ticket_url'] ) {
$timely_tickets = $meta->get(
$evt->get( 'post_id' ),
'_ai1ec_timely_tickets_url',
null
);
if ( $timely_tickets ) {
$event['ticket_url'] = $timely_tickets;
$evt->set( 'ticket_url', $event['ticket_url'] );
}
}
if (
true === apply_filters(
'ai1ec_buy_button_product',
false
)
) {
$full_details = $meta->get(
$evt->get( 'post_id' ),
'_ai1ec_ep_product_details',
null
);
if (
is_array( $full_details ) &&
isset( $full_details['show_buy_button'] ) &&
true === $full_details['show_buy_button']
&& $event['ticket_url']
) {
// Tickets button is shown by default in this case.
} else {
// Otherwise not.
$event['ticket_url'] = false;
}
$evt->set( 'ticket_url', $event['ticket_url'] );
}
if (
$this->_compatibility->use_backward_compatibility()
) {
$event = $evt;
}
if ( 'notallday' === $event_type) {
$start = $evt->get( 'start' );
// Calculate top and bottom edges of current event
$top = $start->format( 'G' ) * 60 + $start->format( 'i' );
$bottom = min( $top + $evt->get_duration() / 60, 1440 );
// While there's more than one event in the stack and this event's top
// position is beyond the last event's bottom, pop the stack
while ( count( $evt_stack ) > 1 && $top >= end( $evt_stack ) ) {
array_pop( $evt_stack );
}
// Indentation is number of stacked events minus 1
$indent = count( $evt_stack ) - 1;
// Push this event onto the top of the stack
array_push( $evt_stack, $bottom );
$evt = array(
'top' => $top,
'height' => $bottom - $top,
'indent' => $indent,
'event' => $event,
);
} else {
$evt = $event;
}
}
}
$days[$day_date] = array(
'today' =>
$day_date_ob->format( 'Y' ) == $now->format( 'Y' ) &&
$day_date_ob->format( 'm' ) == $now->format( 'm' ) &&
$day_date_ob->format( 'j' ) == $now->format( 'j' ),
'allday' => $all_events[$day_date]['allday'],
'notallday' => $all_events[$day_date]['notallday'],
'href' => $href_for_date,
'day' => $this->_registry->
get( 'date.time', $day_date )->format_i18n( 'j' ),
'weekday' => $this->_registry->
get( 'date.time', $day_date )->format_i18n( 'D' ),
);
}
return apply_filters( 'ai1ec_get_week_cell_array', $days, $start_of_week, $filter );
}
/**
* get_week_start_day_offset function
*
* Returns the day offset of the first day of the week given a weekday in
* question.
*
* @param int $wday The weekday to get information about
* @return int A value between -6 and 0 indicating the week start
* day relative to the given weekday.
*/
protected function get_week_start_day_offset( $wday ) {
$settings = $this->_registry->get( 'model.settings' );
return - ( 7 - ( $settings->get( 'week_start_day' ) - $wday ) ) % 7;
}
/**
* Get start/end timestamps for a given weekday and week start identifier.
*
* @param int $day Week day number.
* @param Ai1ec_Date_Time $week_start Date/Time information for week start.
*
* @return array List of start and and timestamps, 0-indexed array.
*/
protected function _get_wkday_start_end(
$day,
Ai1ec_Date_Time $week_start
) {
$entry = null;
$day = (int)$day;
if ( null === ( $entry = $this->_days_cache->get( $day ) ) ) {
$day_start = $this->_registry
->get( 'date.time', $week_start )
->set_date(
$week_start->format( 'Y' ),
$week_start->format( 'm' ),
$day
)
->set_time( 0, 0, 0 );
$day_end = $this->_registry->get( 'date.time', $day_start );
$day_end->adjust_day( 1 );
$entry = array(
$day_start->format(),
$day_end->format(),
$day_start
);
unset( $day_end ); // discard and free memory
$this->_days_cache->set( $day, $entry );
}
return $entry;
}
}