$google['api_key'], 'calendarId' => $google['calendar_id'], 'i18n' => [ 'testing' => __( 'Testing...', 'hi-theme-toolkit' ), 'connecting' => __( 'Connecting to Google Calendar...', 'hi-theme-toolkit' ), 'successTitle' => __( '✓ Connection successful!', 'hi-theme-toolkit' ), 'calendarLabel' => __( 'Calendar:', 'hi-theme-toolkit' ), 'eventsFoundLabel'=> __( 'Events found:', 'hi-theme-toolkit' ), 'unknownError' => __( 'Unknown error', 'hi-theme-toolkit' ), 'failureTitle' => __( '✗ Connection failed', 'hi-theme-toolkit' ), 'testButton' => __( 'Test Connection', 'hi-theme-toolkit' ), ], ] ); wp_add_inline_script( 'toolkit-calendar-test', ' jQuery(document).ready(function($) { var cfg = toolkitCalendarTest; $("#test-google-connection").on("click", function() { var button = $(this); var result = $("#test-result"); button.prop("disabled", true).text(cfg.i18n.testing); result.empty().append($("

").text(cfg.i18n.connecting)); var testUrl = "https://www.googleapis.com/calendar/v3/calendars/" + encodeURIComponent(cfg.calendarId) + "/events?key=" + cfg.apiKey + "&maxResults=1&singleEvents=true&orderBy=startTime&" + "timeMin=" + new Date().toISOString(); fetch(testUrl) .then(function(response) { if (!response.ok) { return response.json().then(function(err) { return Promise.reject(err); }); } return response.json(); }) .then(function(data) { var box = $("

").addClass("notice notice-success inline"); $("

").append($("").text(cfg.i18n.successTitle)).appendTo(box); $("

").text(cfg.i18n.calendarLabel + " " + (data.summary || cfg.calendarId)).appendTo(box); $("

").text(cfg.i18n.eventsFoundLabel + " " + (data.items ? data.items.length : 0)).appendTo(box); result.empty().append(box); }) .catch(function(error) { var msg = error && error.error ? error.error.message : error && error.message ? error.message : cfg.i18n.unknownError; var box = $("

").addClass("notice notice-error inline"); $("

").append($("").text(cfg.i18n.failureTitle)).appendTo(box); $("

").text(msg).appendTo(box); result.empty().append(box); }) .finally(function() { button.prop("disabled", false).html( " " + cfg.i18n.testButton ); }); }); }); ' ); } /** * Register admin menu and submenus */ public static function register_menu() { // Main menu page add_menu_page( __( 'Calendar', 'hi-theme-toolkit' ), // Page title __( 'Calendar', 'hi-theme-toolkit' ), // Menu title 'manage_options', // Capability self::MENU_SLUG, // Menu slug [ self::class, 'render_main_page' ], // Callback 'dashicons-calendar-alt', // Icon 81 // Position (below Settings) ); // Submenu: Paramètres (settings) add_submenu_page( self::MENU_SLUG, __( 'Google Calendar Settings', 'hi-theme-toolkit' ), __( 'Link Google Calendar', 'hi-theme-toolkit' ), 'manage_options', self::MENU_SLUG . '-google-calendar', [ self::class, 'render_settings_page' ] ); // Submenu: Configuration Événements WordPress add_submenu_page( self::MENU_SLUG, __( 'WordPress Events Configuration', 'hi-theme-toolkit' ), __( 'Link WordPress Events', 'hi-theme-toolkit' ), 'manage_options', self::MENU_SLUG . '-wordpress-events', [ self::class, 'render_wordpress_events_page' ] ); } /** * Register settings */ public static function register_settings() { register_setting( 'toolkit_calendar_settings_group', self::OPTION_NAME, [ 'type' => 'array', 'sanitize_callback' => [ self::class, 'sanitize_settings' ], 'default' => self::get_default_settings(), ] ); } /** * Get default settings */ private static function get_default_settings() { return [ 'google' => [ 'enabled' => false, 'api_key' => '', 'calendar_id' => '', 'sync_interval' => 'daily', 'max_results' => 250, 'time_min_offset' => '-30', // days in the past 'time_max_offset' => '365', // days in the future ], 'wordpress_events' => [ 'enabled' => false, 'custom_post_type' => 'calendar_event', 'acf_field_group' => '', 'sync_interval' => 'daily', ], ]; } /** * Sanitize settings */ public static function sanitize_settings( $input ) { // Get existing settings to preserve values not in the current form $existing = get_option( self::OPTION_NAME, self::get_default_settings() ); $sanitized = $existing; if ( isset( $input['google'] ) ) { $sanitized['google'] = [ 'enabled' => ! empty( $input['google']['enabled'] ), 'api_key' => sanitize_text_field( $input['google']['api_key'] ?? '' ), 'calendar_id' => sanitize_text_field( $input['google']['calendar_id'] ?? '' ), 'sync_interval' => sanitize_text_field( $input['google']['sync_interval'] ?? 'daily' ), 'max_results' => absint( $input['google']['max_results'] ?? 250 ), 'time_min_offset' => intval( $input['google']['time_min_offset'] ?? -30 ), 'time_max_offset' => intval( $input['google']['time_max_offset'] ?? 365 ), ]; } if ( isset( $input['wordpress_events'] ) ) { $sanitized['wordpress_events'] = [ 'enabled' => ! empty( $input['wordpress_events']['enabled'] ), 'custom_post_type' => sanitize_text_field( $input['wordpress_events']['custom_post_type'] ?? 'calendar_event' ), 'acf_field_group' => sanitize_text_field( $input['wordpress_events']['acf_field_group'] ?? '' ), 'sync_interval' => sanitize_text_field( $input['wordpress_events']['sync_interval'] ?? 'daily' ), ]; } return $sanitized; } /** * Render main page (overview) */ public static function render_main_page() { $settings = get_option( self::OPTION_NAME, self::get_default_settings() ); $google_enabled = $settings['google']['enabled'] ?? false; $wp_events_enabled = $settings['wordpress_events']['enabled'] ?? false; $wp_events_cpt = $settings['wordpress_events']['custom_post_type'] ?? ''; $last_sync = get_option( 'toolkit_calendar_last_sync' ); // Get stats $total_events = wp_count_posts( 'calendar_event' ); $published_events = $total_events->publish ?? 0; ?>

: ✓ ✗
: ✓ () ✗
:
:

', $success_messages ), 'success' ); } // Add error messages if ( ! empty( $error_messages ) ) { add_settings_error( 'toolkit_calendar_messages', 'toolkit_calendar_sync_error', implode( '
', $error_messages ), 'error' ); } // If no results at all if ( empty( $results ) ) { add_settings_error( 'toolkit_calendar_messages', 'toolkit_calendar_sync_error', __( 'No calendar source is enabled.', 'hi-theme-toolkit' ), 'error' ); } } set_transient( 'settings_errors', get_settings_errors(), 30 ); wp_safe_redirect( esc_url_raw( admin_url( 'admin.php?page=' . self::MENU_SLUG . '&settings-updated=true' ) ) ); exit; } /** * Get all ACF fields recursively including sub-fields and repeater fields */ private static function get_all_acf_fields( $parent_field = null, $prefix = '' ) { $all_fields = []; if ( $parent_field ) { // Get sub-fields from parent $fields = []; if ( function_exists( 'acf_get_fields' ) ) { $fields = acf_get_fields( $parent_field ); } } else { // Get all field groups $field_groups = []; if ( function_exists( 'acf_get_field_groups' ) ) { $field_groups = acf_get_field_groups(); } $fields = []; foreach ( $field_groups as $group ) { if ( function_exists( 'acf_get_fields' ) ) { $group_fields = acf_get_fields( $group['key'] ); if ( $group_fields ) { foreach ( $group_fields as $field ) { $field['_group_title'] = $group['title']; $fields[] = $field; } } } } } if ( empty( $fields ) ) { return $all_fields; } foreach ( $fields as $field ) { $field_label = $prefix . $field['label']; $group_title = isset( $field['_group_title'] ) ? $field['_group_title'] . ' → ' : ''; $all_fields[] = [ 'key' => $field['key'], 'name' => $field['name'], 'label' => $field_label, 'type' => $field['type'], 'display_label' => $group_title . $field_label . ' (' . $field['type'] . ')', ]; // If field has sub-fields (repeater, group, flexible content, etc.) if ( in_array( $field['type'], [ 'repeater', 'group', 'flexible_content', 'clone' ], true ) ) { $sub_fields = self::get_all_acf_fields( $field['key'], $prefix . $field['label'] . ' → ' ); $all_fields = array_merge( $all_fields, $sub_fields ); } } return $all_fields; } /** * Render WordPress Events configuration page */ public static function render_wordpress_events_page() { $settings = get_option( self::OPTION_NAME, self::get_default_settings() ); $wp_events = $settings['wordpress_events'] ?? self::get_default_settings()['wordpress_events']; // Get available post types $post_types = get_post_types( [ 'public' => true ], 'objects' ); // Get all ACF fields if ACF is active $all_acf_fields = []; if ( function_exists( 'acf_get_field_groups' ) && function_exists( 'acf_get_fields' ) ) { $all_acf_fields = self::get_all_acf_fields(); } ?>