$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 = $("
").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 = $(" ").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;
?>
', $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();
}
?>