PluginProbe
Jock On Air Now (JOAN) / 6.0.7
Jock On Air Now (JOAN) v6.0.7
4.7.3 4.7.4 4.7.5 4.7.6 4.7.7 4.7.8 5.0 5.1 5.2.0 5.3.0 5.4.0 5.5.0 5.5.1 5.5.2 5.5.3 5.5.4 5.5.6 5.5.7 5.5.8 5.5.9 5.6 5.6.1 5.6.2 5.6.3 5.6.4 All 77 releases
joan / joan.php

joan.php in Jock On Air Now (JOAN) 6.0.7, at joan.php

379 lines 16.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Jock On Air Now
4 * Plugin URI: https://wordpress.org/plugins/joan/
5 * Description: Display your station's current and upcoming on-air schedule in real-time with timezone awareness, Elementor and Visual Composer/WPBakery Page Builder integration support. Your site visitors can keep track of your on air schedule and their favorite shows.
6 * Author: G &amp; D Enterprises, Inc.
7 * Version: 6.0.7
8 * Author URI: https://www.gandenterprisesinc.com
9 * Text Domain: joan
10 * Domain Path: /languages
11 */
12
13 defined( 'ABSPATH' ) || exit;
14
15 // Constants
16 define( 'JOAN_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
17 define( 'JOAN_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
18 // Update the version constant to mirror the plugin header. This helps
19 // internal version checks and migration routines stay in sync with the
20 // version defined above. When bumping the plugin version, always
21 // update this constant accordingly.
22 define( 'JOAN_VERSION', '6.0.7' );
23
24 // Block automatic updates when jumping from any pre‑6.x version to 6.x.
25 // Version 6.x introduced a complete redesign that wipes the existing
26 // schedule table. The previous implementation keyed off of the
27 // specific version “6.0.0”; keep the code here but update the comment
28 // for clarity. Because 6.0.7 is still a 6.x release, the
29 // comparison remains against 6.0.0.
30 add_filter('auto_update_plugin', function($update, $item) {
31 if (isset($item->slug) && $item->slug === 'joan') {
32 $current_version = get_option('joan_plugin_version', '0.0.0');
33 $new_version = $item->new_version;
34
35 // Block auto-update if jumping from 5.x to 6.x
36 if (version_compare($current_version, '6.0.0', '<') &&
37 version_compare($new_version, '6.0.0', '>=')) {
38 return false; // Block automatic update
39 }
40 }
41 return $update;
42 }, 10, 2);
43
44 add_action('admin_notices', function() {
45 $current_version = get_option('joan_plugin_version', '0.0.0');
46 if (version_compare($current_version, '6.0.0', '<')) {
47 $screen = get_current_screen();
48 if ($screen && strpos($screen->id, 'plugins') !== false) {
49 echo '<div class="notice notice-error is-dismissible">';
50 echo '<h3>CRITICAL: JOAN Plugin Update Warning</h3>';
51 echo '<p><strong>Version 6.0.7 will permanently delete your existing schedule data.</strong></p>';
52 echo '<p>Before updating: Export your schedule, take screenshots, and prepare to re-enter all shows manually.</p>';
53 echo '<p><a href="admin.php?page=joan-settings&tab=help">View migration guide</a></p>';
54 echo '</div>';
55 }
56 }
57 });
58
59 // Version detection and migration handling
60 add_action('admin_init', 'joan_check_version_compatibility');
61
62 function joan_check_version_compatibility() {
63 // Check if this is a fresh activation or upgrade
64 $current_version = get_option('joan_plugin_version', '0.0.0');
65 $old_version_detected = version_compare($current_version, '6.0.0', '<') && $current_version !== '0.0.0';
66
67 // If old version detected and user hasn't made a decision
68 if ($old_version_detected && !get_option('joan_migration_handled', false)) {
69 // Check if user clicked a migration button
70 if (isset($_POST['joan_migration_action'])) {
71 if ($_POST['joan_migration_action'] === 'proceed' && wp_verify_nonce($_POST['joan_migration_nonce'], 'joan_migration')) {
72 // User chose to proceed - wipe old data and create new
73 joan_migrate_from_old_version();
74 update_option('joan_migration_handled', true);
75 update_option('joan_plugin_version', JOAN_VERSION);
76 add_action('admin_notices', 'joan_migration_success_notice');
77 } elseif ($_POST['joan_migration_action'] === 'backup' && wp_verify_nonce($_POST['joan_migration_nonce'], 'joan_migration')) {
78 // User chose to backup - deactivate plugin and show message
79 deactivate_plugins(plugin_basename(__FILE__));
80 add_action('admin_notices', 'joan_backup_notice');
81 return;
82 }
83 } else {
84 // Show migration warning
85 add_action('admin_notices', 'joan_migration_warning_notice');
86 return;
87 }
88 } elseif (!$old_version_detected) {
89 // Fresh installation or compatible version
90 update_option('joan_plugin_version', JOAN_VERSION);
91 update_option('joan_migration_handled', true);
92 }
93 }
94
95 function joan_migration_warning_notice() {
96 ?>
97 <div class="notice notice-warning" style="padding: 20px; border-left: 4px solid #ffba00;">
98 <h2 style="margin-top: 0;">WARNING: JOAN Version 6.0.* - Important Migration Notice</h2>
99 <p><strong>You are upgrading from an older version of JOAN to version 6.0.*, which is a complete redesign.</strong></p>
100 <p><strong style="color: #d63638;">WARNING: Your existing schedule data from version 5.9.0 and below cannot be automatically imported and will be permanently deleted.</strong></p>
101
102 <div style="background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 15px 0;">
103 <h3>What happens if you proceed:</h3>
104 <ul>
105 <li><strong>BENEFITS:</strong> You'll get all the amazing new features of JOAN 6.0.7</li>
106 <li><strong>BENEFITS:</strong> Modern admin interface with better usability</li>
107 <li><strong>BENEFITS:</strong> Smart image positioning and enhanced widgets</li>
108 <li><strong>BENEFITS:</strong> Improved timezone handling and page builder support</li>
109 <li><strong style="color: #d63638;">WARNING:</strong> All existing schedule data will be permanently deleted</li>
110 <li><strong style="color: #d63638;">WARNING:</strong> You'll need to re-enter your shows manually</li>
111 </ul>
112 </div>
113
114 <div style="background: #e8f4fd; padding: 15px; border-radius: 5px; margin: 15px 0;">
115 <h3>Recommended steps:</h3>
116 <ol>
117 <li>Go to your current JOAN admin page and take screenshots or export your schedule</li>
118 <li>Write down all your show names, times, jock names, and image URLs</li>
119 <li>Come back here and choose to proceed with the upgrade</li>
120 <li>Re-enter your schedule using the new, improved interface</li>
121 </ol>
122 </div>
123
124 <form method="post" style="margin-top: 20px;">
125 <?php wp_nonce_field('joan_migration', 'joan_migration_nonce'); ?>
126 <p style="margin-bottom: 15px;"><strong>What would you like to do?</strong></p>
127 <div style="display: flex; gap: 15px; align-items: center;">
128 <button type="submit" name="joan_migration_action" value="backup" class="button button-secondary">
129 Wait, Let Me Backup My Schedule First
130 </button>
131 <button type="submit" name="joan_migration_action" value="proceed" class="button button-primary" id="joan-proceed-migration">
132 I Understand, Activate Version 6.0.7 Anyway
133 </button>
134 </div>
135 </form>
136 </div>
137 <?php
138 }
139
140 function joan_backup_notice() {
141 ?>
142 <div class="notice notice-info">
143 <h2>JOAN Plugin Deactivated</h2>
144 <p>Good choice! The JOAN plugin has been deactivated so you can backup your schedule.</p>
145 <p><strong>To backup your schedule:</strong></p>
146 <ol>
147 <li>Go to your JOAN admin page (if still accessible)</li>
148 <li>Take screenshots of your schedule</li>
149 <li>Write down show names, times, jock names, and image URLs</li>
150 <li>When ready, reactivate the plugin and choose to proceed with the upgrade</li>
151 </ol>
152 </div>
153 <?php
154 }
155
156 function joan_migration_success_notice() {
157 ?>
158 <div class="notice notice-success is-dismissible">
159 <h2>JOAN 6.0.7 Successfully Activated!</h2>
160 <p>The plugin has been upgraded and old data has been cleaned up. You can now start adding your shows using the new interface.</p>
161 <p><a href="<?php echo admin_url('admin.php?page=joan-schedule'); ?>" class="button button-primary">Go to Schedule Manager</a></p>
162 </div>
163 <?php
164 }
165
166 function joan_migrate_from_old_version() {
167 global $wpdb;
168
169 // List of old table names to clean up
170 $old_tables = [
171 $wpdb->prefix . 'joan_schedule_legacy',
172 $wpdb->prefix . 'showtime_jockonair', // Very old table name
173 $wpdb->prefix . 'onair_schedule', // Another possible old name
174 ];
175
176 // Drop old tables
177 foreach ($old_tables as $table) {
178 $wpdb->query("DROP TABLE IF EXISTS $table");
179 }
180
181 // Drop current table if it exists and recreate it fresh
182 $current_table = $wpdb->prefix . 'joan_schedule';
183 $wpdb->query("DROP TABLE IF EXISTS $current_table");
184
185 // Create fresh table
186 joan_ensure_table();
187
188 // Clear any old options
189 delete_option('joan_legacy_data_imported');
190 delete_option('joan_old_version_data');
191
192 // Set default options for new installation
193 update_option('joan_time_format', '12');
194 update_option('joan_timezone', 'America/New_York');
195 update_option('joan_show_next_show', '1');
196 update_option('joan_show_jock_image', '1');
197 update_option('joan_widget_max_width', '300');
198 update_option('joan_schedule_status', 'active');
199 update_option('joan_off_air_message', 'We\'re currently off the air. Please check back later!');
200
201 error_log('JOAN: Successfully migrated from old version to 6.0.7');
202 }
203
204 // Load core functionality only after migration check
205 if (get_option('joan_migration_handled', false)) {
206 require_once JOAN_PLUGIN_DIR . 'includes/crud.php';
207 require_once JOAN_PLUGIN_DIR . 'includes/admin-menu.php';
208 require_once JOAN_PLUGIN_DIR . 'includes/shortcodes.php';
209 require_once JOAN_PLUGIN_DIR . 'includes/widget.php';
210 require_once JOAN_PLUGIN_DIR . 'includes/import-legacy.php';
211
212 // Load page builder compatibility check
213 require_once JOAN_PLUGIN_DIR . 'includes/compatibility-check.php';
214 }
215
216 // Enqueue admin assets
217 function joan_enqueue_admin_assets($hook) {
218 // Enqueue admin script when migration notice is shown or on plugin pages
219 if (!get_option('joan_migration_handled', false) || strpos($hook, 'joan') !== false) {
220 wp_enqueue_script('joan-admin', JOAN_PLUGIN_URL . 'assets/js/admin.js', ['jquery'], JOAN_VERSION, true);
221 }
222 }
223 add_action('admin_enqueue_scripts', 'joan_enqueue_admin_assets');
224
225 // Enqueue frontend assets with enhanced styling
226 function joan_enqueue_assets() {
227 if (!get_option('joan_migration_handled', false)) return;
228
229 wp_enqueue_style( 'joan-style', JOAN_PLUGIN_URL . 'assets/css/joan.css', [], JOAN_VERSION );
230 wp_enqueue_script( 'joan-script', JOAN_PLUGIN_URL . 'assets/js/joan.js', ['jquery'], JOAN_VERSION, true );
231 wp_localize_script('joan-script', 'joan_ajax', [
232 'ajaxurl' => admin_url('admin-ajax.php'),
233 'nonce' => wp_create_nonce('joan_frontend_nonce'),
234 'settings' => [
235 'show_next_show' => get_option('joan_show_next_show', '1'),
236 'show_jock_image' => get_option('joan_show_jock_image', '1'),
237 'joan_show_local_time' => get_option('joan_show_local_time', '1'),
238 'joan_allow_timezone_selector' => get_option('joan_allow_timezone_selector', '1'),
239 'time_format' => get_option('joan_time_format', '12'),
240 'widget_max_width' => get_option('joan_widget_max_width', '300'),
241 'joan_dark_mode' => get_option('joan_dark_mode', 'auto'),
242 'joan_dark_mode_override' => get_option('joan_dark_mode_override', '0'),
243 // NEW: show day element symbols setting
244 'joan_show_day_emoji' => get_option('joan_show_day_emoji', '0'),
245
246 /*
247 * Pass additional display options to the frontend. These values are used by
248 * joan.js to determine how show titles and jock/host names should be
249 * rendered. Without exposing these, the JavaScript cannot honor the
250 * "Host Field Label" and "Link Assignment" options configured in the
251 * Display tab.
252 */
253 // The Host Field Label should default to an empty string. When
254 // provided, this value acts as a fallback and is only used if
255 // a show does not have its own host/jock name. Leaving it
256 // empty preserves per‑show names and avoids overriding them.
257 'joan_jock_field_label' => get_option('joan_jock_field_label', ''),
258 'joan_link_assignment' => get_option('joan_link_assignment', 'jock_name'),
259 'joan_image_display_mode' => get_option('joan_image_display_mode', 'constrained')
260 ]
261 ]);
262 }
263 add_action( 'wp_enqueue_scripts', 'joan_enqueue_assets' );
264
265 // Database setup
266 function joan_ensure_table() {
267 global $wpdb;
268 $table_name = $wpdb->prefix . 'joan_schedule';
269 $charset_collate = $wpdb->get_charset_collate();
270
271 $sql = "CREATE TABLE IF NOT EXISTS $table_name (
272 id INT AUTO_INCREMENT PRIMARY KEY,
273 show_name VARCHAR(255),
274 start_day VARCHAR(10),
275 start_time TIME,
276 end_time TIME,
277 image_url TEXT,
278 dj_name VARCHAR(255),
279 link_url TEXT
280 ) $charset_collate;";
281
282 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
283 dbDelta($sql);
284 }
285
286 function joan_ensure_columns_exist() {
287 global $wpdb;
288 $table = $wpdb->prefix . 'joan_schedule';
289
290 $expected = [
291 'start_day' => "ALTER TABLE $table ADD COLUMN start_day VARCHAR(10)",
292 'start_time' => "ALTER TABLE $table ADD COLUMN start_time TIME",
293 'end_time' => "ALTER TABLE $table ADD COLUMN end_time TIME",
294 'show_name' => "ALTER TABLE $table ADD COLUMN show_name VARCHAR(255)",
295 'image_url' => "ALTER TABLE $table ADD COLUMN image_url TEXT",
296 'dj_name' => "ALTER TABLE $table ADD COLUMN dj_name VARCHAR(255)",
297 'link_url' => "ALTER TABLE $table ADD COLUMN link_url TEXT",
298 ];
299
300 $columns = $wpdb->get_col("DESC $table", 0);
301
302 foreach ($expected as $col => $sql) {
303 if (!in_array($col, $columns)) {
304 $wpdb->query($sql);
305 }
306 }
307 }
308
309 // Initialize only after migration is handled
310 //CodeSig: sgketg
311 add_action('admin_init', function() {
312 if (get_option('joan_migration_handled', false)) {
313 joan_ensure_table();
314 joan_ensure_columns_exist();
315 joan_add_capabilities();
316 }
317 });
318
319 // Add user capability for managing schedules
320 function joan_add_capabilities() {
321 $role = get_role('administrator');
322 if ($role) {
323 $role->add_cap('manage_joan_schedule');
324 }
325
326 // Also allow editors to manage schedules
327 $editor = get_role('editor');
328 if ($editor) {
329 $editor->add_cap('manage_joan_schedule');
330 }
331 }
332
333 // Plugin activation hook
334 register_activation_hook(__FILE__, function() {
335 // Version check will happen in admin_init
336 // Don't set up database until migration is handled
337 });
338
339 // Plugin deactivation hook
340 register_deactivation_hook(__FILE__, function() {
341 // Clean up scheduled events if any
342 wp_clear_scheduled_hook('joan_cleanup_event');
343
344 // Don't remove migration flags on deactivation
345 // delete_option('joan_migration_handled');
346 // delete_option('joan_plugin_version');
347 });
348
349 // Add settings link on plugins page
350 add_filter('plugin_action_links_' . plugin_basename(__FILE__), function($links) {
351 if (get_option('joan_migration_handled', false)) {
352 $settings_link = '<a href="admin.php?page=joan-schedule">' . __('Schedule', 'joan') . '</a>';
353 array_unshift($links, $settings_link);
354 }
355 return $links;
356 });
357
358 // Add AJAX endpoint for frontend widget updates
359 add_action('wp_ajax_joan_widget_refresh', 'joan_handle_widget_refresh');
360 add_action('wp_ajax_nopriv_joan_widget_refresh', 'joan_handle_widget_refresh');
361
362 function joan_handle_widget_refresh() {
363 if (!get_option('joan_migration_handled', false)) {
364 wp_die('Plugin not properly initialized');
365 }
366
367 // Verify nonce for security
368 if (!wp_verify_nonce($_POST['nonce'], 'joan_frontend_nonce')) {
369 wp_die('Security check failed');
370 }
371
372 // Return current show data
373 $crud = new stdClass();
374 $crud->action = 'read';
375 $crud->read_type = 'current';
376
377 // Use existing CRUD handler
378 do_action('wp_ajax_show_time_curd');
379 }