plugin_name = $plugin_name; $this->version = $version; // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Only reading for tab display $this->current_tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'dashboard'; // phpcs:enable WordPress.Security.NonceVerification.Recommended } /** * Enqueue admin styles */ public function enqueue_styles($hook) { if (strpos($hook, '3task-calendar') === false) { return; } wp_enqueue_style('wp-color-picker'); wp_enqueue_style( $this->plugin_name . '-admin', THREECAL_PLUGIN_URL . 'admin/css/admin.css', array(), $this->version ); } /** * Enqueue admin scripts */ public function enqueue_scripts($hook) { if (strpos($hook, '3task-calendar') === false) { return; } wp_enqueue_media(); wp_enqueue_script('wp-color-picker'); wp_enqueue_script( $this->plugin_name . '-admin', THREECAL_PLUGIN_URL . 'admin/js/admin.js', array('jquery', 'wp-color-picker'), $this->version, true ); wp_localize_script($this->plugin_name . '-admin', 'threecal_admin', array( 'ajax_url' => admin_url('admin-ajax.php'), 'admin_url' => admin_url(), 'nonce' => wp_create_nonce('threecal_admin_nonce'), 'strings' => array( 'confirm_delete' => __('Are you sure you want to delete this item?', '3task-calendar'), 'confirm_delete_multiple' => __('Are you sure you want to delete the selected items?', '3task-calendar'), 'select_image' => __('Select Image', '3task-calendar'), 'use_image' => __('Use Image', '3task-calendar'), 'saving' => __('Saving...', '3task-calendar'), 'saved' => __('Saved!', '3task-calendar'), 'error' => __('An error occurred.', '3task-calendar') ) )); } /** * Add menu pages. */ public function add_menu_pages() { add_menu_page( __( '3task Calendar', '3task-calendar' ), __( '3task Calendar', '3task-calendar' ), 'edit_posts', '3task-calendar', array( $this, 'render_admin_page' ), 'dashicons-calendar-alt', 26 ); } /** * Get available tabs. * * @return array Tabs configuration. */ private function get_tabs() { return array( 'dashboard' => array( 'title' => __( 'Dashboard', '3task-calendar' ), 'icon' => 'dashicons-dashboard', ), 'events' => array( 'title' => __( 'Events', '3task-calendar' ), 'icon' => 'dashicons-calendar-alt', ), 'categories' => array( 'title' => __( 'Categories', '3task-calendar' ), 'icon' => 'dashicons-category', ), 'locations' => array( 'title' => __( 'Locations', '3task-calendar' ), 'icon' => 'dashicons-location', ), 'settings' => array( 'title' => __( 'Settings', '3task-calendar' ), 'icon' => 'dashicons-admin-settings', ), 'help' => array( 'title' => __( 'Help', '3task-calendar' ), 'icon' => 'dashicons-editor-help', ), ); } /** * Render main admin page with tabs. */ public function render_admin_page() { $tabs = $this->get_tabs(); ?>

render_tab_content(); ?>
current_tab ) { case 'dashboard': $this->render_dashboard(); break; case 'events': $this->render_events(); break; case 'categories': $this->render_categories(); break; case 'locations': $this->render_locations(); break; case 'settings': $this->render_settings(); break; case 'help': $this->render_help(); break; default: $this->render_dashboard(); } } /** * Render dashboard tab. */ private function render_dashboard() { $stats = $this->get_event_stats(); ?>

render_mini_calendar(); ?>
render_edit_event(); return; } // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Only reading for navigation, no data modification. if ( isset( $_GET['action'] ) && $_GET['action'] === 'new' ) { // phpcs:enable WordPress.Security.NonceVerification.Recommended $this->render_new_event(); return; } // List all events $events = $this->get_events(); ?>

start_date ) ) ); ?>
title); ?>
description, 0, 100)) . '...'; ?>
isset( $_POST['date_format'] ) ? sanitize_text_field( wp_unslash( $_POST['date_format'] ) ) : get_option( 'date_format' ), 'time_format' => isset( $_POST['time_format'] ) ? sanitize_text_field( wp_unslash( $_POST['time_format'] ) ) : get_option( 'time_format' ), 'week_starts_on' => isset( $_POST['week_starts_on'] ) ? absint( $_POST['week_starts_on'] ) : 1, 'default_view' => isset( $_POST['default_view'] ) ? sanitize_text_field( wp_unslash( $_POST['default_view'] ) ) : 'month', 'show_event_time' => ! empty( $_POST['show_event_time'] ), 'show_event_location' => ! empty( $_POST['show_event_location'] ), 'events_per_page' => isset( $_POST['events_per_page'] ) ? absint( $_POST['events_per_page'] ) : 10, ); update_option( 'threecal_settings', $settings ); echo '

