all-in-one-event-calendar/app/view/calendar/page.php
2017-11-09 17:36:04 +01:00

522 lines
20 KiB
PHP

<?php
/**
* The concrete class for the calendar page.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View
*/
class Ai1ec_Calendar_Page extends Ai1ec_Base {
/**
* @var Ai1ec_Memory_Utility Instance of memory to hold exact dates
*/
protected $_exact_dates = NULL;
/**
* Public constructor
*
* @param Ai1ec_Registry_Object $registry The registry object
*/
public function __construct( Ai1ec_Registry_Object $registry ) {
parent::__construct( $registry );
$this->_exact_dates = $registry->get( 'cache.memory' );
}
/**
* Get the content if the calendar page
*
* @param Ai1ec_Request_Parser $request Request object.
* @param string $caller Method caller, expected one of
* ['shortcode', 'render-command']
* Defaults to 'render-command'.
*
* @return string Content.
*/
public function get_content(
Ai1ec_Request_Parser $request,
$caller = 'render-command'
) {
// Get args for the current view; required to generate HTML for views
// dropdown list, categories, tags, subscribe buttons, and of course the
// view itself.
$view_args = $this->get_view_args_for_view( $request );
try {
$action = $this->_registry->get( 'model.settings-view' )
->get_configured( $view_args['action'] );
} catch ( Ai1ec_Settings_Exception $exception ) {
// short-circuit and return error message
return '<div id="ai1ec-container"><div class="timely"><p>' .
Ai1ec_I18n::__(
'There was an error loading calendar. Please contact site administrator and inform him to configure calendar views.'
) .
'</p></div></div>';
}
$type = $request->get( 'request_type' );
$is_json = $this->_registry->get( 'http.request' )->is_json_required(
$view_args['request_format'], $action
);
// Add view-specific args to the current view args.
$exact_date = $this->get_exact_date( $request );
try {
$view_obj = $this->_registry->get(
'view.calendar.view.' . $action,
$request
);
} catch ( Ai1ec_Bootstrap_Exception $exc ) {
$this->_registry->get( 'notification.admin' )->store(
sprintf(
Ai1ec_I18n::__( 'Calendar was unable to initialize %s view and has reverted to Agenda view. Please check if you have installed the latest versions of calendar add-ons.' ),
ucfirst( $action )
),
'error',
0,
array( Ai1ec_Notification_Admin::RCPT_ADMIN ),
true
);
// don't disable calendar - just switch to agenda which should
// always exists
$action = 'agenda';
$view_obj = $this->_registry->get(
'view.calendar.view.' . $action,
$request
);
}
$view_args = $view_obj->get_extra_arguments( $view_args, $exact_date );
// Get HTML for views dropdown list.
$dropdown_args = $view_args;
if (
isset( $dropdown_args['time_limit'] ) &&
false !== $exact_date
) {
$dropdown_args['exact_date'] = $exact_date;
}
$views_dropdown =
$this->get_html_for_views_dropdown( $dropdown_args, $view_obj );
// Add views dropdown markup to view args.
$view_args['views_dropdown'] = $views_dropdown;
$settings = $this->_registry->get( 'model.settings' );
if ( $settings->get( 'ai1ec_use_frontend_rendering' ) ) {
$view_args['request_format'] = 'json';
}
// Get HTML for subscribe buttons.
$subscribe_buttons = $this->get_html_for_subscribe_buttons( $view_args );
// Get HTML for view itself.
$view = $view_obj->get_content( $view_args );
$router = $this->_registry->get( 'routing.router' );
$are_filters_set = $router->is_at_least_one_filter_set_in_request(
$view_args
);
if (
$is_json &&
( $view_args['no_navigation'] || $type !== 'html' )
) {
// send data both for json and jsonp as shortcodes are jsonp
return array(
'html' => $view,
'views_dropdown' => $views_dropdown,
'subscribe_buttons' => $subscribe_buttons,
'are_filters_set' => $are_filters_set,
'is_json' => $is_json,
);
} else {
$loader = $this->_registry->get( 'theme.loader' );
$empty = $loader->get_file( 'empty.twig', array(), false );
// Get HTML for categories and for tags
$taxonomy = $this->_registry->get( 'view.calendar.taxonomy' );
$categories = $taxonomy->get_html_for_categories(
$view_args
);
$tags = $taxonomy->get_html_for_tags(
$view_args,
true
);
// option to show filters in the super widget
// Define new arguments for overall calendar view
$filter_args = array(
'views_dropdown' => $views_dropdown,
'categories' => $categories,
'tags' => $tags,
'contribution_buttons' => apply_filters(
'ai1ec_contribution_buttons',
'',
$type,
$caller
),
'additional_buttons' => apply_filters(
'ai1ec_additional_buttons',
'',
$view_args
),
'show_dropdowns' => apply_filters(
'ai1ec_show_dropdowns',
true
),
'show_select2' => apply_filters(
'ai1ec_show_select2',
false
),
'span_for_select2' => apply_filters(
'ai1ec_span_for_select2',
''
),
'authors' => apply_filters(
'ai1ec_authors',
''
),
'save_view_btngroup' => apply_filters(
'ai1ec_save_view_btngroup',
$empty
),
'view_args' => $view_args,
'request' => $request,
);
$filter_menu = $loader->get_file(
'filter-menu.twig',
$filter_args,
false
)->get_content();
// hide filters in the SW
if ( 'true' !== $request->get( 'display_filters' ) && 'jsonp' === $type ) {
$filter_menu = '';
}
$calendar_args = array(
'version' => AI1EC_VERSION,
'filter_menu' => $filter_menu,
'view' => $view,
'subscribe_buttons' => $subscribe_buttons,
'disable_standard_filter_menu' => apply_filters(
'ai1ec_disable_standard_filter_menu',
false
),
'inline_js_calendar' => apply_filters(
'ai1ec_inline_js_calendar',
''
),
'after_view' => apply_filters(
'ai1ec_after_view',
''
),
'ai1ec_above_calendar' => apply_filters(
'ai1ec_above_calendar',
''
),
);
if ( is_array( $calendar_args['view'] ) ) {
$view_args['request_format'] = 'html';
$calendar_args['view'] = $view_obj->get_content( $view_args );
}
$calendar = $loader->get_file( 'calendar.twig', $calendar_args, false );
// if it's just html, only the calendar html must be returned.
if ( 'html' === $type ) {
return $calendar->get_content();
}
// send data both for json and jsonp as shortcodes are jsonp
return array(
'html' => $calendar->get_content(),
'views_dropdown' => $views_dropdown,
'subscribe_buttons' => $subscribe_buttons,
'are_filters_set' => $are_filters_set,
'is_json' => $is_json
);
}
}
/**
* Render the HTML for the `subscribe' buttons.
*
* @param array $view_args Args to pass.
*
* @return string Rendered HTML to include in output.
*/
public function get_html_for_subscribe_buttons( array $view_args ) {
$settings = $this->_registry->get( 'model.settings' );
$turn_off_subscribe = $settings->get( 'turn_off_subscription_buttons' );
if ( $turn_off_subscribe ) {
return '';
}
$args = array(
'url_args' => '',
'is_filtered' => false,
'export_url' => AI1EC_EXPORT_URL,
'export_url_no_html' => AI1EC_EXPORT_URL . '&no_html=true',
'text_filtered' => Ai1ec_I18n::__( 'Subscribe to filtered calendar' ),
'text_subscribe' => Ai1ec_I18n::__( 'Subscribe' ),
'text_get_calendar' => Ai1ec_I18n::__( 'Get a Timely Calendar' ),
'show_get_calendar' => ! $settings->get( 'disable_get_calendar_button' ),
'text' => $this->_registry
->get( 'view.calendar.subscribe-button' )
->get_labels(),
'placement' => 'up',
);
if ( ! empty( $view_args['cat_ids'] ) ) {
$args['url_args'] .= '&ai1ec_cat_ids=' .
implode( ',', $view_args['cat_ids'] );
$args['is_filtered'] = true;
}
if ( ! empty( $view_args['tag_ids'] ) ) {
$args['url_args'] .= '&ai1ec_tag_ids=' .
implode( ',', $view_args['tag_ids'] );
$args['is_filtered'] = true;
}
if ( ! empty( $view_args['post_ids'] ) ) {
$args['url_args'] .= '&ai1ec_post_ids=' .
implode( ',', $view_args['post_ids'] );
$args['is_filtered'] = true;
}
$args = apply_filters(
'ai1ec_subscribe_buttons_arguments',
$args,
$view_args
);
$localization = $this->_registry->get( 'p28n.wpml' );
if (
NULL !== ( $use_lang = $localization->get_language() )
) {
$args['url_args'] .= '&lang=' . $use_lang;
}
$subscribe = $this->_registry->get( 'theme.loader' )
->get_file( 'subscribe-buttons.twig', $args, false );
return $subscribe->get_content();
}
/**
* This function generates the html for the view dropdowns.
*
* @param array $view_args Args passed to view
* @param Ai1ec_Calendar_View_Abstract $view View object
*/
protected function get_html_for_views_dropdown(
array $view_args,
Ai1ec_Calendar_View_Abstract $view
) {
$settings = $this->_registry->get( 'model.settings' );
$available_views = array();
$enabled_views = (array)$settings->get( 'enabled_views', array() );
$view_names = array();
$mode = wp_is_mobile() ? '_mobile' : '';
foreach ( $enabled_views as $key => $val ) {
$view_names[$key] = translate_nooped_plural(
$val['longname'],
1
);
// Find out if view is enabled in requested mode (mobile or desktop). If
// no mode-specific setting is available, fall back to desktop setting.
$view_enabled = isset( $enabled_views[$key]['enabled' . $mode] ) ?
$enabled_views[$key]['enabled' . $mode] :
$enabled_views[$key]['enabled'];
$values = array();
$options = $view_args;
if ( $view_enabled ) {
if ( $view instanceof Ai1ec_Calendar_View_Agenda ) {
if (
isset( $options['exact_date'] ) &&
! isset( $options['time_limit'] )
) {
$options['time_limit'] = $options['exact_date'];
}
unset( $options['exact_date'] );
} else {
unset( $options['time_limit'] );
}
unset( $options['month_offset'] );
unset( $options['week_offset'] );
unset( $options['oneday_offset'] );
$options['action'] = $key;
$values['desc'] = translate_nooped_plural(
$val['longname'],
1
);
if ( $settings->get( 'ai1ec_use_frontend_rendering' ) ) {
$options['request_format'] = 'json';
}
$href = $this->_registry->get( 'html.element.href', $options );
$values['href'] = $href->generate_href();
$available_views[$key] = $values;
}
};
$args = array(
'view_names' => $view_names,
'available_views' => $available_views,
'current_view' => $view_args['action'],
'data_type' => $view_args['data_type'],
);
$views_dropdown = $this->_registry->get( 'theme.loader' )
->get_file( 'views_dropdown.twig', $args, false );
return $views_dropdown->get_content();
}
/**
* Get the exact date from request if available, or else from settings.
*
* @param Ai1ec_Abstract_Query settings
*
* @return boolean|int
*/
private function get_exact_date( Ai1ec_Abstract_Query $request ) {
$settings = $this->_registry->get( 'model.settings' );
// Preprocess exact_date.
// Check to see if a date has been specified.
$exact_date = $request->get( 'exact_date' );
$use_key = $exact_date;
if ( null === ( $exact_date = $this->_exact_dates->get( $use_key ) ) ) {
$exact_date = $use_key;
// Let's check if we have a date
if ( false !== $exact_date ) {
// If it's not a timestamp
if ( ! Ai1ec_Validation_Utility::is_valid_time_stamp( $exact_date ) ) {
// Try to parse it
$exact_date = $this->return_gmtime_from_exact_date( $exact_date );
if ( false === $exact_date ) {
return null;
}
}
}
// Last try, let's see if an exact date is set in settings.
if ( false === $exact_date && $settings->get( 'exact_date' ) !== '' ) {
$exact_date = $this->return_gmtime_from_exact_date(
$settings->get( 'exact_date' )
);
}
$this->_exact_dates->set( $use_key, $exact_date );
}
return $exact_date;
}
/**
* Decomposes an 'exact_date' parameter into month, day, year components based
* on date pattern defined in settings (assumed to be in local time zone),
* then returns a timestamp in GMT.
*
* @param string $exact_date 'exact_date' parameter passed to a view
* @return bool|int false if argument not provided or invalid,
* else UNIX timestamp in GMT
*/
private function return_gmtime_from_exact_date( $exact_date ) {
$input_format = $this->_registry->get( 'model.settings' )
->get( 'input_date_format' );
$date = Ai1ec_Validation_Utility::format_as_iso(
$exact_date,
$input_format
);
if ( false === $date ) {
$exact_date = false;
} else {
$exact_date = $this->_registry->get(
'date.time',
$date,
'sys.default'
)->format_to_gmt();
if ( $exact_date < 0 ) {
return false;
}
}
return $exact_date;
}
/**
* Returns the correct data attribute to use in views
*
* @param string $type
*/
private function return_data_type_for_request_type( $type ) {
$data_type = 'data-type="json"';
if ( $type === 'jsonp' ) {
$data_type = 'data-type="jsonp"';
}
return $data_type;
}
/**
* Get the parameters for the view from the request object
*
* @param Ai1ec_Abstract_Query $request
*
* @return array
*/
protected function get_view_args_for_view( Ai1ec_Abstract_Query $request ) {
$settings = $this->_registry->get( 'model.settings' );
// Define arguments for specific calendar sub-view (month, agenda, etc.)
// Preprocess action.
// Allow action w/ or w/o ai1ec_ prefix. Remove ai1ec_ if provided.
$action = $request->get( 'action' );
if ( 0 === strncmp( $action, 'ai1ec_', 6 ) ) {
$action = substr( $action, 6 );
}
$view_args = $request->get_dict(
apply_filters(
'ai1ec_view_args_for_view',
array(
'post_ids',
'auth_ids',
'cat_ids',
'tag_ids',
'events_limit',
'instance_ids',
)
)
);
$type = $request->get( 'request_type' );
if ( 'html' === $type ) {
$add_defaults = array(
'cat_ids' => 'categories',
'tag_ids' => 'tags',
);
foreach ( $add_defaults as $query => $default ) {
if ( empty( $view_args[$query] ) ) {
$setting = $settings->get( 'default_tags_categories' );
if ( isset( $setting[$default] ) ) {
$view_args[$query] = $setting[$default];
}
}
}
}
$view_args['data_type'] = $this->return_data_type_for_request_type(
$type
);
$view_args['request_format'] = $request->get( 'request_format' );
$exact_date = $this->get_exact_date( $request );
$view_args['no_navigation'] = $request->get( 'no_navigation' ) == true;
// Find out which view of the calendar page was requested, and render it
// accordingly.
$view_args['action'] = $action;
$view_args['request'] = $request;
$view_args = apply_filters(
'ai1ec_view_args_array',
$view_args
);
if ( null === $exact_date ) {
$href = $this->_registry->get( 'html.element.href', $view_args )
->generate_href();
return Ai1ec_Http_Response_Helper::redirect( $href, 307 );
}
return $view_args;
}
}