load_dependencies(); $this->set_locale(); $this->init_hooks(); } /** * Load dependencies */ private function load_dependencies() { // Core classes require_once THREECAL_PLUGIN_DIR . 'includes/class-activator.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-deactivator.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-i18n.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-event.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-location.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-category.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-shortcode.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-calendar-renderer.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-email.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-ics.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-icons.php'; require_once THREECAL_PLUGIN_DIR . 'includes/class-themes.php'; // Admin classes if (is_admin()) { require_once THREECAL_PLUGIN_DIR . 'admin/class-admin.php'; require_once THREECAL_PLUGIN_DIR . 'admin/class-events-list.php'; } // Public classes require_once THREECAL_PLUGIN_DIR . 'public/class-public.php'; } /** * Set locale * * Note: WordPress 4.6+ automatically loads translations from the languages directory. * Manual load_plugin_textdomain() is discouraged by WordPress.org Plugin Guidelines. */ private function set_locale() { // Language packs from translate.wordpress.org load automatically. // The bundled German files are only a fallback when no language pack exists. add_action('init', array($this, 'load_bundled_translations'), 0); } /** * Load the bundled German translation when no language pack is installed. */ public function load_bundled_translations() { $locale = determine_locale(); if (0 !== strpos($locale, 'de_')) { return; } $pack = WP_LANG_DIR . '/plugins/3task-calendar-' . $locale . '.mo'; if (file_exists($pack)) { return; } $bundled = THREECAL_PLUGIN_DIR . 'languages/3task-calendar-' . $locale . '.mo'; if (!file_exists($bundled)) { // de_AT, de_CH and others fall back to de_DE (formal variants to de_DE_formal). $fallback = (false !== strpos($locale, '_formal')) ? 'de_DE_formal' : 'de_DE'; $bundled = THREECAL_PLUGIN_DIR . 'languages/3task-calendar-' . $fallback . '.mo'; } if (file_exists($bundled)) { load_textdomain('3task-calendar', $bundled); } } /** * Initialize hooks */ private function init_hooks() { // Admin if (is_admin()) { $this->admin = new ThreeCal_Admin('3task-calendar', THREECAL_VERSION); add_action('admin_enqueue_scripts', array($this->admin, 'enqueue_styles')); add_action('admin_enqueue_scripts', array($this->admin, 'enqueue_scripts')); add_action('admin_menu', array($this->admin, 'add_menu_pages')); add_action('admin_init', array($this->admin, 'register_settings')); add_action('admin_init', array($this, 'maybe_add_capabilities')); } // Database upgrades after plugin updates (activation hook does not run on update). add_action('init', array($this, 'maybe_upgrade_database'), 5); // Public $this->public = new ThreeCal_Public('3task-calendar', THREECAL_VERSION); add_action('wp_enqueue_scripts', array($this->public, 'enqueue_styles')); add_action('wp_enqueue_scripts', array($this->public, 'enqueue_scripts')); // Shortcodes new ThreeCal_Shortcode(); // iCalendar download and subscription feed new ThreeCal_ICS(); // Register block add_action('init', array($this, 'register_block')); add_action('enqueue_block_editor_assets', array($this, 'block_editor_data')); // Pages saved with the old block name (before 1.3.0) render with the new one. add_filter('the_content', array(__CLASS__, 'migrate_block_name'), 8); // REST API add_action('rest_api_init', array($this, 'register_rest_routes')); // Schema.org SEO add_action('wp_head', array($this, 'output_schema')); // AJAX handlers add_action('wp_ajax_threecal_get_events', array($this, 'ajax_get_events')); add_action('wp_ajax_nopriv_threecal_get_events', array($this, 'ajax_get_events')); add_action('wp_ajax_threecal_get_event_details', array($this, 'ajax_get_event_details')); add_action('wp_ajax_nopriv_threecal_get_event_details', array($this, 'ajax_get_event_details')); } /** * Register Gutenberg block */ public function register_block() { if (!function_exists('register_block_type')) { return; } wp_register_script( 'threecal-block-editor', THREECAL_PLUGIN_URL . 'blocks/calendar-block/index.js', array('wp-blocks', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-i18n', 'wp-server-side-render'), threecal_asset_version('blocks/calendar-block/index.js'), true ); if (function_exists('wp_set_script_translations')) { wp_set_script_translations('threecal-block-editor', '3task-calendar', THREECAL_PLUGIN_DIR . 'languages'); } // The calendar stylesheet also styles the live preview in the editor. if (!wp_style_is('threecal-public', 'registered')) { wp_register_style('threecal-public', THREECAL_PLUGIN_URL . 'public/css/threecal.css', array(), threecal_asset_version('public/css/threecal.css')); } wp_register_style( 'threecal-block-editor', THREECAL_PLUGIN_URL . 'blocks/calendar-block/editor.css', array(), threecal_asset_version('blocks/calendar-block/editor.css') ); register_block_type('threecal/calendar', array( 'api_version' => 3, 'editor_script' => 'threecal-block-editor', 'editor_style' => array('threecal-block-editor', 'threecal-public'), 'render_callback' => array($this, 'render_block'), 'attributes' => array( 'view' => array( 'type' => 'string', 'default' => 'month' ), 'category' => array( 'type' => 'number', 'default' => 0 ), 'theme' => array( 'type' => 'string', 'default' => '' ), 'align' => array( 'type' => 'string', 'default' => '' ), 'className' => array( 'type' => 'string', 'default' => '' ) ), 'supports' => array( 'align' => array('wide', 'full'), 'html' => false ) )); } /** * Rename the old block "3task-calendar/calendar" to "threecal/calendar". * * Block names must start with a letter, so WordPress never parsed the old * name and the block rendered nothing. Runs before do_blocks(). * * @param string $content Post content. * @return string */ public static function migrate_block_name($content) { if (!is_string($content) || false === strpos($content, 'wp:3task-calendar/calendar')) { return $content; } return str_replace( array(''), array(''), $content ); } /** * Designs and categories for the block settings (editor only). */ public function block_editor_data() { // Designs and categories for the block settings. $block_themes = array(); foreach (ThreeCal_Themes::all() as $key => $theme) { $block_themes[] = array('value' => $key, 'label' => $theme['label']); } $block_categories = array(); foreach (ThreeCal_Category::get_all() as $cat) { $block_categories[] = array('value' => (int) $cat->id, 'label' => $cat->name); } wp_add_inline_script( 'threecal-block-editor', 'window.threecalBlockData = ' . wp_json_encode(array('themes' => $block_themes, 'categories' => $block_categories)) . ';', 'before' ); } /** * Render block callback */ public function render_block($attributes) { $view = isset($attributes['view']) ? $attributes['view'] : 'month'; $category = isset($attributes['category']) ? absint($attributes['category']) : 0; $theme = isset($attributes['theme']) ? ThreeCal_Themes::normalize($attributes['theme']) : ''; $shortcode = '[threecal view="' . esc_attr($view) . '"'; if ($category > 0) { $shortcode .= ' category="' . $category . '"'; } $shortcode .= $theme ? ' theme="' . esc_attr($theme) . '"]' : ']'; $classes = array('wp-block-threecal-calendar'); if (!empty($attributes['align']) && in_array($attributes['align'], array('wide', 'full'), true)) { $classes[] = 'align' . $attributes['align']; } if (!empty($attributes['className'])) { $classes[] = $attributes['className']; } return '
' . do_shortcode($shortcode) . '
'; } /** * Output Schema.org markup for events * * Runs on singular pages that contain the calendar shortcode or block and * respects the category of the first calendar on the page. */ public function output_schema() { if (!is_singular()) { return; } $settings = get_option('threecal_settings', array()); if (isset($settings['enable_schema']) && !$settings['enable_schema']) { return; } $post = get_post(); if (!$post) { return; } $category = $this->find_calendar_category($post->post_content); if (null === $category) { return; } $events = ThreeCal_Event::get_upcoming(5, $category); if (empty($events)) { return; } $page_url = get_permalink($post); foreach ($events as $event) { $this->output_event_schema($event, $page_url); } } /** * Find the category of the first calendar on a page. * * @param string $content Post content. * @return int|null Category ID (0 = all) or null when the page has no calendar. */ private function find_calendar_category($content) { if (function_exists('has_block') && (has_block('threecal/calendar', $content) || false !== strpos($content, '