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

1227 lines
43 KiB
PHP

<?php
/**
* The front controller of the plugin.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.Controller
*/
class Ai1ec_Front_Controller {
/**
* @var Ai1ec_Registry_Object The Object registry.
*/
protected $_registry;
/**
* @var bool Whether the domain has alredy been loaded or not.
*/
protected $_domain_loaded = false;
/**
* @var string The pagebase used by Ai1ec_Href_Helper.
*/
protected $_pagebase_for_href;
/**
* @var Ai1ec_Request_Parser Instance of the request pa
*/
protected $_request;
/**
* @var array
*/
protected $_default_theme;
/**
* Initializes the default theme property.
*/
public function __construct() {
// Initialize default theme.
$this->_default_theme = array(
'theme_dir' => AI1EC_DEFAULT_THEME_PATH,
'theme_root' => AI1EC_DEFAULT_THEME_ROOT,
'theme_url' => AI1EC_THEMES_URL . '/' . AI1EC_DEFAULT_THEME_NAME,
'stylesheet' => AI1EC_DEFAULT_THEME_NAME,
'legacy' => false,
);
}
/**
* Initialize the controller.
*
* @param Ai1ec_Loader $ai1ec_loader Instance of Ai1EC classes loader
*
* @return void
*/
public function initialize( $ai1ec_loader ) {
ai1ec_start();
$this->_init( $ai1ec_loader );
$this->_initialize_dispatcher();
$lessphp = $this->_registry->get( 'less.lessphp' );
$lessphp->initialize_less_variables_if_not_set();
$this->_registry->get( 'controller.shutdown' )
->register( 'ai1ec_stop' );
add_action( 'plugins_loaded', array( $this, 'register_extensions' ), 1 );
add_action( 'after_setup_theme', array( $this, 'register_themes' ), 1 );
add_action( 'init', array( $lessphp, 'invalidate_css_cache_if_requested' ) );
}
/**
* Let other objects access default theme
*
* @return array
*/
public function get_default_theme() {
return $this->_default_theme;
}
/**
* Remove unwanted menus
*/
public function admin_menu() {
remove_submenu_page(
'edit.php?post_type=ai1ec_event',
'edit-tags.php?taxonomy=events_tags&amp;post_type=ai1ec_event'
);
}
/**
* Notify extensions and pass them instance of objects registry.
*
* @return void
*/
public function register_extensions() {
do_action( 'ai1ec_loaded', $this->_registry );
}
/**
* Notify themes and pass them instance of objects registry.
*
* @return void
*/
public function register_themes() {
do_action( 'ai1ec_after_themes_setup', $this->_registry );
}
/**
* Returns the registry object
*
* @param mixed $discard not used. Always return the registry.
*
* @return Ai1ec_Registry_Object
*/
public function return_registry( $discard ) {
return $this->_registry;
}
/**
* If WIDGET_PARAMETER is set.
*
* @return boolean
*/
protected function is_widget() {
return isset(
$_GET[Ai1ec_Controller_Javascript_Widget::WIDGET_PARAMETER]
);
}
/**
* If Advanced JS cache enabled.
*
* @return boolean
*/
protected function if_js_cache_enabled() {
$settings = $this->_registry->get( 'model.settings' );
return $settings->get( 'cache_dynamic_js' );
}
/**
* If LEGACY_WIDGET_PARAMETER is set.
*
* @return boolean
*/
protected function is_legacy_widget() {
return isset(
$_GET[Ai1ec_Controller_Javascript_Widget::LEGACY_WIDGET_PARAMETER]
);
}
/**
* Execute commands if our plugin must handle the request.
*
* @wp_hook init
*
* @return void
*/
public function route_request() {
$this->_process_request();
// get the resolver
$resolver = $this->_registry->get(
'command.resolver',
$this->_request
);
// get the command
$commands = $resolver->get_commands();
// if we have a command
if ( ! empty( $commands ) ) {
foreach( $commands as $command ) {
$result = $command->execute();
if ( $command->stop_execution() ) {
return $result;
}
}
}
}
/**
* Initializes the URL router used by our plugin.
*
* @wp_hook init
*
* @return void
*/
public function initialize_router() {
/* @var $cal_state Ai1ec_Calendar_State */
$cal_state = $this->_registry->get( 'calendar.state' );
$cal_state->set_routing_initialization( true );
$settings = $this->_registry->get( 'model.settings' );
$cal_page = $settings->get( 'calendar_page_id' );
if (
! $cal_page ||
$cal_page < 1
) { // Routing may not be affected in any way if no calendar page exists.
$cal_state->set_routing_initialization( false );
return null;
}
$router = $this->_registry->get( 'routing.router' );
$localization_helper = $this->_registry->get( 'p28n.wpml' );
$page_base = '';
$clang = '';
if ( $localization_helper->is_wpml_active() ) {
$trans = $localization_helper
->get_wpml_translations_of_page(
$cal_page,
true
);
$clang = $localization_helper->get_language();
if ( isset( $trans[$clang] ) ) {
$cal_page = $trans[$clang];
}
}
$template_link_helper = $this->_registry->get( 'template.link.helper' );
if ( ! get_post( $cal_page ) ) {
$cal_state->set_routing_initialization( false );
return null;
}
$page_base = $template_link_helper->get_page_link(
$cal_page
);
$page_base = Ai1ec_Wp_Uri_Helper::get_pagebase( $page_base );
$page_link = 'index.php?page_id=' .
$cal_page;
$pagebase_for_href = Ai1ec_Wp_Uri_Helper::get_pagebase_for_links(
get_page_link( $cal_page ),
$clang
);
// save the pagebase to set up the factory later
$application = $this->_registry->get( 'bootstrap.registry.application' );
$application->set( 'calendar_base_page', $pagebase_for_href );
$option = $this->_registry->get( 'model.option' );
// If the calendar is set as the front page, disable permalinks.
// They would not be legal under a Windows server. See:
// https://issues.apache.org/bugzilla/show_bug.cgi?id=41441
if (
$option->get( 'permalink_structure' ) &&
( int ) get_option( 'page_on_front' ) !==
( int ) $cal_page
) {
$application->set( 'permalinks_enabled', true );
}
$router->asset_base( $page_base )
->register_rewrite( $page_link );
$cal_state->set_routing_initialization( false );
}
/**
* Initialize the system.
*
* Perform all the inizialization needed for the system.
* Throws some uncatched exception for critical failures.
* Plugin will be disabled by the exception handler on those failures.
*
* @param Ai1ec_Loader $ai1ec_loader Instance of Ai1EC classes loader
*
* @throws Ai1ec_Constants_Not_Set_Exception
* @throws Ai1ec_Database_Update_Exception
* @throws Ai1ec_Database_Schema_Exception
*
* @return void Method does not return
*/
protected function _init( $ai1ec_loader ) {
$exception = null;
// Load the textdomain
add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
try {
// Initialize the registry object
$this->_initialize_registry( $ai1ec_loader );
$this->_registry->get( 'event.dispatcher' )->register_filter(
'ai1ec_perform_scheme_update',
array( 'database.datetime-migration', 'filter_scheme_update' )
);
// Procedures to take when upgrading plugin version
$this->_plugin_upgrade_procedures();
// Load the css if needed
$this->_load_css_if_needed();
// Initialize the crons
$this->_install_crons();
// Register the activation hook
$this->_initialize_schema();
// set the default theme if not set
$this->_add_default_theme_if_not_set();
} catch ( Ai1ec_Constants_Not_Set_Exception $e ) {
// This is blocking, throw it and disable the plugin
$exception = $e;
} catch ( Ai1ec_Database_Update_Exception $e ) {
// Blocking throw it so that the plugin is disabled
$exception = $e;
} catch ( Ai1ec_Database_Schema_Exception $e ) {
// Blocking throw it so that the plugin is disabled
$exception = $e;
} catch ( Ai1ec_Scheduling_Exception $e ) {
// not blocking
}
if ( null !== $exception ) {
throw $exception;
}
}
/**
* Set the default theme if no theme is set, or populate theme info array if
* insufficient information is currently being stored.
*
* @uses apply_filters() Calls 'ai1ec_pre_save_current_theme' hook to allow
* overwriting of theme information before being stored.
*/
protected function _add_default_theme_if_not_set() {
$option = $this->_registry->get( 'model.option' );
$theme = $option->get( 'ai1ec_current_theme', array() );
$update = false;
// Theme setting is undefined; default to Vortex.
if ( empty( $theme ) ) {
$theme = $this->_default_theme;
$update = true;
}
// Legacy settings; in 1.x the active theme was stored as a bare string,
// and they were located in a different folder than they are now.
else if ( is_string( $theme ) ) {
$theme_name = strtolower( $theme );
$core_themes = explode( ',', AI1EC_CORE_THEMES );
$legacy = ! in_array( $theme_name, $core_themes );
if ( $legacy ) {
$root = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . AI1EC_THEME_FOLDER;
$url = WP_CONTENT_URL . '/' . AI1EC_THEME_FOLDER . '/' . $theme_name;
} else {
$root = AI1EC_DEFAULT_THEME_ROOT;
$url = AI1EC_THEMES_URL . '/' . $theme_name;
}
// if it's from 1.x, move folders to avoid confusion
if ( apply_filters( 'ai1ec_move_themes_to_backup', true ) ) {
$this->_registry->get( 'theme.search' )
->move_themes_to_backup( $core_themes );
}
// Ensure existence of theme directory.
if ( ! is_dir( $root . DIRECTORY_SEPARATOR . $theme_name ) ) {
// It's missing; something is wrong with this theme. Reset theme to
// Vortex and warn the user accordingly.
$option->set( 'ai1ec_current_theme', $this->_default_theme );
$notification = $this->_registry->get( 'notification.admin' );
$notification->store(
sprintf(
Ai1ec_I18n::__(
'Your active calendar theme could not be properly initialized. The default theme has been activated instead. Please visit %s and try reactivating your theme manually.'
),
'<a href="' . ai1ec_admin_url( AI1EC_THEME_SELECTION_BASE_URL ) . '">' .
Ai1ec_I18n::__( 'Calendar Themes' ) . '</a>'
),
'error',
1
);
}
$theme = array(
'theme_dir' => $root . DIRECTORY_SEPARATOR . $theme_name,
'theme_root' => $root,
'theme_url' => $url,
'stylesheet' => $theme_name,
'legacy' => $legacy,
);
$update = true;
}
// Ensure 'theme_url' is defined, as this property was added after the first
// public beta release.
else if ( ! isset( $theme['theme_url'] ) ) {
if ( $theme['legacy'] ) {
$theme['theme_url'] = WP_CONTENT_URL . '/' . AI1EC_THEME_FOLDER . '/' .
$theme['stylesheet'];
} else {
$theme['theme_url'] = AI1EC_THEMES_URL . '/' . $theme['stylesheet'];
}
$update = true;
}
if ( $update ) {
$theme = apply_filters( 'ai1ec_pre_save_current_theme', $theme );
$option->set( 'ai1ec_current_theme', $theme );
}
}
/**
* Adds actions handled by the front controller.
*/
protected function _add_front_controller_actions() {
// Initialize router. I use add_action as the dispatcher would just add
// overhead.
add_action(
'init',
array( $this, 'initialize_router' ),
PHP_INT_MAX - 1
);
add_action(
'widgets_init',
array( 'Ai1ec_View_Admin_Widget', 'register_widget' )
);
if (
$this->is_widget() ||
$this->is_legacy_widget()
) {
$this->_registry->get( 'event.dispatcher' )->register_action(
'init',
array( 'controller.javascript-widget', 'render_js_widget' ),
PHP_INT_MAX
);
}
// Route the request.
$action = 'template_redirect';
if ( is_admin() ) {
$action = 'init';
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
}
add_action( $action, array( $this, 'route_request' ) );
add_filter( 'ai1ec_registry', array( $this, 'return_registry' ) );
}
/**
* Initialize the dispatcher.
*
* Complete this when writing the dispatcher.
*
* @return void
*/
protected function _initialize_dispatcher() {
$dispatcher = $this->_registry->get( 'event.dispatcher' );
$dispatcher->register_action(
'init',
array( 'post.custom-type', 'register' )
);
$this->_add_front_controller_actions();
if ( isset( $_GET[Ai1ec_Javascript_Controller::LOAD_JS_PARAMETER] ) ) {
$dispatcher->register_action(
'wp_loaded',
array( 'controller.javascript', 'render_js' )
);
}
$dispatcher->register_action(
'before_delete_post',
array( 'model.event.trashing', 'before_delete_post' ),
0,
3
);
$dispatcher->register_action(
'delete_post',
array( 'model.event.trashing', 'delete' )
);
$dispatcher->register_action(
'wp_trash_post',
array( 'model.event.trashing', 'trash_post' )
);
$dispatcher->register_action(
'trashed_post',
array( 'model.event.trashing', 'trashed_post' )
);
$dispatcher->register_action(
'untrash_post',
array( 'model.event.trashing', 'untrash_post' )
);
$dispatcher->register_action(
'untrashed_post',
array( 'model.event.trashing', 'untrashed_post' )
);
$dispatcher->register_action(
'pre_http_request',
array( 'http.request', 'pre_http_request' ),
10,
3
);
$dispatcher->register_action(
'http_request_args',
array( 'http.request', 'init_certificate' ),
10,
2
);
// add the filter to let the organize page work
$dispatcher->register_action(
'admin_init',
array( 'view.admin.organize', 'add_taxonomy_actions' ),
10000
);
$dispatcher->register_action(
'plugins_loaded',
array( 'theme.loader', 'clean_cache_on_upgrade' ),
PHP_INT_MAX
);
$dispatcher->register_filter(
'get_the_excerpt',
array( 'view.event.content', 'event_excerpt' ),
11
);
remove_filter( 'the_excerpt', 'wpautop', 10 );
$dispatcher->register_filter(
'the_excerpt',
array( 'view.event.content', 'event_excerpt_noautop' ),
11
);
$dispatcher->register_filter(
'robots_txt',
array( 'robots.helper', 'rules' ),
10,
2
);
$dispatcher->register_filter(
'ai1ec_dbi_debug',
array( 'http.request', 'debug_filter' )
);
$dispatcher->register_filter(
'ai1ec_dbi_debug',
array( 'compatibility.cli', 'disable_db_debug' )
);
// editing a child instance
if ( isset( $_SERVER['SCRIPT_NAME'] ) && basename( $_SERVER['SCRIPT_NAME'] ) === 'post.php' ) {
$dispatcher->register_action(
'admin_action_editpost',
array( 'model.event.parent', 'admin_init_post' )
);
$dispatcher->register_filter(
'user_has_cap',
array( 'content.filter', 'display_trash_link' ),
10,
2
);
}
// post row action for parent/child
$dispatcher->register_action(
'post_row_actions',
array( 'model.event.parent', 'post_row_actions' ),
100,
2
);
// Category colors
$dispatcher->register_action(
'events_categories_add_form_fields',
array( 'view.admin.event-category', 'events_categories_add_form_fields' )
);
$dispatcher->register_action(
'events_categories_edit_form_fields',
array( 'view.admin.event-category', 'events_categories_edit_form_fields' )
);
$dispatcher->register_action(
'created_events_categories',
array( 'view.admin.event-category', 'created_events_categories' )
);
$dispatcher->register_action(
'edited_events_categories',
array( 'view.admin.event-category', 'edited_events_categories' )
);
$dispatcher->register_action(
'manage_edit-events_categories_columns',
array( 'view.admin.event-category', 'manage_event_categories_columns' )
);
$dispatcher->register_action(
'manage_events_categories_custom_column',
array( 'view.admin.event-category', 'manage_events_categories_custom_column' ),
10,
3
);
// register ICS cron action
$dispatcher->register_action(
Ai1ecIcsConnectorPlugin::HOOK_NAME,
array( 'calendar-feed.ics', 'cron' )
);
$dispatcher->register_shortcode(
'ai1ec',
array( 'view.calendar.shortcode', 'shortcode' )
);
$dispatcher->register_action(
'updated_option',
array( 'model.settings', 'wp_options_observer' ),
PHP_INT_MAX - 1,
3
);
$dispatcher->register_action(
'ai1ec_settings_updated',
array( 'compatibility.check', 'ai1ec_settings_observer' ),
PHP_INT_MAX - 1,
2
);
if ( $this->if_js_cache_enabled() ) {
$dispatcher->register_action(
'ai1ec_settings_updated',
array( 'controller.javascript', 'revalidate_cache' ),
PHP_INT_MAX - 1
);
$dispatcher->register_action(
'ai1ec_settings_updated',
array( 'controller.javascript-widget', 'revalidate_cache' ),
PHP_INT_MAX - 1
);
}
if ( is_admin() ) {
// Import suggested event
$dispatcher->register_action(
'wp_ajax_ai1ec_import_suggested_event',
array( 'calendar-feed.ics', 'add_discover_events_feed_subscription' )
);
// Remove suggested event
$dispatcher->register_action(
'wp_ajax_ai1ec_remove_suggested_event',
array( 'calendar-feed.ics', 'delete_individual_event_subscription' )
);
// Search for events
$dispatcher->register_action(
'wp_ajax_ai1ec_search_events',
array( 'calendar-feed.suggested', 'search_events' )
);
// get the repeat box
$dispatcher->register_action(
'wp_ajax_ai1ec_get_repeat_box',
array( 'view.admin.get-repeat-box', 'get_repeat_box' )
);
// get the tax options box
$dispatcher->register_action(
'wp_ajax_ai1ec_get_tax_box',
array( 'view.admin.get-tax-box', 'get_tax_box' )
);
// add dismissable notice handler
$dispatcher->register_action(
'wp_ajax_ai1ec_dismiss_notice',
array( 'notification.admin', 'dismiss_notice' )
);
// save rrurle and convert it to text
$dispatcher->register_action(
'wp_ajax_ai1ec_rrule_to_text',
array( 'view.admin.get-repeat-box', 'convert_rrule_to_text' )
);
// display ticketing details in the events list
$dispatcher->register_action(
'wp_ajax_ai1ec_show_ticket_details',
array( 'view.admin.all-events', 'show_ticket_details' )
);
// display attendees list
$dispatcher->register_action(
'wp_ajax_ai1ec_show_attendees',
array( 'view.admin.all-events', 'show_attendees' )
);
// CSS and templates for ticketing options
$dispatcher->register_action(
'restrict_manage_posts',
array( 'view.admin.all-events', 'add_ticketing_styling' )
);
// taxonomy filter
$dispatcher->register_action(
'restrict_manage_posts',
array( 'view.admin.all-events', 'taxonomy_filter_restrict_manage_posts' )
);
$dispatcher->register_action(
'parse_query',
array( 'view.admin.all-events', 'taxonomy_filter_post_type_request' )
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.calendar-feeds', 'add_page' )
);
$dispatcher->register_action(
'current_screen',
array( 'view.admin.calendar-feeds', 'add_meta_box' )
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.add-ons', 'add_page' )
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.theme-switching', 'add_page' )
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.theme-options', 'add_page' )
);
$dispatcher->register_action(
'current_screen',
array( 'view.admin.theme-options', 'add_meta_box' )
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.settings', 'add_page' )
);
$dispatcher->register_action(
'current_screen',
array( 'view.admin.settings', 'add_meta_box' )
);
$dispatcher->register_action(
'init',
array( 'controller.javascript', 'load_admin_js' )
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.samples', 'add_page' ),
100,
1
);
$dispatcher->register_action(
'wp_ajax_ai1ec_add_ics',
array( 'calendar-feed.ics', 'add_ics_feed' )
);
$dispatcher->register_action(
'wp_ajax_ai1ec_delete_ics',
array( 'calendar-feed.ics', 'delete_feeds_and_events' )
);
$dispatcher->register_action(
'wp_ajax_ai1ec_update_ics',
array( 'calendar-feed.ics', 'update_ics_feed' )
);
$dispatcher->register_action(
'wp_ajax_ai1ec_feeds_page_post',
array( 'calendar-feed.ics', 'handle_feeds_page_post' )
);
$dispatcher->register_action(
'wp_ajax_ai1ec_send_feedback_message',
array( 'model.review', 'send_feedback_message' )
);
$dispatcher->register_action(
'wp_ajax_ai1ec_save_feedback_review',
array( 'model.review', 'save_feedback_review' )
);
$dispatcher->register_action(
'network_admin_notices',
array( 'notification.admin', 'send' )
);
$dispatcher->register_action(
'admin_notices',
array( 'notification.admin', 'send' )
);
$dispatcher->register_action(
'admin_footer-edit.php',
array( 'clone.renderer-helper', 'duplicate_custom_bulk_admin_footer' )
);
$dispatcher->register_filter(
'post_row_actions',
array( 'clone.renderer-helper', 'ai1ec_duplicate_post_make_duplicate_link_row' ),
100,
2
);
$dispatcher->register_action(
'add_meta_boxes',
array( 'view.admin.add-new-event', 'event_meta_box_container' )
);
$dispatcher->register_action(
'edit_form_after_title',
array( 'view.admin.add-new-event', 'event_inline_alert' )
);
$dispatcher->register_action(
'save_post',
array( 'model.event.creating', 'save_post' ),
10,
3
);
$dispatcher->register_action(
'pre_post_update',
array( 'model.event.creating', 'pre_post_update' ),
0,
2
);
$dispatcher->register_action(
'wp_insert_post_data',
array( 'model.event.creating', 'wp_insert_post_data' )
);
$dispatcher->register_action(
'manage_ai1ec_event_posts_custom_column',
array( 'view.admin.all-events', 'custom_columns' ),
10,
2
);
$dispatcher->register_filter(
'manage_ai1ec_event_posts_columns',
array( 'view.admin.all-events', 'change_columns' )
);
$dispatcher->register_filter(
'manage_edit-ai1ec_event_sortable_columns',
array( 'view.admin.all-events', 'sortable_columns' )
);
$dispatcher->register_filter(
'posts_orderby',
array( 'view.admin.all-events', 'orderby' ),
10,
2
);
$dispatcher->register_filter(
'ai1ec_count_future_events',
array( 'view.admin.all-events', 'count_future_events' ),
10,
1
);
$dispatcher->register_filter(
'post_updated_messages',
array( 'view.event.post', 'post_updated_messages' )
);
add_action( 'admin_head', array( $this, 'admin_head' ) );
$dispatcher->register_action(
'plugin_action_links_' . AI1EC_PLUGIN_BASENAME,
array( 'view.admin.nav', 'plugin_action_links' )
);
$dispatcher->register_action(
'wp_ajax_ai1ec_rescan_cache',
array( 'twig.cache', 'rescan' )
);
$dispatcher->register_action(
'admin_init',
array( 'environment.check', 'run_checks' )
);
$dispatcher->register_action(
'activated_plugin',
array( 'environment.check', 'check_addons_activation' )
);
$dispatcher->register_filter(
'upgrader_post_install',
array( 'environment.check', 'check_bulk_addons_activation' )
);
if ( $this->if_js_cache_enabled() ) {
$dispatcher->register_action(
'activated_plugin',
array( 'controller.javascript', 'revalidate_cache' )
);
$dispatcher->register_action(
'deactivated_plugin',
array( 'controller.javascript', 'revalidate_cache' )
);
$dispatcher->register_action(
'upgrader_post_install',
array( 'controller.javascript', 'revalidate_cache' )
);
$dispatcher->register_action(
'activated_plugin',
array( 'controller.javascript-widget', 'revalidate_cache' )
);
$dispatcher->register_action(
'deactivated_plugin',
array( 'controller.javascript-widget', 'revalidate_cache' )
);
$dispatcher->register_action(
'upgrader_post_install',
array( 'controller.javascript-widget', 'revalidate_cache' )
);
}
// Widget Creator
$dispatcher->register_action(
'admin_enqueue_scripts',
array( 'css.admin', 'admin_enqueue_scripts' )
);
$dispatcher->register_action(
'current_screen',
array( 'view.admin.widget-creator', 'add_meta_box' )
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.widget-creator', 'add_page' )
);
$dispatcher->register_filter(
'pre_set_site_transient_update_plugins',
array( 'calendar.updates', 'check_updates' )
);
$dispatcher->register_filter(
'plugins_api',
array( 'calendar.updates', 'plugins_api_filter' ),
10,
3
);
$dispatcher->register_action(
'admin_menu',
array( 'view.admin.tickets', 'add_page' )
);
} else { // ! is_admin()
$dispatcher->register_action(
'after_setup_theme',
array( 'theme.loader', 'execute_theme_functions' )
);
$dispatcher->register_action(
'the_post',
array( 'post.content', 'check_content' ),
PHP_INT_MAX
);
$dispatcher->register_action(
'send_headers',
array( 'request.redirect', 'handle_categories_and_tags' )
);
$dispatcher->register_action(
'wp_head',
array( 'view.event.single', 'add_meta_tags' )
);
}
}
/**
* Outputs menu icon between head tags
*/
public function admin_head() {
global $wp_version;
$argv = array(
'before_font_icons' => version_compare( $wp_version, '3.8', '<' ),
'admin_theme_img_url' => AI1EC_ADMIN_THEME_IMG_URL,
'admin_theme_font_url' => AI1EC_ADMIN_THEME_FONT_URL,
);
$this->_registry->get( 'theme.loader' )
->get_file( 'timely-menu-icon.twig', $argv, true )
->render();
}
/**
* _add_defaults method
*
* Add (merge) default options to given query variable.
*
* @param string settingsquery variable to ammend
*
* @return string|NULL Modified variable values or NULL on failure
*
* @global Ai1ec_Settings $ai1ec_settings Instance of settings object
* to pull data from
* @staticvar array $mapper Mapping of query names to
* default in settings
*/
protected function _add_defaults( $name ) {
$settings = $this->_registry->get( 'model.settings' );
static $mapper = array(
'cat' => 'categories',
'tag' => 'tags',
);
$rq_name = 'ai1ec_' . $name . '_ids';
if (
! isset( $mapper[$name] ) ||
! array_key_exists( $rq_name, $this->_request )
) {
return NULL;
}
$options = explode( ',', $this->_request[$rq_name] );
$property = 'default_' . $mapper[$name];
$options = array_merge(
$options,
$settings->get( $property )
);
$filtered = array();
foreach ( $options as $item ) { // avoid array_filter + is_numeric
$item = (int)$item;
if ( $item > 0 ) {
$filtered[] = $item;
}
}
unset( $options );
if ( empty( $filtered ) ) {
return NULL;
}
return implode( ',', $filtered );
}
/**
* Process_request function.
*
* Initialize/validate custom request array, based on contents of $_REQUEST,
* to keep track of this component's request variables.
*
* @return void
**/
protected function _process_request() {
$settings = $this->_registry->get( 'model.settings' );
$this->_request = $this->_registry->get( 'http.request.parser' );
$aco = $this->_registry->get( 'acl.aco' );
$page_id = $settings->get( 'calendar_page_id' );
if (
! $aco->is_admin() &&
$page_id &&
is_page( $page_id )
) {
foreach ( array( 'cat', 'tag' ) as $name ) {
$implosion = $this->_add_defaults( $name );
if ( $implosion ) {
$this->request['ai1ec_' . $name . '_ids'] = $implosion;
$_REQUEST['ai1ec_' . $name . '_ids'] = $implosion;
}
}
}
}
/**
* Initialize cron functions.
*
* @throws Ai1ec_Scheduling_Exception
*
* @return void
*/
protected function _install_crons() {
$scheduling = $this->_registry->get( 'scheduling.utility' );
$hook_name = 'ai1ec_n_cron';
$scheduling->delete( $hook_name );
}
/**
* Initialize the registry object.
*
* @param Ai1ec_Loader $ai1ec_loader Instance of Ai1EC classes loader
*
* @return void Method does not return
*/
protected function _initialize_registry( $ai1ec_loader ) {
global $ai1ec_registry;
$this->_registry = new Ai1ec_Registry_Object( $ai1ec_loader );
Ai1ec_Time_Utility::set_registry( $this->_registry );
$ai1ec_registry = $this->_registry;
}
/**
* Loads the CSS for the plugin
*
*/
protected function _load_css_if_needed() {
// ==================================
// = Add the hook to render the css =
// ==================================
if ( isset( $_GET[Ai1ec_Css_Frontend::QUERY_STRING_PARAM] ) ) {
// we need to wait for the extension to be registered if the css
// needs to be compiled. Will find a better way when compiling css.
$css_controller = $this->_registry->get( 'css.frontend' );
add_action( 'plugins_loaded', array( $css_controller, 'render_css' ), 2 );
}
}
/**
* Load the texdomain for the plugin.
*
* @wp_hook plugins_loaded
*
* @return void
*/
public function load_textdomain() {
if ( false === $this->_domain_loaded ) {
load_plugin_textdomain(
AI1EC_PLUGIN_NAME, false, AI1EC_LANGUAGE_PATH
);
$this->_domain_loaded = true;
}
}
/**
* Check if the schema is up to date.
*
* @throws Ai1ec_Database_Schema_Exception
* @throws Ai1ec_Database_Update_Exception
*
* @return void
*/
protected function _initialize_schema() {
$option = $this->_registry->get( 'model.option' );
$schema_sql = $this->get_current_db_schema();
$version = sha1( $schema_sql );
// If existing DB version is not consistent with current plugin's version,
// or does not exist, then create/update table structure using dbDelta().
if ( $option->get( 'ai1ec_db_version' ) != $version ) {
$errors = $this->_registry->get( 'database.applicator' )
->check_db_consistency_for_date_migration() ;
if ( ! empty( $errors ) ) {
$message = Ai1ec_I18n::__(
'Your database is found to be corrupt. Likely previous update has failed. Please restore All-in-One Event Calendar tables from a backup and retry.<br>Following errors were found:<br>%s'
);
$message = sprintf( $message, implode( $errors, '<br>' ) );
throw new Ai1ec_Database_Update_Exception( $message );
}
$this->_registry->get( 'database.applicator' )
->remove_instance_duplicates();
if (
apply_filters( 'ai1ec_perform_scheme_update', true ) &&
$this->_registry->get( 'database.helper' )->apply_delta(
$schema_sql
)
) {
$option->set( 'ai1ec_db_version', $version );
} else {
throw new Ai1ec_Database_Update_Exception();
}
// If the schema structure upgrade is complete move contents
$categories_key = 'ai1ec_category_meta_ported';
if ( ! $option->get( $categories_key ) ) {
$this->_migrate_categories_meta();
$option->set( $categories_key, true );
}
}
}
/**
* Transform categories meta information.
*
* Use new `meta` table instead of legacy `colors` table.
*
* @return void Method does not return.
*/
protected function _migrate_categories_meta() {
$db = $this->_registry->get( 'dbi.dbi' );
$table_name = $db->get_table_name( 'ai1ec_event_category_colors' );
$db_h = $this->_registry->get( 'database.helper' );
if ( $db_h->table_exists( $table_name ) ) { // if old table exists otherwise ignore it
// Migrate color information
$dest_table = $db->get_table_name( 'ai1ec_event_category_meta' );
$colors = $db->select(
$table_name,
array( 'term_id', 'term_color'),
ARRAY_A
);
if ( ! empty( $colors ) ) {
foreach ( $colors as $color ) {
$db->insert( $dest_table, $color );
}
}
// Drop the old table
$db->query( 'DROP TABLE IF EXISTS ' . $table_name );
}
}
/**
* Procedures to take when upgrading plugin version
*
* @return void
*/
protected function _plugin_upgrade_procedures() {
$option = $this->_registry->get( 'model.option' );
$version = AI1EC_VERSION;
if ( $option->get( 'ai1ec_version' ) != $version ) {
try {
// Force regeneration of JS cache
$this->_registry->get( 'controller.javascript' )->revalidate_cache();
$this->_registry->get( 'controller.javascript-widget' )->revalidate_cache();
// Run upgrade commands
$settings = $this->_registry->get( 'model.settings' );
$settings->perform_upgrade_actions();
} catch ( Exception $e ) {
}
// Update plugin version
$option->set( 'ai1ec_version', $version );
}
}
/**
* Get current database schema as a multi SQL statement.
*
* @return string Multiline SQL statement.
*/
public function get_current_db_schema() {
$dbi = $this->_registry->get( 'dbi.dbi' );
// =======================
// = Create table events =
// =======================
$table_name = $dbi->get_table_name( 'ai1ec_events' );
$sql = "CREATE TABLE $table_name (
post_id bigint(20) NOT NULL,
start int(10) UNSIGNED NOT NULL,
end int(10) UNSIGNED,
timezone_name varchar(50),
allday tinyint(1) NOT NULL,
instant_event tinyint(1) NOT NULL DEFAULT 0,
recurrence_rules longtext,
exception_rules longtext,
recurrence_dates longtext,
exception_dates longtext,
venue varchar(255),
country varchar(255),
address varchar(255),
city varchar(255),
province varchar(255),
postal_code varchar(32),
show_map tinyint(1),
contact_name varchar(255),
contact_phone varchar(32),
contact_email varchar(128),
contact_url varchar(255),
cost varchar(255),
ticket_url varchar(255),
ical_feed_url varchar(255),
ical_source_url varchar(255),
ical_organizer varchar(255),
ical_contact varchar(255),
ical_uid varchar(255),
show_coordinates tinyint(1),
latitude decimal(20,15),
longitude decimal(20,15),
force_regenerate tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (post_id),
KEY feed_source (ical_feed_url)
) CHARACTER SET utf8;";
// ==========================
// = Create table instances =
// ==========================
$table_name = $dbi->get_table_name( 'ai1ec_event_instances' );
$sql .= "CREATE TABLE $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
post_id bigint(20) NOT NULL,
start int(10) UNSIGNED NOT NULL,
end int(10) UNSIGNED NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY evt_instance (post_id,start)
) CHARACTER SET utf8;";
// ================================
// = Create table category colors =
// ================================
$table_name = $dbi->get_table_name( 'ai1ec_event_category_meta' );
$sql .= "CREATE TABLE $table_name (
term_id bigint(20) NOT NULL,
term_color varchar(255) NOT NULL,
term_image varchar(254) NULL DEFAULT NULL,
PRIMARY KEY (term_id)
) CHARACTER SET utf8;";
return $sql;
}
}