| 1 |
<?php |
| 2 |
/** |
| 3 |
* Plugin Name: Test Telemetry Plugin |
| 4 |
* Plugin URI: https://linno.co |
| 5 |
* Description: A test plugin to demonstrate and validate Linno Telemetry SDK functionality |
| 6 |
* Version: 1.0.0 |
| 7 |
* Author: Linno |
| 8 |
* Author URI: https://linno.co |
| 9 |
* License: GPL-2.0-or-later |
| 10 |
* Text Domain: test-telemetry-plugin |
| 11 |
* Requires at least: 5.0 |
| 12 |
* Requires PHP: 7.4 |
| 13 |
*/ |
| 14 |
|
| 15 |
// Exit if accessed directly |
| 16 |
if (!defined('ABSPATH')) { |
| 17 |
exit; |
| 18 |
} |
| 19 |
|
| 20 |
// Load Composer autoloader |
| 21 |
// Try plugin's own vendor directory first (if installed via Composer in plugin dir) |
| 22 |
if (file_exists(__DIR__ . '/vendor/autoload.php')) { |
| 23 |
require_once __DIR__ . '/vendor/autoload.php'; |
| 24 |
} |
| 25 |
// Fall back to SDK's vendor directory (when testing from SDK repository) |
| 26 |
elseif (file_exists(__DIR__ . '/../../vendor/autoload.php')) { |
| 27 |
require_once __DIR__ . '/../../vendor/autoload.php'; |
| 28 |
} |
| 29 |
// If neither exists, show error |
| 30 |
else { |
| 31 |
add_action('admin_notices', function() { |
| 32 |
echo '<div class="notice notice-error"><p>'; |
| 33 |
echo '<strong>Test Telemetry Plugin Error:</strong> Composer autoloader not found. '; |
| 34 |
echo 'Please run <code>composer install</code> from the plugin directory or the SDK root directory.'; |
| 35 |
echo '</p></div>'; |
| 36 |
}); |
| 37 |
return; |
| 38 |
} |
| 39 |
|
| 40 |
use LinnoSDK\Telemetry\Client; |
| 41 |
|
| 42 |
/** |
| 43 |
* Global telemetry client instance. |
| 44 |
* |
| 45 |
* @var Client|null |
| 46 |
*/ |
| 47 |
$test_telemetry_client = null; |
| 48 |
|
| 49 |
$text_domain = 'test-telemetry-plugin'; |
| 50 |
|
| 51 |
try { |
| 52 |
// Optional: Set text domain for i18n (defaults to plugin slug if not set) |
| 53 |
Client::set_text_domain( $text_domain ); |
| 54 |
|
| 55 |
// Initialize the telemetry client using a config array. |
| 56 |
// Only 'pluginFile' and 'slug' are required. |
| 57 |
// Set 'driver' to 'posthog' or 'open_panel'. Omit it to run with no driver (events are silently dropped). |
| 58 |
$test_telemetry_client = new Client([ |
| 59 |
'pluginFile' => __FILE__, |
| 60 |
'slug' => 'test-telemetry-plugin', |
| 61 |
'pluginName' => 'Test Telemetry Plugin', |
| 62 |
'version' => '1.0.0', |
| 63 |
|
| 64 |
// --- PostHog driver example --- |
| 65 |
// 'driver' => 'posthog', |
| 66 |
// 'driver_config' => [ |
| 67 |
// 'host' => 'https://app.posthog.com', |
| 68 |
// 'api_key' => 'phc_YOUR_POSTHOG_API_KEY', |
| 69 |
// ], |
| 70 |
|
| 71 |
// --- OpenPanel driver example --- |
| 72 |
'driver' => 'open_panel', |
| 73 |
'apiKey' => 'op_YOUR_CLIENT_ID', |
| 74 |
'apiSecret' => 'sec_YOUR_API_SECRET', |
| 75 |
]); |
| 76 |
|
| 77 |
// Define optional automatic triggers. |
| 78 |
// Each key is optional — omitting a key simply disables that module. |
| 79 |
$test_telemetry_client->define_triggers([ |
| 80 |
|
| 81 |
// Onboarding completion: fires activation/onboarding_completed once. |
| 82 |
// Both 'setup' (legacy) and 'onboarding' (canonical) are accepted. |
| 83 |
'setup' => 'my_plugin_setup_complete', |
| 84 |
// 'onboarding' => 'my_plugin_onboarding_finished', // canonical alias |
| 85 |
|
| 86 |
// Feature Used: fires retention/feature_used. |
| 87 |
'feature_used' => [ |
| 88 |
'funnel_created' => [ |
| 89 |
'hook' => 'my_plugin_funnel_created', |
| 90 |
], |
| 91 |
], |
| 92 |
|
| 93 |
// AHA-milestone indicators (fire activation/aha_reached). |
| 94 |
// Both 'kui' (legacy) and 'aha' (canonical) are accepted. |
| 95 |
'aha' => [ |
| 96 |
'order_received' => [ |
| 97 |
'hook' => 'woocommerce_order_created', |
| 98 |
'threshold' => ['count' => 2, 'period' => 'week'], |
| 99 |
'callback' => function( $order_id ) { |
| 100 |
return ['order_id' => $order_id]; |
| 101 |
}, |
| 102 |
], |
| 103 |
'student_enrolled' => [ |
| 104 |
'hook' => 'lms_student_enrolled', |
| 105 |
'threshold' => ['count' => 2, 'period' => 'week'], |
| 106 |
'callback' => function( $course_id, $student_id ) { |
| 107 |
return ['course_id' => $course_id, 'student_id' => $student_id]; |
| 108 |
}, |
| 109 |
], |
| 110 |
], |
| 111 |
]); |
| 112 |
|
| 113 |
} catch (Exception $e) { |
| 114 |
error_log('Test Telemetry Plugin: Failed to initialize - ' . $e->getMessage()); |
| 115 |
} |
| 116 |
|
| 117 |
/** |
| 118 |
* Track a custom event when a post is published. |
| 119 |
* |
| 120 |
* Option A — direct PHP API call: |
| 121 |
* |
| 122 |
* @param int $post_id Post ID |
| 123 |
* @since 1.0.0 |
| 124 |
*/ |
| 125 |
function test_telemetry_track_post_published($post_id) { |
| 126 |
global $test_telemetry_client; |
| 127 |
if ($test_telemetry_client instanceof Client) { |
| 128 |
$test_telemetry_client->track('post_published', [ |
| 129 |
'post_id' => $post_id, |
| 130 |
'post_type' => get_post_type($post_id), |
| 131 |
]); |
| 132 |
} |
| 133 |
} |
| 134 |
add_action('publish_post', 'test_telemetry_track_post_published'); |
| 135 |
|
| 136 |
/** |
| 137 |
* Option B — WordPress action hook: |
| 138 |
* Any code in the plugin can fire this action to send a custom telemetry event: |
| 139 |
* |
| 140 |
* do_action( 'test-telemetry-plugin_telemetry_track', 'post_published', ['post_id' => 42] ); |
| 141 |
* |
| 142 |
* The client registers this handler automatically during initialization. |
| 143 |
* No extra setup is required. |
| 144 |
*/ |
| 145 |
|
| 146 |
// --- Examples of optional trigger-module tracking (commented out) --- |
| 147 |
|
| 148 |
// Example: Track onboarding completion (once, requires consent) |
| 149 |
// function test_telemetry_track_setup_complete() { |
| 150 |
// global $test_telemetry_client; |
| 151 |
// if ($test_telemetry_client instanceof Client) { |
| 152 |
// $test_telemetry_client->track_setup(['setup_method' => 'quick_install']); |
| 153 |
// // Emits: activation/onboarding_completed |
| 154 |
// } |
| 155 |
// } |
| 156 |
// add_action('my_plugin_setup_complete', 'test_telemetry_track_setup_complete'); |
| 157 |
|
| 158 |
// Example: Track feature usage via static convenience method (requires consent) |
| 159 |
// Call this after the client is initialized to register the event for a specific hook. |
| 160 |
Client::add_feature_used_event( 'my_plugin_settings_exported', 'Export Settings' ); |
| 161 |
// When 'my_plugin_settings_exported' action fires, a retention/feature_used event |
| 162 |
// is sent with feature='Export Settings'. |
| 163 |
|
| 164 |
// With optional extra parameters: |
| 165 |
// Client::add_feature_used_event( 'my_plugin_settings_imported', 'Import Settings', [ 'source' => 'file' ] ); |
| 166 |
|
| 167 |
// Example: Track AHA milestone (multiple times, requires consent) |
| 168 |
// function test_telemetry_track_order_received($order_id, $amount) { |
| 169 |
// global $test_telemetry_client; |
| 170 |
// if ($test_telemetry_client instanceof Client) { |
| 171 |
// $test_telemetry_client->track_kui('order_received', ['order_id' => $order_id, 'amount' => $amount]); |
| 172 |
// // Emits: activation/aha_reached with indicator=order_received |
| 173 |
// } |
| 174 |
// } |
| 175 |
// add_action('woocommerce_new_order', 'test_telemetry_track_order_received', 10, 2); |
| 176 |
|
| 177 |
|
| 178 |
/** |
| 179 |
* Add admin menu for testing |
| 180 |
* |
| 181 |
* @since 1.0.0 |
| 182 |
*/ |
| 183 |
function test_telemetry_admin_menu() { |
| 184 |
add_menu_page( |
| 185 |
'Telemetry Test', |
| 186 |
'Telemetry Test', |
| 187 |
'manage_options', |
| 188 |
'test-telemetry', |
| 189 |
'test_telemetry_admin_page', |
| 190 |
'dashicons-chart-line', |
| 191 |
100 |
| 192 |
); |
| 193 |
} |
| 194 |
add_action('admin_menu', 'test_telemetry_admin_menu'); |
| 195 |
|
| 196 |
/** |
| 197 |
* Render admin test page |
| 198 |
* |
| 199 |
* @since 1.0.0 |
| 200 |
*/ |
| 201 |
function test_telemetry_admin_page() { |
| 202 |
if (!current_user_can('manage_options')) { |
| 203 |
return; |
| 204 |
} |
| 205 |
|
| 206 |
global $test_telemetry_client; |
| 207 |
|
| 208 |
// Handle test event submission |
| 209 |
if (isset($_POST['test_event']) && check_admin_referer('test_telemetry_event')) { |
| 210 |
error_log('=== TEST PLUGIN FORM HANDLER EXECUTING - ' . time() . ' ==='); |
| 211 |
|
| 212 |
// Remove WordPress magic quotes from entire POST array |
| 213 |
$_POST = array_map('stripslashes_deep', $_POST); |
| 214 |
|
| 215 |
$event_name = sanitize_text_field($_POST['event_name']); |
| 216 |
$event_data = isset($_POST['event_data']) ? trim($_POST['event_data']) : ''; |
| 217 |
|
| 218 |
error_log('Test Plugin [' . time() . '] - Raw event_data: ' . $event_data); |
| 219 |
|
| 220 |
$properties = []; |
| 221 |
if (!empty($event_data)) { |
| 222 |
// Try to decode JSON |
| 223 |
$decoded = json_decode($event_data, true); |
| 224 |
|
| 225 |
error_log('Test Plugin [' . time() . '] - JSON decode result: ' . print_r($decoded, true)); |
| 226 |
error_log('Test Plugin [' . time() . '] - JSON error: ' . json_last_error_msg()); |
| 227 |
|
| 228 |
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { |
| 229 |
// JSON is valid - use decoded array |
| 230 |
$properties = $decoded; |
| 231 |
error_log('Test Plugin [' . time() . '] - SUCCESS: Using decoded properties'); |
| 232 |
} else { |
| 233 |
// JSON is invalid - store as raw_data with error info |
| 234 |
$properties = [ |
| 235 |
'raw_data' => $event_data, |
| 236 |
'json_error' => json_last_error_msg() |
| 237 |
]; |
| 238 |
error_log('Test Plugin [' . time() . '] - FAILED: JSON decode failed, using raw_data'); |
| 239 |
} |
| 240 |
} |
| 241 |
|
| 242 |
if ($test_telemetry_client instanceof Client) { |
| 243 |
$test_telemetry_client->track($event_name, $properties); |
| 244 |
$message = 'Event added to queue! It will be sent during the next cron run.'; |
| 245 |
echo '<div class="notice notice-success"><p>' . esc_html($message) . '</p></div>'; |
| 246 |
} else { |
| 247 |
$message = 'Telemetry Client not initialized.'; |
| 248 |
echo '<div class="notice notice-error"><p>' . esc_html($message) . '</p></div>'; |
| 249 |
} |
| 250 |
} |
| 251 |
|
| 252 |
// Handle manual cron trigger |
| 253 |
if (isset($_POST['trigger_cron']) && check_admin_referer('test_telemetry_cron')) { |
| 254 |
global $test_telemetry_client; |
| 255 |
if ($test_telemetry_client instanceof Client) { |
| 256 |
$test_telemetry_client->process_queue(); |
| 257 |
echo '<div class="notice notice-success"><p>Telemetry queue processed manually!</p></div>'; |
| 258 |
} else { |
| 259 |
echo '<div class="notice notice-error"><p>Telemetry Client not initialized for cron processing.</p></div>'; |
| 260 |
} |
| 261 |
} |
| 262 |
|
| 263 |
?> |
| 264 |
<div class="wrap"> |
| 265 |
<h1>Telemetry SDK Test Page</h1> |
| 266 |
|
| 267 |
<p class="description" style="background: #fff; padding: 15px; border-left: 4px solid #00a0d2;"> |
| 268 |
<strong>Note:</strong> This test plugin demonstrates integration with the Linno Telemetry SDK. |
| 269 |
Consent notices and deactivation modals will appear as expected. Events are added to a queue and sent via WP-Cron. |
| 270 |
</p> |
| 271 |
|
| 272 |
<div class="card"> |
| 273 |
<h2>Test Custom Event</h2> |
| 274 |
<form method="post"> |
| 275 |
<?php wp_nonce_field('test_telemetry_event'); ?> |
| 276 |
<table class="form-table"> |
| 277 |
<tr> |
| 278 |
<th scope="row"><label for="event_name">Event Name</label></th> |
| 279 |
<td> |
| 280 |
<input type="text" id="event_name" name="event_name" value="test_custom_event" class="regular-text" required> |
| 281 |
<p class="description">Use alphanumeric characters and underscores only</p> |
| 282 |
</td> |
| 283 |
</tr> |
| 284 |
<tr> |
| 285 |
<th scope="row"><label for="event_data">Event Properties (JSON)</label></th> |
| 286 |
<td> |
| 287 |
<textarea id="event_data" name="event_data" rows="5" class="large-text" placeholder='{"test_key": "test_value", "user_action": "button_click"}'></textarea> |
| 288 |
<p class="description">Optional: Enter JSON object with custom properties.</p> |
| 289 |
</td> |
| 290 |
</tr> |
| 291 |
</table> |
| 292 |
<p class="submit"> |
| 293 |
<button type="submit" name="test_event" class="button button-primary">Send Test Event</button> |
| 294 |
</p> |
| 295 |
</form> |
| 296 |
</div> |
| 297 |
|
| 298 |
<div class="card"> |
| 299 |
<h2>Test Telemetry Queue Processing</h2> |
| 300 |
<p>Manually trigger the telemetry queue processing:</p> |
| 301 |
<form method="post"> |
| 302 |
<?php wp_nonce_field('test_telemetry_cron'); ?> |
| 303 |
<p class="submit"> |
| 304 |
<button type="submit" name="trigger_cron" class="button button-secondary">Process Telemetry Queue</button> |
| 305 |
</p> |
| 306 |
</form> |
| 307 |
<?php |
| 308 |
$next_scheduled_cron_hook = $test_telemetry_client ? $test_telemetry_client->get_slug() . '_telemetry_queue_process' : ''; |
| 309 |
$next_scheduled = $next_scheduled_cron_hook ? wp_next_scheduled($next_scheduled_cron_hook) : false; |
| 310 |
|
| 311 |
if ($next_scheduled) { |
| 312 |
echo '<p>Next scheduled queue processing: <strong>' . esc_html(date('Y-m-d H:i:s', $next_scheduled)) . '</strong></p>'; |
| 313 |
} else { |
| 314 |
echo '<p style="color: orange;">No cron job scheduled. This may be normal if consent is not granted or if the client is not initialized.</p>'; |
| 315 |
} |
| 316 |
?> |
| 317 |
</div> |
| 318 |
|
| 319 |
<div class="card"> |
| 320 |
<h2>System Information</h2> |
| 321 |
<table class="widefat"> |
| 322 |
<tbody> |
| 323 |
<tr> |
| 324 |
<td><strong>PHP Version:</strong></td> |
| 325 |
<td><?php echo esc_html(PHP_VERSION); ?></td> |
| 326 |
</tr> |
| 327 |
<tr> |
| 328 |
<td><strong>WordPress Version:</strong></td> |
| 329 |
<td><?php echo esc_html(get_bloginfo('version')); ?></td> |
| 330 |
</tr> |
| 331 |
<tr> |
| 332 |
<td><strong>MySQL Version:</strong></td> |
| 333 |
<td><?php global $wpdb; echo esc_html($wpdb->db_version()); ?></td> |
| 334 |
</tr> |
| 335 |
<tr> |
| 336 |
<td><strong>Server Software:</strong></td> |
| 337 |
<td><?php echo esc_html($_SERVER['SERVER_SOFTWARE'] ?? 'Unknown'); ?></td> |
| 338 |
</tr> |
| 339 |
<tr> |
| 340 |
<td><strong>Site URL:</strong></td> |
| 341 |
<td><?php echo esc_html(get_site_url()); ?></td> |
| 342 |
</tr> |
| 343 |
</tbody> |
| 344 |
</table> |
| 345 |
</div> |
| 346 |
|
| 347 |
<div class="card"> |
| 348 |
<h2>Testing Checklist</h2> |
| 349 |
<ul style="list-style: disc; margin-left: 20px;"> |
| 350 |
<li>✓ Activate plugin and verify consent notice appears.</li> |
| 351 |
<li>✓ Click "Allow" and verify `plugin_activated` event is queued.</li> |
| 352 |
<li>✓ Use form above to send custom test events and verify they are queued.</li> |
| 353 |
<li>✓ Trigger queue processing manually and verify events are dispatched.</li> |
| 354 |
<li>✓ Deactivate plugin and verify reason modal appears.</li> |
| 355 |
<li>✓ Submit deactivation reason and verify `plugin_deactivated` event is queued.</li> |
| 356 |
<li>✓ Reactivate, click "Do not allow" on consent notice and verify custom events are not queued.</li> |
| 357 |
<li>✓ Check browser console and PHP error logs for issues.</li> |
| 358 |
</ul> |
| 359 |
</div> |
| 360 |
|
| 361 |
<div class="card"> |
| 362 |
<h2>Debug Information</h2> |
| 363 |
<p><strong>Plugin File:</strong> <?php echo esc_html(__FILE__); ?></p> |
| 364 |
<p><strong>Plugin Folder:</strong> test-telemetry-plugin</p> |
| 365 |
<p><strong>Plugin Version:</strong> 1.0.0</p> |
| 366 |
<p><strong>SDK Loaded:</strong> <?php echo class_exists('LinnoSDK\Telemetry\Client') ? '✓ Yes' : '✗ No'; ?></p> |
| 367 |
<?php |
| 368 |
if ($test_telemetry_client instanceof Client): ?> |
| 369 |
<p style="color: green;"><strong>✓ Telemetry Client Initialized</strong></p> |
| 370 |
<p><strong>Plugin Slug:</strong> <?php echo esc_html($test_telemetry_client->get_slug()); ?></p> |
| 371 |
<p><strong>Text Domain:</strong> <?php echo esc_html($test_telemetry_client->get_text_domain()); ?></p> |
| 372 |
<p><strong>Opt-in Option Key:</strong> <code><?php echo esc_html($test_telemetry_client->get_optin_key()); ?></code></p> |
| 373 |
<p><strong>Opt-in Status:</strong> <?php |
| 374 |
$opt_in_key = $test_telemetry_client->get_optin_key(); |
| 375 |
$opt_in = get_option($opt_in_key, 'no'); |
| 376 |
echo $opt_in === 'yes' ? '<span style="color: green;">✓ Enabled</span>' : '<span style="color: red;">✗ Disabled</span>'; |
| 377 |
?></p> |
| 378 |
<p><strong>Next Queue Processing Cron:</strong> <code><?php echo esc_html($test_telemetry_client->get_cron_hook()); ?></code></p> |
| 379 |
<?php else: ?> |
| 380 |
<p style="color: red;"><strong>✗ Telemetry Client Not Initialized</strong></p> |
| 381 |
<?php endif; ?> |
| 382 |
</div> |
| 383 |
</div> |
| 384 |
|
| 385 |
<style> |
| 386 |
.card { |
| 387 |
background: #fff; |
| 388 |
border: 1px solid #ccd0d4; |
| 389 |
border-radius: 4px; |
| 390 |
padding: 20px; |
| 391 |
margin: 20px 0; |
| 392 |
box-shadow: 0 1px 1px rgba(0,0,0,.04); |
| 393 |
} |
| 394 |
.card h2 { |
| 395 |
margin-top: 0; |
| 396 |
border-bottom: 1px solid #eee; |
| 397 |
padding-bottom: 10px; |
| 398 |
} |
| 399 |
</style> |
| 400 |
<?php |
| 401 |
} |
| 402 |
|
| 403 |
/** |
| 404 |
* Customize the telemetry report interval for testing |
| 405 |
* |
| 406 |
* @param string $interval Default interval |
| 407 |
* @return string Modified interval |
| 408 |
* @since 1.0.0 |
| 409 |
*/ |
| 410 |
function test_telemetry_custom_interval($interval) { |
| 411 |
// Change to 'hourly' for faster testing, or keep 'weekly' for production |
| 412 |
return 'weekly'; |
| 413 |
} |
| 414 |
add_filter('test-telemetry-plugin_telemetry_report_interval', 'test_telemetry_custom_interval'); |
| 415 |
|
| 416 |
/** |
| 417 |
* Add custom system info for testing |
| 418 |
* |
| 419 |
* @param array $info System information array |
| 420 |
* @return array Modified system information |
| 421 |
* @since 1.0.0 |
| 422 |
*/ |
| 423 |
function test_telemetry_custom_system_info($info) { |
| 424 |
$info['test_plugin_active'] = true; |
| 425 |
$info['active_theme'] = wp_get_theme()->get('Name'); |
| 426 |
return $info; |
| 427 |
} |
| 428 |
add_filter($test_telemetry_client->get_slug() . '_telemetry_system_info', 'test_telemetry_custom_system_info'); |
| 429 |
|