load_dependencies(); $this->init_hooks(); } /** * Load plugin dependencies */ private function load_dependencies(): void { require_once ACSAGMA_AGENDA_PLUGIN_DIR . 'class/class-acs-database.php'; require_once ACSAGMA_AGENDA_PLUGIN_DIR . 'class/class-acs-event.php'; require_once ACSAGMA_AGENDA_PLUGIN_DIR . 'class/class-acs-template.php'; require_once ACSAGMA_AGENDA_PLUGIN_DIR . 'class/class-acs-admin.php'; require_once ACSAGMA_AGENDA_PLUGIN_DIR . 'class/class-acs-options.php'; require_once ACSAGMA_AGENDA_PLUGIN_DIR . 'class/class-acs-help.php'; // Initialize admin pages if (is_admin()) { ACSAGMA_Admin::get_instance(); ACSAGMA_Options::get_instance(); ACSAGMA_Help::get_instance(); } } /** * Initialize WordPress hooks */ private function init_hooks(): void { add_action('wp_enqueue_scripts', [$this, 'enqueue_frontend_assets']); add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']); add_action('plugins_loaded', [$this, 'check_version']); // AJAX handlers add_action('wp_ajax_acsagma_read_more', [$this, 'ajax_read_more']); add_action('wp_ajax_nopriv_acsagma_read_more', [$this, 'ajax_read_more']); // Shortcode add_shortcode('acsagma_agenda', [$this, 'render_agenda_shortcode']); } /** * Enqueue frontend assets */ public function enqueue_frontend_assets(bool $force = false): void { if (!$force && !$this->should_load_frontend_assets()) { return; } // Common styles (variables, buttons, spinner) wp_enqueue_style( 'acs-agenda-common', ACSAGMA_AGENDA_PLUGIN_URL . 'css/acs-common.css', [], ACSAGMA_AGENDA_VERSION ); // Frontend styles wp_enqueue_style( 'acs-agenda-style', ACSAGMA_AGENDA_PLUGIN_URL . 'css/acs.css', ['acs-agenda-common'], ACSAGMA_AGENDA_VERSION ); wp_enqueue_style('wp-jquery-ui-dialog'); wp_enqueue_script('jquery'); wp_enqueue_script('jquery-ui-dialog'); wp_enqueue_script('jquery-ui-datepicker'); wp_enqueue_script( 'acs-multidatespicker', ACSAGMA_AGENDA_PLUGIN_URL . 'js/jquery-ui-multidatespicker.min.js', ['jquery-ui-datepicker'], '1.6.6', true ); wp_enqueue_script( 'acs-agenda-frontend', ACSAGMA_AGENDA_PLUGIN_URL . 'js/acs-frontend.js', ['jquery', 'jquery-ui-dialog'], ACSAGMA_AGENDA_VERSION, [ 'in_footer' => true, 'strategy' => 'defer', ] ); wp_localize_script('acs-agenda-frontend', 'acsagmaAgenda', [ 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('acsagma_agenda_nonce'), 'fallbackImage' => ACSAGMA_AGENDA_PLUGIN_URL . 'css/images/Accept-icon.png', 'i18n' => [ 'readMoreError' => __('Unable to load details. Please try again.', 'acs-agenda-manager'), 'noResults' => __('No events match your filters.', 'acs-agenda-manager'), 'resultsLabel' => __('Showing %1$d-%2$d of %3$d events', 'acs-agenda-manager'), 'compactOn' => __('Compact mode on', 'acs-agenda-manager'), 'compactOff' => __('Compact mode off', 'acs-agenda-manager'), 'prev' => __('Previous', 'acs-agenda-manager'), 'next' => __('Next', 'acs-agenda-manager'), ], ]); } /** * Enqueue admin assets */ public function enqueue_admin_assets(string $hook): void { if (strpos($hook, 'agenda') === false) { return; } // Common styles (variables, buttons, spinner) wp_enqueue_style( 'acs-agenda-common', ACSAGMA_AGENDA_PLUGIN_URL . 'css/acs-common.css', [], ACSAGMA_AGENDA_VERSION ); // Admin-specific styles wp_enqueue_style( 'acs-agenda-admin-style', ACSAGMA_AGENDA_PLUGIN_URL . 'css/acs-admin.css', ['acs-agenda-common', 'wp-jquery-ui-dialog'], ACSAGMA_AGENDA_VERSION ); wp_enqueue_style('wp-jquery-ui-dialog'); wp_enqueue_script('jquery'); wp_enqueue_script('jquery-ui-dialog'); wp_enqueue_script('jquery-ui-datepicker'); // Multi-date picker wp_enqueue_script( 'acs-multidatespicker', ACSAGMA_AGENDA_PLUGIN_URL . 'js/jquery-ui-multidatespicker.min.js', ['jquery-ui-datepicker'], '1.6.6', true ); wp_enqueue_media(); wp_enqueue_style('thickbox'); wp_enqueue_script('thickbox'); // Load Google Maps API if key is configured (only if supplied via option) $google_maps_api_key = get_option('acsagma_google_maps_api_key', ''); if (!empty($google_maps_api_key)) { $maps_url = add_query_arg( [ 'key' => rawurlencode($google_maps_api_key), 'libraries' => 'places', 'loading' => 'async', ], 'https://maps.googleapis.com/maps/api/js' ); wp_enqueue_script( 'google-maps-places', $maps_url, [], ACSAGMA_AGENDA_VERSION, [ 'in_footer' => true, 'strategy' => 'defer', ] ); } wp_enqueue_script( 'acs-agenda-admin', ACSAGMA_AGENDA_PLUGIN_URL . 'js/acs-admin.js', ['jquery', 'jquery-ui-dialog', 'acs-multidatespicker'], ACSAGMA_AGENDA_VERSION, [ 'in_footer' => true, 'strategy' => 'defer', ] ); wp_localize_script('acs-agenda-admin', 'acsagmaAgendaAdmin', [ 'ajaxUrl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('acsagma_agenda_admin_nonce'), 'hasGoogleMaps' => !empty($google_maps_api_key), 'i18n' => $this->get_admin_translations(), ]); } /** * Decide if frontend assets should load to avoid enqueuing everywhere. */ private function should_load_frontend_assets(): bool { if (is_admin()) { return false; } $should_load = false; if (is_singular()) { $post = get_post(); if ($post && has_shortcode($post->post_content, 'acsagma_agenda')) { $should_load = true; } $template = get_page_template_slug($post); if ($template === 'page-agenda.php') { $should_load = true; } } return apply_filters('acsagma_agenda_should_enqueue_assets', $should_load); } /** * Get admin translations for JavaScript */ private function get_admin_translations(): array { return [ 'confirm' => __('Confirm', 'acs-agenda-manager'), 'cancel' => __('Cancel', 'acs-agenda-manager'), 'update' => __('Update', 'acs-agenda-manager'), 'add' => __('Add', 'acs-agenda-manager'), 'close' => __('Close', 'acs-agenda-manager'), 'calendar' => __('Calendar', 'acs-agenda-manager'), 'copied' => __('Copied', 'acs-agenda-manager'), 'fieldEmpty' => __('The field is empty', 'acs-agenda-manager'), 'invalidDate' => __('Invalid date format. Use dd/mm/yy', 'acs-agenda-manager'), 'selectImage' => __('Select or upload an image', 'acs-agenda-manager'), 'filteredEvents' => __('Filtered events', 'acs-agenda-manager'), 'confirmDelete' => __('Do you really want to delete this event?', 'acs-agenda-manager'), 'editor' => __('Editor', 'acs-agenda-manager'), 'addEvent' => __('Add an event', 'acs-agenda-manager'), ]; } /** * Check and update plugin version */ public function check_version(): void { $installed_version = get_option('acsagma_agenda_manager_plugin_version', '0'); if (version_compare($installed_version, ACSAGMA_AGENDA_VERSION, '<')) { ACSAGMA_Database::update_schema(); update_option('acsagma_agenda_manager_plugin_version', ACSAGMA_AGENDA_VERSION); } } /** * Render agenda shortcode */ public function render_agenda_shortcode($atts = [], ?string $content = null): string { $events = ACSAGMA_Event::get_upcoming_events(); if (empty($events)) { return '

' . esc_html__('No upcoming events.', 'acs-agenda-manager') . '

'; } return ACSAGMA_Template::render_agenda($events); } /** * AJAX handler for read more dialog */ public function ajax_read_more(): void { check_ajax_referer('acsagma_agenda_nonce', 'nonce', true); $post_id = isset($_POST['postid']) ? absint($_POST['postid']) : 0; $href = isset($_POST['href']) ? esc_url_raw(wp_unslash($_POST['href'])) : ''; if (!$post_id) { wp_send_json_error(__('Invalid post ID', 'acs-agenda-manager')); } $post = get_post($post_id); if (!$post) { wp_send_json_error(__('Post not found', 'acs-agenda-manager')); } echo wp_kses_post(ACSAGMA_Template::render_read_more_dialog($post, $href)); wp_die(); } /** * Get a page by its title using WP_Query (replaces deprecated get_page_by_title) * * @param string $title Page title * @return WP_Post|null */ public static function get_page_by_title(string $title): ?WP_Post { $query = new WP_Query([ 'post_type' => 'page', 'title' => $title, 'post_status' => 'all', 'posts_per_page' => 1, 'no_found_rows' => true, 'ignore_sticky_posts' => true, 'update_post_term_cache' => false, 'update_post_meta_cache' => false, ]); return $query->have_posts() ? $query->posts[0] : null; } /** * Plugin activation */ public static function activate(): void { require_once plugin_dir_path(__FILE__) . 'class/class-acs-database.php'; ACSAGMA_Database::create_table(); // Install bundled translations to WordPress languages directory self::install_translations(); $page_name = get_option('acsagma_page', 'Agenda'); $existing_page = self::get_page_by_title($page_name); if (!$existing_page) { wp_insert_post([ 'post_title' => $page_name, 'post_name' => sanitize_title($page_name), 'post_status' => 'publish', 'post_type' => 'page', 'post_content' => '[acsagma_agenda]', 'comment_status' => 'closed', 'ping_status' => 'closed', ]); } update_option('acsagma_page', $page_name); update_option('acsagma_agenda_manager_plugin_version', ACSAGMA_AGENDA_VERSION); } /** * Install bundled translations to WordPress languages directory. * This allows WordPress to auto-load translations without load_plugin_textdomain(). */ private static function install_translations(): void { $source_dir = plugin_dir_path(__FILE__) . 'lang/'; $target_dir = WP_LANG_DIR . '/plugins/'; // Create target directory if it doesn't exist if (!is_dir($target_dir)) { wp_mkdir_p($target_dir); } // Get all .mo files from bundled translations $mo_files = glob($source_dir . '*.mo'); if (!$mo_files) { return; } foreach ($mo_files as $mo_file) { $filename = basename($mo_file); $target_file = $target_dir . $filename; // Only copy if source is newer or target doesn't exist if (!file_exists($target_file) || filemtime($mo_file) > filemtime($target_file)) { copy($mo_file, $target_file); } } } /** * Plugin deactivation */ public static function deactivate(): void { // Clean up transients if needed delete_transient('acsagma_agenda_events_cache'); } } // Activation/deactivation hooks register_activation_hook(__FILE__, ['ACSAGMA_Agenda_Manager', 'activate']); register_deactivation_hook(__FILE__, ['ACSAGMA_Agenda_Manager', 'deactivate']); // Initialize plugin add_action('plugins_loaded', function() { ACSAGMA_Agenda_Manager::get_instance(); });