' . esc_html__( 'Settings saved!', '3task-calendar' ) . '

'; } $settings = get_option('threecal_settings', array( 'date_format' => get_option('date_format'), 'time_format' => get_option('time_format'), 'week_starts_on' => 1, 'default_view' => 'month', 'show_event_time' => true, 'show_event_location' => true, 'events_per_page' => 10 )); ?>

/>
/>

[threecal]

[threecal view="month" category="1" location="2" theme="default" show_filters="true" show_legend="true" week_starts_on="1"]
view month
category 0
location 0
theme default
show_filters true
show_legend true
week_starts_on

[threecal_events]

[threecal_events category="1" location="2" limit="10" view="list" show_past="false" show_pagination="true" columns="3"]
category 0
location 0
limit 10
view list
show_past false
show_pagination true
columns 3

[threecal_upcoming]

[threecal_upcoming limit="5" category="1" show_date="true" show_time="true" show_location="true"]
limit 5
category 0
show_date true
show_time true
show_location true

[threecal_event id="123" show_map="true" show_description="true"]
id
show_map true
show_description true

[threecal_mini]

[threecal_mini category="1" show_nav="true" show_today="true" week_starts_on="1"]
category 0
show_nav true
show_today true
week_starts_on

  1. Widgets', '3task-calendar' ); ?>
  2. [threecal_upcoming limit="3"]


[threecal_mini] -
[threecal_upcoming limit="3"] -

' . esc_html__( 'support forum', '3task-calendar' ) . '' ); ?>

get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}threecal_events" ); $published_events = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}threecal_events WHERE status = 'published'" ); $draft_events = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}threecal_events WHERE status = 'draft'" ); $total_categories = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}threecal_categories" ); // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return array( 'total_events' => $total_events ? $total_events : 0, 'published_events' => $published_events ? $published_events : 0, 'draft_events' => $draft_events ? $draft_events : 0, 'total_categories' => $total_categories ? $total_categories : 0, ); } /** * Get events */ private function get_events() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table for events. $results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}threecal_events ORDER BY start_date ASC" ); return $results ? $results : array(); } /** * Get categories */ private function get_categories() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table for categories. $results = $wpdb->get_results( " SELECT c.*, COUNT(e.id) as event_count FROM {$wpdb->prefix}threecal_categories c LEFT JOIN {$wpdb->prefix}threecal_events e ON c.id = e.category_id GROUP BY c.id ORDER BY c.name ASC " ); return $results ? $results : array(); } /** * Get locations */ private function get_locations() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table for locations. $results = $wpdb->get_results( " SELECT * FROM {$wpdb->prefix}threecal_locations ORDER BY name ASC " ); return $results ? $results : array(); } /** * Render mini calendar */ private function render_mini_calendar() { $current_date = current_time('Y-m-d'); $events = $this->get_upcoming_events(5); if ( empty( $events ) ) { echo '

' . esc_html__( 'No upcoming events.', '3task-calendar' ) . '

