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 |
|
|
- Widgets', '3task-calendar' ); ?>
-
[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';
}
}