PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents / 4.4.1
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents v4.4.1
4.6.5 4.6.4 4.6.3 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / EmbedPress / Includes / Classes / Database / Analytics_Schema.php
embedpress / EmbedPress / Includes / Classes / Database Last commit date
Analytics_Schema.php 11 months ago
Analytics_Schema.php
508 lines
1 <?php
2
3 namespace EmbedPress\Includes\Classes\Database;
4
5 defined('ABSPATH') or die("No direct script access allowed.");
6
7 /**
8 * EmbedPress Analytics Database Schema
9 *
10 * Handles creation and management of analytics database tables
11 *
12 * @package EmbedPress
13 * @author EmbedPress <help@embedpress.com>
14 * @copyright Copyright (C) 2023 WPDeveloper. All rights reserved.
15 * @license GPLv3 or later
16 * @since 4.2.7
17 */
18 class Analytics_Schema
19 {
20 /**
21 * Database version for schema updates
22 */
23 const DB_VERSION = '1.0.7';
24
25 /**
26 * Create all analytics tables
27 *
28 * @return void
29 */
30 public static function create_tables()
31 {
32
33 global $wpdb;
34
35 $charset_collate = $wpdb->get_charset_collate();
36
37 // Check if tables need to be created or updated
38 $current_version = get_option('embedpress_analytics_db_version', '0.0.0');
39
40 // Also check if tables actually exist in database
41 $tables_exist = self::check_tables_exist();
42
43 if (version_compare($current_version, self::DB_VERSION, '<') || !$tables_exist) {
44
45 self::create_content_table($charset_collate);
46 self::create_views_table($charset_collate);
47 self::create_browser_info_table($charset_collate);
48 self::create_milestones_table($charset_collate);
49 self::create_referrers_table($charset_collate);
50
51 // Run migrations for existing installations
52 self::run_migrations($current_version);
53
54 // Update database version
55 update_option('embedpress_analytics_db_version', self::DB_VERSION);
56
57 // Log table creation for debugging
58 }
59 }
60
61 /**
62 * Check if all required tables exist
63 *
64 * @return bool
65 */
66 private static function check_tables_exist()
67 {
68 global $wpdb;
69
70 $required_tables = [
71 $wpdb->prefix . 'embedpress_analytics_content',
72 $wpdb->prefix . 'embedpress_analytics_views',
73 $wpdb->prefix . 'embedpress_analytics_browser_info',
74 $wpdb->prefix . 'embedpress_analytics_milestones',
75 $wpdb->prefix . 'embedpress_analytics_referrers'
76 ];
77
78 foreach ($required_tables as $table) {
79 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table));
80 if (!$table_exists) {
81 return false;
82 }
83 }
84
85 return true;
86 }
87
88 /**
89 * Force create all tables (for debugging/repair)
90 *
91 * @return void
92 */
93 public static function force_create_tables()
94 {
95 global $wpdb;
96
97 $charset_collate = $wpdb->get_charset_collate();
98
99
100 self::create_content_table($charset_collate);
101 self::create_views_table($charset_collate);
102 self::create_browser_info_table($charset_collate);
103 self::create_milestones_table($charset_collate);
104 self::create_referrers_table($charset_collate);
105
106 // Update database version
107 update_option('embedpress_analytics_db_version', self::DB_VERSION);
108
109 }
110
111 /**
112 * Create embedpress_analytics_content table
113 * Tracks embedded content by type (Elementor/Gutenberg/Shortcode)
114 *
115 * @param string $charset_collate
116 * @return void
117 */
118 private static function create_content_table($charset_collate)
119 {
120 global $wpdb;
121
122 $table_name = $wpdb->prefix . 'embedpress_analytics_content';
123
124 $sql = "CREATE TABLE IF NOT EXISTS $table_name (
125 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
126 content_id varchar(255) NOT NULL,
127 content_type varchar(50) NOT NULL DEFAULT 'unknown',
128 embed_type varchar(100) NOT NULL,
129 embed_url text NOT NULL,
130 post_id bigint(20) unsigned DEFAULT NULL,
131 page_url text DEFAULT NULL,
132 title varchar(500) DEFAULT NULL,
133 total_views bigint(20) unsigned DEFAULT 0,
134 total_impressions bigint(20) unsigned DEFAULT 0,
135 total_clicks bigint(20) unsigned DEFAULT 0,
136 created_at datetime DEFAULT CURRENT_TIMESTAMP,
137 updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
138 PRIMARY KEY (id),
139 UNIQUE KEY unique_page_embed (page_url(255), embed_type),
140 KEY idx_content_type (content_type),
141 KEY idx_embed_type (embed_type),
142 KEY idx_post_id (post_id),
143 KEY idx_created_at (created_at),
144 KEY idx_total_views (total_views)
145 ) $charset_collate;";
146
147 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
148 dbDelta($sql);
149 }
150
151 /**
152 * Create embedpress_analytics_views table
153 * Tracks individual views/interactions with embedded content
154 *
155 * @param string $charset_collate
156 * @return void
157 */
158 private static function create_views_table($charset_collate)
159 {
160 global $wpdb;
161
162 $table_name = $wpdb->prefix . 'embedpress_analytics_views';
163
164 $sql = "CREATE TABLE IF NOT EXISTS $table_name (
165 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
166 content_id varchar(255) NOT NULL,
167 user_id varchar(255) DEFAULT NULL,
168 session_id varchar(255) NOT NULL,
169 user_ip varchar(45) DEFAULT NULL,
170 user_agent text DEFAULT NULL,
171 referrer_url text DEFAULT NULL,
172 page_url text DEFAULT NULL,
173 interaction_type enum('impression', 'click', 'view', 'play', 'pause', 'complete') NOT NULL DEFAULT 'impression',
174 interaction_data json DEFAULT NULL,
175 view_duration int(11) unsigned DEFAULT 0,
176 created_at datetime DEFAULT CURRENT_TIMESTAMP,
177 PRIMARY KEY (id),
178 KEY idx_content_id (content_id),
179 KEY idx_user_id (user_id),
180 KEY idx_session_id (session_id),
181 KEY idx_interaction_type (interaction_type),
182 KEY idx_created_at (created_at),
183 KEY idx_user_ip (user_ip),
184 KEY idx_content_interaction (content_id, interaction_type),
185 KEY idx_daily_stats (content_id, interaction_type, created_at),
186 KEY idx_user_content_interaction (user_id, content_id, interaction_type),
187 KEY idx_deduplication (user_id, content_id, interaction_type, created_at)
188 ) $charset_collate;";
189
190 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
191 dbDelta($sql);
192 }
193
194 /**
195 * Create embedpress_analytics_browser_info table
196 * Tracks browser and device information for analytics
197 *
198 * @param string $charset_collate
199 * @return void
200 */
201 private static function create_browser_info_table($charset_collate)
202 {
203 global $wpdb;
204
205 $table_name = $wpdb->prefix . 'embedpress_analytics_browser_info';
206
207 $sql = "CREATE TABLE IF NOT EXISTS $table_name (
208 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
209 user_id varchar(255) DEFAULT NULL,
210 session_id varchar(255) NOT NULL,
211 browser_fingerprint varchar(64) DEFAULT NULL,
212 browser_name varchar(100) DEFAULT NULL,
213 browser_version varchar(50) DEFAULT NULL,
214 operating_system varchar(100) DEFAULT NULL,
215 device_type enum('desktop', 'mobile', 'tablet', 'unknown') DEFAULT 'unknown',
216 screen_resolution varchar(20) DEFAULT NULL,
217 language varchar(10) DEFAULT NULL,
218 timezone varchar(50) DEFAULT NULL,
219 country varchar(100) DEFAULT NULL,
220 city varchar(100) DEFAULT NULL,
221 user_agent text DEFAULT NULL,
222 created_at datetime DEFAULT CURRENT_TIMESTAMP,
223 PRIMARY KEY (id),
224 UNIQUE KEY unique_user_fingerprint (user_id, browser_fingerprint),
225 KEY idx_user_id (user_id),
226 KEY idx_session_id (session_id),
227 KEY idx_browser_fingerprint (browser_fingerprint),
228 KEY idx_browser_name (browser_name),
229 KEY idx_operating_system (operating_system),
230 KEY idx_device_type (device_type),
231 KEY idx_country (country),
232 KEY idx_created_at (created_at)
233 ) $charset_collate;";
234
235 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
236 dbDelta($sql);
237 }
238
239 /**
240 * Create embedpress_analytics_milestones table
241 * Tracks milestone achievements for upsell features
242 *
243 * @param string $charset_collate
244 * @return void
245 */
246 private static function create_milestones_table($charset_collate)
247 {
248 global $wpdb;
249
250 $table_name = $wpdb->prefix . 'embedpress_analytics_milestones';
251
252 $sql = "CREATE TABLE IF NOT EXISTS $table_name (
253 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
254 milestone_type enum('total_views', 'total_embeds', 'daily_views', 'monthly_views') NOT NULL,
255 milestone_value bigint(20) unsigned NOT NULL,
256 achieved_value bigint(20) unsigned NOT NULL,
257 is_notified tinyint(1) DEFAULT 0,
258 achieved_at datetime DEFAULT CURRENT_TIMESTAMP,
259 notified_at datetime DEFAULT NULL,
260 PRIMARY KEY (id),
261 KEY idx_milestone_type (milestone_type),
262 KEY idx_milestone_value (milestone_value),
263 KEY idx_is_notified (is_notified),
264 KEY idx_achieved_at (achieved_at)
265 ) $charset_collate;";
266
267 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
268 dbDelta($sql);
269 }
270
271 /**
272 * Create embedpress_analytics_referrers table
273 * Tracks referrer URLs with optimized view and click counting
274 *
275 * @param string $charset_collate
276 * @return void
277 */
278 private static function create_referrers_table($charset_collate)
279 {
280 global $wpdb;
281
282 $table_name = $wpdb->prefix . 'embedpress_analytics_referrers';
283
284 $sql = "CREATE TABLE IF NOT EXISTS $table_name (
285 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
286 referrer_url text NOT NULL,
287 referrer_domain varchar(255) NOT NULL,
288 referrer_source varchar(100) DEFAULT NULL,
289 utm_source varchar(100) DEFAULT NULL,
290 utm_medium varchar(100) DEFAULT NULL,
291 utm_campaign varchar(255) DEFAULT NULL,
292 utm_term varchar(255) DEFAULT NULL,
293 utm_content varchar(255) DEFAULT NULL,
294 total_views bigint(20) unsigned DEFAULT 0,
295 total_clicks bigint(20) unsigned DEFAULT 0,
296 unique_visitors bigint(20) unsigned DEFAULT 0,
297 first_visit datetime DEFAULT CURRENT_TIMESTAMP,
298 last_visit datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
299 created_at datetime DEFAULT CURRENT_TIMESTAMP,
300 updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
301 PRIMARY KEY (id),
302 UNIQUE KEY unique_referrer_url (referrer_url(255)),
303 KEY idx_referrer_domain (referrer_domain),
304 KEY idx_referrer_source (referrer_source),
305 KEY idx_utm_source (utm_source),
306 KEY idx_utm_medium (utm_medium),
307 KEY idx_utm_campaign (utm_campaign),
308 KEY idx_total_views (total_views),
309 KEY idx_total_clicks (total_clicks),
310 KEY idx_unique_visitors (unique_visitors),
311 KEY idx_first_visit (first_visit),
312 KEY idx_last_visit (last_visit),
313 KEY idx_created_at (created_at)
314 ) $charset_collate;";
315
316 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
317 dbDelta($sql);
318 }
319
320 /**
321 * Run database migrations for version updates
322 *
323 * @param string $current_version
324 * @return void
325 */
326 private static function run_migrations($current_version)
327 {
328 global $wpdb;
329
330 // Migration from 1.0.0 to 1.0.1: Fix country field size
331 if (version_compare($current_version, '1.0.1', '<')) {
332 $table_name = $wpdb->prefix . 'embedpress_analytics_browser_info';
333
334 // Check if table exists and has the old country field
335 $column_info = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'country'");
336
337 if (!empty($column_info)) {
338 $column = $column_info[0];
339 // If country field is varchar(5), update it to varchar(100)
340 if (strpos($column->Type, 'varchar(5)') !== false) {
341 $wpdb->query("ALTER TABLE $table_name MODIFY COLUMN country varchar(100) DEFAULT NULL");
342 }
343 }
344 }
345
346 // Migration from 1.0.1 to 1.0.2: Change content_type from ENUM to VARCHAR
347 if (version_compare($current_version, '1.0.2', '<')) {
348 $table_name = $wpdb->prefix . 'embedpress_analytics_content';
349
350 // Check if table exists
351 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));
352 if ($table_exists) {
353 // Check current column type
354 $column_info = $wpdb->get_results("SHOW COLUMNS FROM $table_name LIKE 'content_type'");
355
356 if (!empty($column_info)) {
357 $column = $column_info[0];
358 // If content_type is still an ENUM, update it to VARCHAR
359 if (strpos($column->Type, 'enum') !== false) {
360 $wpdb->query("ALTER TABLE $table_name MODIFY COLUMN content_type varchar(50) NOT NULL DEFAULT 'unknown'");
361 }
362 }
363 }
364 }
365
366 // Migration from 1.0.4 to 1.0.5: Update unique key to prevent duplicate page+embed combinations
367 if (version_compare($current_version, '1.0.5', '<')) {
368 $table_name = $wpdb->prefix . 'embedpress_analytics_content';
369
370 // Check if table exists
371 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));
372 if ($table_exists) {
373 // Check if old unique key exists and drop it
374 $old_key_exists = $wpdb->get_var("SHOW INDEX FROM $table_name WHERE Key_name = 'unique_content'");
375 if ($old_key_exists) {
376 $wpdb->query("ALTER TABLE $table_name DROP INDEX unique_content");
377 }
378
379 // Clean up duplicate entries before adding unique key
380 // Keep the first record for each page_url + embed_type combination
381 $wpdb->query("
382 DELETE t1 FROM $table_name t1
383 INNER JOIN $table_name t2
384 WHERE t1.id > t2.id
385 AND t1.page_url = t2.page_url
386 AND t1.embed_type = t2.embed_type
387 ");
388
389 // Check if new unique key already exists
390 $new_key_exists = $wpdb->get_var("SHOW INDEX FROM $table_name WHERE Key_name = 'unique_page_embed'");
391 if (!$new_key_exists) {
392 // Add new unique key with proper key length for TEXT field
393 $wpdb->query("ALTER TABLE $table_name ADD UNIQUE KEY unique_page_embed (page_url(255), embed_type)");
394 }
395
396 }
397 }
398
399 // Migration from 1.0.5 to 1.0.6: Normalize embed_type to lowercase and merge duplicates
400 if (version_compare($current_version, '1.0.6', '<')) {
401 $table_name = $wpdb->prefix . 'embedpress_analytics_content';
402
403 // Check if table exists
404 $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));
405 if ($table_exists) {
406 // First, temporarily drop the unique constraint to allow updates
407 $wpdb->query("ALTER TABLE $table_name DROP INDEX unique_page_embed");
408
409 // Update all embed_type values to lowercase
410 $wpdb->query("UPDATE $table_name SET embed_type = LOWER(embed_type)");
411
412 // Now merge duplicates by summing their counters
413 $duplicates = $wpdb->get_results("
414 SELECT page_url, embed_type,
415 GROUP_CONCAT(id) as ids,
416 SUM(total_views) as total_views,
417 SUM(total_clicks) as total_clicks,
418 SUM(total_impressions) as total_impressions,
419 COUNT(*) as count
420 FROM $table_name
421 GROUP BY page_url, embed_type
422 HAVING count > 1
423 ");
424
425 foreach ($duplicates as $duplicate) {
426 $ids = explode(',', $duplicate->ids);
427 $keep_id = $ids[0]; // Keep the first record
428 $delete_ids = array_slice($ids, 1); // Delete the rest
429
430 // Update the kept record with merged totals
431 $wpdb->update(
432 $table_name,
433 [
434 'total_views' => $duplicate->total_views,
435 'total_clicks' => $duplicate->total_clicks,
436 'total_impressions' => $duplicate->total_impressions,
437 'updated_at' => current_time('mysql')
438 ],
439 ['id' => $keep_id]
440 );
441
442 // Delete duplicate records
443 if (!empty($delete_ids)) {
444 $delete_ids_str = implode(',', array_map('intval', $delete_ids));
445 $wpdb->query("DELETE FROM $table_name WHERE id IN ($delete_ids_str)");
446 }
447 }
448
449 // Re-add the unique constraint
450 $wpdb->query("ALTER TABLE $table_name ADD UNIQUE KEY unique_page_embed (page_url(255), embed_type)");
451
452 }
453 }
454
455 // Migration from 1.0.6 to 1.0.7: Create referrers table
456 if (version_compare($current_version, '1.0.7', '<')) {
457 self::create_referrers_table($wpdb->get_charset_collate());
458 }
459
460 }
461
462 /**
463 * Drop all analytics tables
464 * Used for plugin uninstallation
465 *
466 * @return void
467 */
468 public static function drop_tables()
469 {
470 global $wpdb;
471
472 $tables = [
473 $wpdb->prefix . 'embedpress_analytics_content',
474 $wpdb->prefix . 'embedpress_analytics_views',
475 $wpdb->prefix . 'embedpress_analytics_browser_info',
476 $wpdb->prefix . 'embedpress_analytics_milestones',
477 $wpdb->prefix . 'embedpress_analytics_referrers'
478 ];
479
480 foreach ($tables as $table) {
481 $wpdb->query("DROP TABLE IF EXISTS $table");
482 }
483
484 // Remove database version option
485 delete_option('embedpress_analytics_db_version');
486 }
487
488
489
490 /**
491 * Get table names with prefix
492 *
493 * @return array
494 */
495 public static function get_table_names()
496 {
497 global $wpdb;
498
499 return [
500 'content' => $wpdb->prefix . 'embedpress_analytics_content',
501 'views' => $wpdb->prefix . 'embedpress_analytics_views',
502 'browser_info' => $wpdb->prefix . 'embedpress_analytics_browser_info',
503 'milestones' => $wpdb->prefix . 'embedpress_analytics_milestones',
504 'referrers' => $wpdb->prefix . 'embedpress_analytics_referrers'
505 ];
506 }
507 }
508