'; return; } echo '
'; foreach ($events as $event) { echo '
'; echo '' . esc_html($event->title) . '
'; echo '' . esc_html( wp_date( 'M j, Y', strtotime( $event->start_date ) ) ) . ''; echo '
'; } echo '
'; } /** * Get upcoming events */ private function get_upcoming_events( $limit = 5 ) { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table for events. $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}threecal_events WHERE start_date >= %s AND status = 'published' ORDER BY start_date ASC LIMIT %d", current_time( 'Y-m-d' ), $limit ) ); return $results ? $results : array(); } /** * Register settings */ public function register_settings() { register_setting('threecal_settings', 'threecal_settings', array( 'sanitize_callback' => array($this, 'sanitize_settings') )); // Handle form submissions $this->handle_form_submissions(); } /** * Sanitize settings */ public function sanitize_settings($input) { $sanitized = array(); // General $sanitized['date_format'] = sanitize_text_field($input['date_format'] ?? get_option('date_format')); $sanitized['time_format'] = sanitize_text_field($input['time_format'] ?? get_option('time_format')); $sanitized['week_starts_on'] = absint($input['week_starts_on'] ?? 1); $sanitized['default_view'] = sanitize_text_field($input['default_view'] ?? 'month'); $sanitized['default_theme'] = sanitize_text_field($input['default_theme'] ?? 'default'); // Display $sanitized['show_event_time'] = !empty($input['show_event_time']); $sanitized['show_event_location'] = !empty($input['show_event_location']); $sanitized['show_event_description'] = !empty($input['show_event_description']); $sanitized['events_per_page'] = absint($input['events_per_page'] ?? 10); $sanitized['enable_event_popup'] = !empty($input['enable_event_popup']); // Google Maps $sanitized['google_maps_api_key'] = sanitize_text_field($input['google_maps_api_key'] ?? ''); $sanitized['default_map_zoom'] = absint($input['default_map_zoom'] ?? 14); $sanitized['default_map_type'] = sanitize_text_field($input['default_map_type'] ?? 'roadmap'); // Email $sanitized['enable_notifications'] = !empty($input['enable_notifications']); $sanitized['notification_sender_name'] = sanitize_text_field($input['notification_sender_name'] ?? ''); $sanitized['notification_sender_email'] = sanitize_email($input['notification_sender_email'] ?? ''); $sanitized['notification_template'] = wp_kses_post($input['notification_template'] ?? ''); // SEO $sanitized['enable_schema'] = !empty($input['enable_schema']); // Advanced $sanitized['delete_data_on_uninstall'] = !empty($input['delete_data_on_uninstall']); return $sanitized; } /** * Handle form submissions */ private function handle_form_submissions() { // Save event. if ( isset( $_POST['threecal_save_event'] ) && check_admin_referer( 'threecal_save_event' ) ) { $this->save_event(); } // Delete event. if ( isset( $_GET['action'] ) && 'delete' === $_GET['action'] && isset( $_GET['id'], $_GET['_wpnonce'] ) ) { $nonce = sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ); $id = absint( $_GET['id'] ); if ( wp_verify_nonce( $nonce, 'threecal_delete_' . $id ) ) { $this->delete_event( $id ); } } // Save location. if ( isset( $_POST['threecal_save_location'] ) && check_admin_referer( 'threecal_save_location' ) ) { $this->save_location(); } // Delete location. if ( isset( $_GET['action'] ) && 'delete_location' === $_GET['action'] && isset( $_GET['id'], $_GET['_wpnonce'] ) ) { $nonce = sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ); $id = absint( $_GET['id'] ); if ( wp_verify_nonce( $nonce, 'threecal_delete_location_' . $id ) ) { $this->delete_location( $id ); } } // Save category. if ( isset( $_POST['threecal_save_category'] ) && check_admin_referer( 'threecal_save_category' ) ) { $this->save_category(); } // Delete category. if ( isset( $_GET['action'] ) && 'delete_category' === $_GET['action'] && isset( $_GET['id'], $_GET['_wpnonce'] ) ) { $nonce = sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ); $id = absint( $_GET['id'] ); if ( wp_verify_nonce( $nonce, 'threecal_delete_category_' . $id ) ) { $this->delete_category( $id ); } } } /** * Save event */ private function save_event() { if ( ! current_user_can( 'create_threecal_events' ) && ! current_user_can( 'edit_threecal_events' ) ) { wp_die( esc_html__( 'Permission denied.', '3task-calendar' ) ); } // Nonce is already verified in handle_form_submissions() via check_admin_referer(). // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in handle_form_submissions. $event_id = isset( $_POST['event_id'] ) ? absint( $_POST['event_id'] ) : 0; if ( $event_id > 0 ) { $event = ThreeCal_Event::get( $event_id ); if ( ! $event ) { wp_die( esc_html__( 'Event not found.', '3task-calendar' ) ); } } else { $event = new ThreeCal_Event(); } $event->title = isset( $_POST['event_title'] ) ? sanitize_text_field( wp_unslash( $_POST['event_title'] ) ) : ''; $event->description = isset( $_POST['event_description'] ) ? wp_kses_post( wp_unslash( $_POST['event_description'] ) ) : ''; $event->start_date = isset( $_POST['event_start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['event_start_date'] ) ) : ''; $event->end_date = isset( $_POST['event_end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['event_end_date'] ) ) : ''; $event->all_day = isset( $_POST['event_all_day'] ); $event->location_id = isset( $_POST['event_location'] ) ? absint( $_POST['event_location'] ) : 0; $event->url = isset( $_POST['event_url'] ) ? esc_url_raw( wp_unslash( $_POST['event_url'] ) ) : ''; $event->featured_image = isset( $_POST['event_featured_image'] ) ? absint( $_POST['event_featured_image'] ) : 0; $event->color = isset( $_POST['event_color'] ) ? ( sanitize_hex_color( wp_unslash( $_POST['event_color'] ) ) ?: '#3788d8' ) : '#3788d8'; $event->status = isset( $_POST['event_status'] ) ? sanitize_text_field( wp_unslash( $_POST['event_status'] ) ) : 'publish'; if ( $event->save() ) { // Set categories. $categories = isset( $_POST['event_categories'] ) ? array_map( 'absint', $_POST['event_categories'] ) : array(); $event->set_categories( $categories ); // phpcs:enable WordPress.Security.NonceVerification.Missing // Redirect $redirect = add_query_arg(array( 'page' => '3task-calendar', 'tab' => 'events', 'action' => 'edit', 'event' => $event->id, 'message' => 'saved' ), admin_url('admin.php')); wp_safe_redirect( $redirect ); exit; } else { wp_die( esc_html__( 'Error saving event.', '3task-calendar' ) ); } } /** * Delete event */ private function delete_event($id) { if (!current_user_can('delete_threecal_events')) { wp_die( esc_html__( 'Permission denied.', '3task-calendar' ) ); } $event = ThreeCal_Event::get($id); if ($event && $event->delete()) { wp_safe_redirect( add_query_arg( array( 'page' => '3task-calendar', 'message' => 'deleted' ), admin_url('admin.php'))); exit; } } /** * Save location */ private function save_location() { if ( ! current_user_can( 'manage_threecal_locations' ) ) { wp_die( esc_html__( 'Permission denied.', '3task-calendar' ) ); } // Nonce is already verified in handle_form_submissions() via check_admin_referer(). // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in handle_form_submissions. $location_id = isset( $_POST['location_id'] ) ? absint( $_POST['location_id'] ) : 0; if ( $location_id > 0 ) { $location = ThreeCal_Location::get( $location_id ); if ( ! $location ) { wp_die( esc_html__( 'Location not found.', '3task-calendar' ) ); } } else { $location = new ThreeCal_Location(); } $location->name = isset( $_POST['location_name'] ) ? sanitize_text_field( wp_unslash( $_POST['location_name'] ) ) : ''; $location->address = isset( $_POST['location_address'] ) ? sanitize_text_field( wp_unslash( $_POST['location_address'] ) ) : ''; $location->city = isset( $_POST['location_city'] ) ? sanitize_text_field( wp_unslash( $_POST['location_city'] ) ) : ''; $location->postal_code = isset( $_POST['location_postal_code'] ) ? sanitize_text_field( wp_unslash( $_POST['location_postal_code'] ) ) : ''; $location->country = isset( $_POST['location_country'] ) ? sanitize_text_field( wp_unslash( $_POST['location_country'] ) ) : 'DE'; $location->phone = isset( $_POST['location_phone'] ) ? sanitize_text_field( wp_unslash( $_POST['location_phone'] ) ) : ''; $location->email = isset( $_POST['location_email'] ) ? sanitize_email( wp_unslash( $_POST['location_email'] ) ) : ''; $location->website = isset( $_POST['location_website'] ) ? esc_url_raw( wp_unslash( $_POST['location_website'] ) ) : ''; $location->description = isset( $_POST['location_description'] ) ? wp_kses_post( wp_unslash( $_POST['location_description'] ) ) : ''; $location->featured_image = isset( $_POST['location_featured_image'] ) ? absint( $_POST['location_featured_image'] ) : 0; // Geocode if API key is set. $do_geocode = isset( $_POST['location_geocode'] ) ? sanitize_text_field( wp_unslash( $_POST['location_geocode'] ) ) : ''; if ( $do_geocode ) { $location->geocode(); } else { $location->latitude = isset( $_POST['location_latitude'] ) ? floatval( $_POST['location_latitude'] ) : null; $location->longitude = isset( $_POST['location_longitude'] ) ? floatval( $_POST['location_longitude'] ) : null; } // phpcs:enable WordPress.Security.NonceVerification.Missing if ( $location->save() ) { wp_safe_redirect( add_query_arg( array( 'page' => '3task-calendar', 'tab' => 'locations', 'message' => 'saved', ), admin_url( 'admin.php' ) ) ); exit; } } /** * Delete location */ private function delete_location($id) { if (!current_user_can('manage_threecal_locations')) { wp_die( esc_html__( 'Permission denied.', '3task-calendar' ) ); } $location = ThreeCal_Location::get($id); if ($location && $location->delete()) { wp_safe_redirect( add_query_arg( array( 'page' => '3task-calendar', 'tab' => 'locations', 'message' => 'deleted' ), admin_url('admin.php'))); exit; } } /** * Save category */ private function save_category() { if ( ! current_user_can( 'manage_threecal_categories' ) ) { wp_die( esc_html__( 'Permission denied.', '3task-calendar' ) ); } // Nonce is already verified in handle_form_submissions() via check_admin_referer(). // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in handle_form_submissions. $category_id = isset( $_POST['category_id'] ) ? absint( $_POST['category_id'] ) : 0; if ( $category_id > 0 ) { $category = ThreeCal_Category::get( $category_id ); if ( ! $category ) { wp_die( esc_html__( 'Category not found.', '3task-calendar' ) ); } } else { $category = new ThreeCal_Category(); } $category->name = isset( $_POST['category_name'] ) ? sanitize_text_field( wp_unslash( $_POST['category_name'] ) ) : ''; $category->slug = isset( $_POST['category_slug'] ) ? sanitize_title( wp_unslash( $_POST['category_slug'] ) ) : ''; $category->description = isset( $_POST['category_description'] ) ? sanitize_textarea_field( wp_unslash( $_POST['category_description'] ) ) : ''; $category->color = isset( $_POST['category_color'] ) ? ( sanitize_hex_color( wp_unslash( $_POST['category_color'] ) ) ?: '#3788d8' ) : '#3788d8'; $category->parent_id = isset( $_POST['category_parent'] ) ? absint( $_POST['category_parent'] ) : 0; $category->sort_order = isset( $_POST['category_sort_order'] ) ? absint( $_POST['category_sort_order'] ) : 0; // phpcs:enable WordPress.Security.NonceVerification.Missing if ( $category->save() ) { wp_safe_redirect( add_query_arg( array( 'page' => '3task-calendar', 'tab' => 'categories', 'message' => 'saved', ), admin_url( 'admin.php' ) ) ); exit; } } /** * Delete category */ private function delete_category($id) { if (!current_user_can('manage_threecal_categories')) { wp_die( esc_html__( 'Permission denied.', '3task-calendar' ) ); } $category = ThreeCal_Category::get($id); if ($category && $category->delete()) { wp_safe_redirect( add_query_arg( array( 'page' => '3task-calendar', 'tab' => 'categories', 'message' => 'deleted' ), admin_url('admin.php'))); exit; } } /** * Render events page */ public function render_events_page() { include THREECAL_PLUGIN_DIR . 'admin/views/events-list.php'; } /** * Render edit event form (inline in events tab) */ private function render_edit_event() { include THREECAL_PLUGIN_DIR . 'admin/views/event-edit.php'; } /** * Render new event form (inline in events tab) */ private function render_new_event() { include THREECAL_PLUGIN_DIR . 'admin/views/event-edit.php'; } /** * Render edit event page */ public function render_edit_event_page() { include THREECAL_PLUGIN_DIR . 'admin/views/event-edit.php'; } /** * Render locations page */ public function render_locations_page() { include THREECAL_PLUGIN_DIR . 'admin/views/locations.php'; } /** * Render categories page */ public function render_categories_page() { include THREECAL_PLUGIN_DIR . 'admin/views/categories.php'; } /** * Render settings page */ public function render_settings_page() { include THREECAL_PLUGIN_DIR . 'admin/views/settings.php'; } }