fixed wp plugin

This commit is contained in:
Karl Breuer 2025-04-16 16:13:31 +02:00
parent 78ab65fccf
commit 3a7f791fa9
18 changed files with 921 additions and 693 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
.env
deploy.sh
/palanticshelper
/palanticshelper
*.zip

View File

@ -9,6 +9,16 @@ While direct integration between the frontend script and tracker server is possi
The WordPress plugin includes the helper functionality integrated into the PHP backend for seamless operation.
1. Copy the contents of the WordPress folder to your WordPress plugins directory
OR zip it and upload
```bash
cd wordpress
zip -r palantics-tracking-plugin.zip palantics-tracking-plugin/
```
2. Configure the tracker endpoint with your domain in the plugin settings
3. Track click events by either:
- Wrapping elements in the Analysis block

View File

@ -1,133 +0,0 @@
/**
* Tracking Block for Gutenberg with Popup Editor
*/
(function(blocks, element, blockEditor) {
var el = element.createElement;
var InnerBlocks = blockEditor.InnerBlocks;
var useBlockProps = blockEditor.useBlockProps;
var __ = wp.i18n.__;
var Button = wp.components.Button;
var Popover = wp.components.Popover;
var TextControl = wp.components.TextControl;
var useState = wp.element.useState;
blocks.registerBlockType('simplified-tracking/track-element', {
title: 'Tracking Wrapper',
icon: 'chart-bar',
category: 'design',
attributes: {
eventName: {
type: 'string',
default: 'tracked_element'
}
},
edit: function(props) {
var blockProps = useBlockProps();
var eventName = props.attributes.eventName;
var [isPopoverVisible, setPopoverVisible] = useState(false);
function onChangeEventName(newEventName) {
props.setAttributes({ eventName: newEventName });
}
return el(
'div',
blockProps,
el(
'div',
{ className: 'tracking-block-wrapper' },
el(
'div',
{ className: 'tracking-block-notice' },
'Event: ' + eventName,
el(
Button,
{
isSmall: true,
onClick: function() {
setPopoverVisible(!isPopoverVisible);
},
style: {
marginLeft: '8px',
background: 'white',
color: '#007cba'
}
},
'Edit'
),
isPopoverVisible && el(
Popover,
{
onClose: function() {
setPopoverVisible(false);
},
position: 'bottom',
focusOnMount: 'firstElement'
},
el(
'div',
{
style: {
padding: '16px',
width: '300px'
}
},
el(
'h3',
{
style: {
margin: '0 0 8px 0'
}
},
'Tracking Event Settings'
),
el(
TextControl,
{
label: 'Event Name',
value: eventName,
onChange: onChangeEventName,
help: 'Enter the event name to track when this element is clicked'
}
),
el(
Button,
{
isPrimary: true,
onClick: function() {
setPopoverVisible(false);
}
},
'Done'
)
)
)
),
el(
'div',
{ style: { marginTop: '5px' } },
el(InnerBlocks)
)
)
);
},
save: function(props) {
var blockProps = useBlockProps.save({
className: 'tracking-block-wrapper',
onClick: "tE('" + props.attributes.eventName + "')"
});
return el(
'div',
blockProps,
el(InnerBlocks.Content)
);
}
});
}(
window.wp.blocks,
window.wp.element,
window.wp.blockEditor
));

View File

@ -0,0 +1,40 @@
/**
* Palantics Admin Styles
*/
.palantics-heading {
color: #007cba;
font-weight: bold;
}
.palantics-methods-container {
background-color: #fff;
padding: 20px;
margin-top: 20px;
border: 1px solid #ccc;
border-radius: 4px;
}
.palantics-method {
margin-bottom: 20px;
}
.palantics-method-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
}
.palantics-code-sample {
background-color: #f5f5f5;
padding: 10px;
border-radius: 4px;
font-family: monospace;
margin-top: 5px;
}
.palantics-description {
margin-top: 5px;
font-size: 13px;
color: #666;
}

View File

@ -0,0 +1,2 @@
<?php
// silence is golden

View File

@ -1,13 +1,13 @@
/**
* Tracking Block Styles
* Palantics Tracking Block Styles
*/
.tracking-block-wrapper {
.palantics-tracking-block-wrapper {
border: 1px dashed #007cba;
padding: 10px;
position: relative;
}
.tracking-block-notice {
.palantics-tracking-block-notice {
background: #007cba;
color: white;
padding: 5px 10px;
@ -18,4 +18,9 @@
border-bottom-left-radius: 4px;
display: flex;
align-items: center;
}
}
/* Frontend styles */
/*
.palantics-tracking-wrapper {
} */

View File

@ -0,0 +1,2 @@
<?php
// silence is golden

View File

@ -0,0 +1,87 @@
/**
* Palantics Tracking Block
*/
const { registerBlockType } = wp.blocks;
const { InspectorControls } = wp.blockEditor;
const { PanelBody, TextControl } = wp.components;
const { Fragment } = wp.element;
const { __ } = wp.i18n;
// Register the block
registerBlockType('palantics-tracking/track-element', {
title: __('Tracking Element', 'palantics-tracking-plugin'),
icon: 'chart-line',
category: 'widgets',
keywords: [
__('track', 'palantics-tracking-plugin'),
__('analytics', 'palantics-tracking-plugin'),
__('palantics', 'palantics-tracking-plugin'),
],
attributes: {
eventName: {
type: 'string',
default: 'tracked_element',
},
content: {
type: 'string',
default: '',
},
},
edit: function(props) {
const { attributes, setAttributes } = props;
const { eventName, content } = attributes;
return (
<Fragment>
<InspectorControls>
<PanelBody title={__('Tracking Settings', 'palantics-tracking-plugin')}>
<TextControl
label={__('Event Name', 'palantics-tracking-plugin')}
value={eventName}
onChange={(value) => setAttributes({ eventName: value })}
help={__('Enter the event name that will be used for tracking this element', 'palantics-tracking-plugin')}
/>
</PanelBody>
</InspectorControls>
<div
className="palantics-tracking-block"
style={{
padding: '20px',
border: '1px dashed #ccc',
backgroundColor: '#f8f8f8'
}}
>
<div style={{ marginBottom: '10px', fontWeight: 'bold' }}>
{__('Palantics Tracking Element', 'palantics-tracking-plugin')}
</div>
<div style={{ fontSize: '0.9em', marginBottom: '10px' }}>
{__('Event:', 'palantics-tracking-plugin')} <code>{eventName}</code>
</div>
<div style={{ fontStyle: 'italic', fontSize: '0.8em' }}>
{__('This element will be tracked when clicked', 'palantics-tracking-plugin')}
</div>
</div>
</Fragment>
);
},
save: function(props) {
const { attributes } = props;
const { eventName } = attributes;
return (
<div
className="palantics-tracking-wrapper"
onClick={`tE('${eventName}')`}
>
<div className="palantics-tracking-content">
{props.children}
</div>
</div>
);
},
});

View File

@ -0,0 +1,87 @@
/**
* Palantics class-based tracking script
*/
document.addEventListener('DOMContentLoaded', function() {
// Helper function to get element details for tracking
function getElementDetails(element) {
const details = {};
// Get element text or alternative
if (element.textContent && element.textContent.trim()) {
details.text = element.textContent.trim();
} else if (element.placeholder) {
details.text = element.placeholder;
} else if (element.value) {
details.text = element.value;
} else if (element.alt) {
details.text = element.alt;
} else if (element.getAttribute('aria-label')) {
details.text = element.getAttribute('aria-label');
}
// Limit text length
if (details.text && details.text.length > 50) {
details.text = details.text.substring(0, 47) + '...';
}
// Get element ID, class and href if available
if (element.id) {
details.id = element.id;
}
if (element.className) {
details.class = element.className;
}
if (element.href) {
const href = element.href;
// Only include if it's not too long
if (href.length < 100) {
details.href = href;
}
}
return details;
}
// Function to initialize tracking for a specific class
function initializeClassTracking(className) {
const elements = document.querySelectorAll('.' + className);
elements.forEach(function(element) {
element.addEventListener('click', function(e) {
const details = getElementDetails(element);
tE('custom_element_click', {
element: 'custom',
class: className,
...details
});
});
});
}
// Track elements with data-track attribute
function initializeDataAttributeTracking() {
const trackedElements = document.querySelectorAll('[data-track]');
trackedElements.forEach(function(element) {
element.addEventListener('click', function(e) {
const eventName = element.getAttribute('data-track') || 'tracked_element';
const details = getElementDetails(element);
tE(eventName, {
element: 'data-track',
...details
});
});
});
}
// Initialize data attribute tracking
initializeDataAttributeTracking();
// If trackClasses is defined (will be injected by WordPress), initialize class tracking
if (typeof trackClasses !== 'undefined' && Array.isArray(trackClasses)) {
trackClasses.forEach(function(className) {
initializeClassTracking(className);
});
}
});

View File

@ -0,0 +1,2 @@
<?php
// silence is golden

View File

@ -0,0 +1,95 @@
/**
* Palantics main tracking script
*/
// Initialize tracking function
function tE(eventName, additionalParams = {}) {
// Create tracking data object
const data = {
event: eventName,
url: window.location.href,
ref: document.referrer,
ua: navigator.userAgent,
sw: window.screen.width,
lang: navigator.language || navigator.userLanguage,
};
// Add any additional parameters
Object.assign(data, additionalParams);
// Helper function to serialize an object into URL parameters
function serializeObject(obj, prefix) {
const str = [];
for (const p in obj) {
if (obj.hasOwnProperty(p)) {
const k = prefix ? prefix + "[" + p + "]" : p;
const v = obj[p];
if (v !== null && typeof v === "object") {
str.push(serializeObject(v, k));
} else {
str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
}
}
return str.join("&");
}
// Function for sending tracking data
function sendInfo() {
try {
// Get site URL and prepare REST API endpoint URL
const siteUrl = window.location.origin;
const restApiEndpoint = `${siteUrl}/wp-json/palantics/v1/sun`;
// Use POST request with data in body and query string
fetch(restApiEndpoint + '?' + serializeObject(data), {
method: "POST",
mode: "no-cors",
cache: "no-cache",
credentials: "omit", // Don't send cookies
headers: {
"X-Final-Destination": server, // Pass the final destination
"Content-Type": "application/json"
},
keepalive: true, // Ensures request completes even if page unloads
})
.catch(e => {
console.error("Tracking error:", e);
// If POST fails, try GET as fallback
fetch(restApiEndpoint + '?' + serializeObject(data), {
method: "GET",
mode: "no-cors",
cache: "no-cache",
credentials: "omit",
headers: {
"X-Final-Destination": server
},
keepalive: true,
}).catch(e2 => {
console.error("Fallback tracking error:", e2);
});
});
return true;
} catch (e) {
console.error("Tracking error:", e);
}
}
return sendInfo();
}
// Page load tracking function
function pL() {
tE("pageLoad_" + window.location.pathname);
}
// Set up page load tracking
document.addEventListener("DOMContentLoaded", function() {
try {
pL();
} catch (error) {
console.error("Error in page load tracking:", error);
}
});

View File

@ -0,0 +1,149 @@
<?php
/**
* Admin functionality class
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
class Palantics_Admin {
// Tracking server URL
private $tracking_server_url = 'tracking1.karlbreuer.com';
/**
* Constructor
*/
public function __construct() {
// Add settings page
add_action('admin_menu', array($this, 'add_settings_page'));
add_action('admin_init', array($this, 'register_settings'));
}
/**
* Add settings page
*/
public function add_settings_page() {
add_options_page(
'Palantics Tracking',
'Palantics Tracking',
'manage_options',
'palantics-tracking-settings',
array($this, 'render_settings_page')
);
}
/**
* Register settings
*/
public function register_settings() {
register_setting('palantics_tracking_settings', 'palantics_tracking_server_url');
register_setting('palantics_tracking_settings', 'palantics_tracking_track_classes');
add_settings_section(
'palantics_tracking_section',
'Tracking Settings',
array($this, 'settings_section_callback'),
'palantics-tracking-settings'
);
add_settings_field(
'palantics_tracking_server_url',
'Tracking Server URL',
array($this, 'server_url_callback'),
'palantics-tracking-settings',
'palantics_tracking_section'
);
add_settings_section(
'palantics_tracking_auto_section',
'Element Tracking Settings',
array($this, 'auto_settings_section_callback'),
'palantics-tracking-settings'
);
add_settings_field(
'palantics_tracking_track_classes',
'Track Elements with Classes',
array($this, 'track_classes_callback'),
'palantics-tracking-settings',
'palantics_tracking_auto_section'
);
}
/**
* Settings section callback
*/
public function settings_section_callback() {
echo '<p>Configure your Palantics tracking server settings.</p>';
}
/**
* Auto tracking settings section callback
*/
public function auto_settings_section_callback() {
echo '<p>Configure which elements to track based on CSS classes.</p>';
}
/**
* Server URL field callback
*/
public function server_url_callback() {
$value = get_option('palantics_tracking_server_url', $this->tracking_server_url);
echo '<input type="text" id="palantics_tracking_server_url" name="palantics_tracking_server_url" value="' . esc_attr($value) . '" class="regular-text">';
echo '<p class="description">Enter the tracking server URL without https:// (e.g., tracking1.karlbreuer.com)</p>';
echo '<p class="description">You will find this in your domain screen in the Palantics app</p>';
}
/**
* Track classes callback
*/
public function track_classes_callback() {
$value = get_option('palantics_tracking_track_classes', '');
echo '<input type="text" id="palantics_tracking_track_classes" name="palantics_tracking_track_classes" value="' . esc_attr($value) . '" class="regular-text">';
echo '<p class="description">Enter comma-separated class names to automatically track clicks on elements with these classes (e.g., cta-button, featured-product)</p>';
}
/**
* Render settings page
*/
public function render_settings_page() {
?>
<div class="wrap">
<h1><span style="color: #007cba;">Palantics</span> Tracking Settings</h1>
<form method="post" action="options.php">
<?php
settings_fields('palantics_tracking_settings');
do_settings_sections('palantics-tracking-settings');
submit_button();
?>
</form>
<div style="background-color: #fff; padding: 20px; margin-top: 20px; border: 1px solid #ccc; border-radius: 4px;">
<h2>Manual Tracking Methods</h2>
<div style="margin-bottom: 20px;">
<h3>Method 1: Using Data Attributes</h3>
<p>Add the <code>data-track</code> attribute to any HTML element to track clicks:</p>
<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px;">&lt;button data-track="signup_button_click"&gt;Sign Up&lt;/button&gt;</pre>
</div>
<div style="margin-bottom: 20px;">
<h3>Method 2: Using Shortcode</h3>
<p>Wrap any content with the shortcode to track clicks:</p>
<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px;">[track_element event="download_click" class="your-class" style="your-style"]
Your content here (can include other shortcodes)
[/track_element]</pre>
</div>
<div>
<h3>Method 3: Using Direct JavaScript</h3>
<p>You can also manually add <code>onclick="tE('your_event_name')"</code> to any HTML element.</p>
</div>
</div>
</div>
<?php
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* Asset loader class
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
class Palantics_Loader {
/**
* Constructor
*/
public function __construct() {
// Enqueue frontend scripts
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
// Enqueue admin scripts
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}
/**
* Enqueue frontend scripts
*/
public function enqueue_frontend_scripts() {
// Register main tracking script
wp_register_script(
'palantics-tracking-script',
PALANTICS_PLUGIN_URL . 'assets/js/tracking-script.js',
array(),
PALANTICS_PLUGIN_VERSION,
false
);
// Register class tracking script
wp_register_script(
'palantics-class-tracking',
PALANTICS_PLUGIN_URL . 'assets/js/class-tracking.js',
array('palantics-tracking-script'),
PALANTICS_PLUGIN_VERSION,
true
);
// Get tracking classes from options
$track_classes = get_option('palantics_tracking_track_classes', '');
$track_classes_array = array_filter(array_map('trim', explode(',', $track_classes)));
// Only enqueue class tracking if we have classes to track
if (!empty($track_classes_array)) {
// Add trackClasses as a JavaScript variable
wp_localize_script(
'palantics-class-tracking',
'trackClasses',
$track_classes_array
);
// Enqueue the script
wp_enqueue_script('palantics-class-tracking');
}
// Always enqueue the main tracking script
wp_enqueue_script('palantics-tracking-script');
// Add server URL to JavaScript
$server_url = esc_url(get_option('palantics_tracking_server_url', 'tracking1.karlbreuer.com'));
wp_add_inline_script(
'palantics-tracking-script',
"const server = 'https://" . $server_url . "';",
'before'
);
}
/**
* Enqueue admin scripts
*/
public function enqueue_admin_scripts($hook) {
// Only load on our settings page
if ($hook != 'settings_page_palantics-tracking-settings') {
return;
}
// Enqueue admin styles
wp_enqueue_style(
'palantics-admin-style',
PALANTICS_PLUGIN_URL . 'assets/css/admin.css',
array(),
PALANTICS_PLUGIN_VERSION
);
}
}

View File

@ -0,0 +1,261 @@
<?php
/**
* Tracking functionality class
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
class Palantics_Tracking {
// Tracking server URL
private $tracking_server_url = 'tracking1.karlbreuer.com';
/**
* Constructor
*/
public function __construct() {
// Add the script to the header
add_action('wp_head', array($this, 'add_tracking_script'), 10);
// Register REST API endpoint
add_action('rest_api_init', array($this, 'register_rest_endpoint'));
// Add focused tracking for elements with specific classes
add_action('wp_footer', array($this, 'add_class_tracking_script'), 99);
// Add shortcode for manual tracking
add_shortcode('track_element', array($this, 'tracking_shortcode'));
// Register custom block for Gutenberg
add_action('init', array($this, 'register_tracking_block'));
}
/**
* Add tracking script to header
*/
public function add_tracking_script() {
// This is now handled by the Loader class using wp_enqueue_scripts
// Fallback for backward compatibility - will call pL() function
?>
<!-- Page Load Tracking Initialization -->
<script>
document.addEventListener("DOMContentLoaded", function() {
if (typeof pL === 'function') {
try {
pL();
} catch (error) {
console.error("Error in page load tracking:", error);
}
}
});
</script>
<?php
}
/**
* Add JavaScript to track elements with specific classes
*/
public function add_class_tracking_script() {
// This is now handled by the Loader class using wp_enqueue_scripts
// The function is kept for backward compatibility but doesn't output anything
}
/**
* Register REST API endpoint for tracking
*/
public function register_rest_endpoint() {
register_rest_route('palantics/v1', '/sun', array(
'methods' => 'POST',
'callback' => array($this, 'handle_rest_request'),
'permission_callback' => '__return_true', // Allow unauthenticated access
));
// Also register GET method for easier testing and compatibility
register_rest_route('palantics/v1', '/sun', array(
'methods' => 'GET',
'callback' => array($this, 'handle_rest_request'),
'permission_callback' => '__return_true', // Allow unauthenticated access
));
}
/**
* Handle REST API request for tracking
*/
public function handle_rest_request($request) {
// Get all parameters from the request
$params = $request->get_params();
// Add IP address to the parameters
$params['ip'] = $this->get_real_ip();
// Forward tracking data to the tracking server
$this->forward_to_tracker($params, $request);
// Return an empty success response
return new WP_REST_Response(array('status' => 'success'), 200);
}
/**
* Shortcode for tracking elements
*/
public function tracking_shortcode($atts, $content = null) {
$atts = shortcode_atts(array(
'event' => 'tracked_element',
'class' => '',
'style' => '',
), $atts, 'track_element');
// Sanitize attributes
$event = esc_attr($atts['event']);
$class = esc_attr($atts['class']);
$style = esc_attr($atts['style']);
// Return the div with content and onclick attribute
return sprintf(
'<div class="palantics-tracking-wrapper %s" style="%s" onclick="tE(\'%s\')">%s</div>',
$class,
$style,
$event,
do_shortcode($content)
);
}
/**
* Register Gutenberg tracking block
*/
public function register_tracking_block() {
// Only register block if Gutenberg is available
if (!function_exists('register_block_type')) {
return;
}
// Register block script
wp_register_script(
'palantics-tracking-block-editor',
PALANTICS_PLUGIN_URL . 'assets/js/block.js',
array(
'wp-blocks',
'wp-element',
'wp-block-editor',
'wp-components',
'wp-i18n'
),
PALANTICS_PLUGIN_VERSION
);
// Register block styles
wp_register_style(
'palantics-tracking-block-editor-style',
PALANTICS_PLUGIN_URL . 'assets/css/tracking-block.css',
array(),
PALANTICS_PLUGIN_VERSION
);
// Register the block
register_block_type('palantics-tracking/track-element', array(
'editor_script' => 'palantics-tracking-block-editor',
'editor_style' => 'palantics-tracking-block-editor-style',
'attributes' => array(
'eventName' => array(
'type' => 'string',
'default' => 'tracked_element',
),
),
));
// Add inline script to ensure tE function is available in editor
wp_add_inline_script('palantics-tracking-block-editor', "
// Mock tE function for the editor if it doesn't exist
if (typeof window.tE === 'undefined') {
window.tE = function(eventName) {
console.log('Tracking event: ' + eventName);
};
}
");
}
/**
* Forward the request to the tracking server
*/
public function forward_to_tracker($params, $request) {
// Get tracking server URL from settings
$tracking_server_url = get_option('palantics_tracking_server_url', $this->tracking_server_url);
$forward_url = 'https://' . $tracking_server_url . '/spur';
// Create a new cURL resource
$ch = curl_init();
// Set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, $forward_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request->get_method());
// Build query string from parameters
$query_string = http_build_query($params);
curl_setopt($ch, CURLOPT_URL, $forward_url . '?' . $query_string);
// Prepare headers
$headers = array();
// Get all request headers
$request_headers = $request->get_headers();
// Check for final destination header
if (isset($request_headers['x_final_destination']) && !empty($request_headers['x_final_destination'][0])) {
// If a custom destination is provided in the header, use it
$final_destination = $request_headers['x_final_destination'][0];
$forward_url = 'https://' . $final_destination . '/spur';
curl_setopt($ch, CURLOPT_URL, $forward_url . '?' . $query_string);
// Add the header to the forwarded request
$headers[] = "X-Final-Destination: " . $final_destination;
}
// Forward all other headers
foreach ($request_headers as $name => $values) {
if ($name !== 'x_final_destination') {
foreach ($values as $value) {
$headers[] = sprintf("%s: %s", str_replace('_', '-', ucwords($name, '_')), $value);
}
}
}
// Set headers for cURL request
if (!empty($headers)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
// Execute request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
error_log('Palantics tracking error: ' . curl_error($ch));
$http_code = 500;
}
// Close cURL resource
curl_close($ch);
return $http_code;
}
/**
* Get the real IP address
*/
private function get_real_ip() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
}

View File

@ -0,0 +1 @@
<?php // silence is golden

View File

@ -0,0 +1 @@
<?php // silence is golden

View File

@ -0,0 +1,81 @@
<?php
/**
* Plugin Name: Palantics Tracking Plugin
* Description: Adds tracking script to website header and tracks user interactions using WordPress REST API
* Version: 1.3.0
* Author: Karl Breuer
* Text Domain: palantics-tracking-plugin
* Domain Path: /languages
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
// Define plugin constants
define('PALANTICS_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('PALANTICS_PLUGIN_URL', plugin_dir_url(__FILE__));
define('PALANTICS_PLUGIN_VERSION', '1.3.0');
// Include required files
require_once PALANTICS_PLUGIN_PATH . 'includes/class-tracking.php';
require_once PALANTICS_PLUGIN_PATH . 'includes/class-admin.php';
require_once PALANTICS_PLUGIN_PATH . 'includes/class-loader.php';
class Palantics_Tracking_Plugin {
// Plugin instance
private static $instance = null;
// Tracking instance
private $tracking;
// Admin instance
private $admin;
// Loader instance
private $loader;
/**
* Constructor
*/
private function __construct() {
// Initialize tracking
$this->tracking = new Palantics_Tracking();
// Initialize admin
$this->admin = new Palantics_Admin();
// Initialize loader
$this->loader = new Palantics_Loader();
// Add settings link on plugin page
add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'add_settings_link'));
}
/**
* Get plugin instance
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Add settings link to plugin page
*/
public function add_settings_link($links) {
$settings_link = '<a href="options-general.php?page=palantics-tracking-settings">Settings</a>';
array_unshift($links, $settings_link);
return $links;
}
}
// Initialize the plugin
function palantics_tracking_init() {
Palantics_Tracking_Plugin::get_instance();
}
add_action('plugins_loaded', 'palantics_tracking_init');

View File

@ -1,555 +0,0 @@
<?php
/**
* Plugin Name: Simplified Tracking Plugin
* Description: Adds tracking script to website header and tracks user interactions
* Version: 1.2.0
* Author: Karl Breuer
* Text Domain: simple-tracking
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
class Simplified_Tracking_Plugin {
// Plugin instance
private static $instance = null;
// Tracking server URL
private $tracking_server_url = 'tracking1.karlbreuer.com';
/**
* Constructor
*/
private function __construct() {
// Add the script to the header
add_action('wp_head', array($this, 'add_tracking_script'), 10);
// Register the endpoint
add_action('init', array($this, 'register_tracking_endpoint'));
// Add settings page
add_action('admin_menu', array($this, 'add_settings_page'));
add_action('admin_init', array($this, 'register_settings'));
// Add focused tracking for elements with specific classes
add_action('wp_footer', array($this, 'add_class_tracking_script'), 99);
// Add shortcode for manual tracking
add_shortcode('track_element', array($this, 'tracking_shortcode'));
// Register custom block for Gutenberg
add_action('init', array($this, 'register_tracking_block'));
}
/**
* Get plugin instance
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Add tracking script to header
*/
public function add_tracking_script() {
$server_url = esc_url(get_option('simple_tracking_server_url', $this->tracking_server_url));
?>
<!-- Global server config -->
<script> const server = "https://<?php echo $server_url; ?>";</script>
<!-- Tracking function -->
<script>
// Initialize tracking function
function tE(eventName, additionalParams = {}) {
// Create tracking data object
const data = {
event: eventName,
url: window.location.href,
ref: document.referrer,
ua: navigator.userAgent,
sw: window.screen.width,
lang: navigator.language || navigator.userLanguage,
};
// Add any additional parameters
Object.assign(data, additionalParams);
// Helper function to serialize an object into URL parameters
function serializeObject(obj, prefix) {
const str = [];
for (const p in obj) {
if (obj.hasOwnProperty(p)) {
const k = prefix ? prefix + "[" + p + "]" : p;
const v = obj[p];
if (v !== null && typeof v === "object") {
str.push(serializeObject(v, k));
} else {
str.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
}
}
return str.join("&");
}
// Function for sending tracking data
function sendInfo() {
try {
// Send to our WordPress endpoint first, which will forward to the tracking server
const siteUrl = window.location.origin;
const trackingUrl = `${siteUrl}/track?${serializeObject(data)}`;
fetch(trackingUrl, {
method: "POST",
mode: "no-cors",
cache: "no-cache",
credentials: "omit", // Don't send cookies
headers: {
"X-Final-Destination": server // Pass the final destination
},
keepalive: true, // Ensures request completes even if page unloads
});
return true;
} catch (e) {
console.error("Tracking error:", e);
}
}
return sendInfo();
}
function pL() {
tE("pageLoad_" + window.location.pathname);
}
</script>
<!-- Set up page load tracking -->
<script>
document.addEventListener("DOMContentLoaded", function() {
try {
pL();
} catch (error) {
console.error("Error in page load tracking:", error);
}
});
</script>
<?php
}
/**
* Add JavaScript to track elements with specific classes
*/
public function add_class_tracking_script() {
// Get custom elements to track
$track_classes = get_option('simple_tracking_track_classes', '');
$track_classes_array = array_filter(array_map('trim', explode(',', $track_classes)));
if (empty($track_classes_array)) {
return;
}
?>
<!-- Simplified class-based tracking script -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Helper function to get element details for tracking
function getElementDetails(element) {
const details = {};
// Get element text or alternative
if (element.textContent && element.textContent.trim()) {
details.text = element.textContent.trim();
} else if (element.placeholder) {
details.text = element.placeholder;
} else if (element.value) {
details.text = element.value;
} else if (element.alt) {
details.text = element.alt;
} else if (element.getAttribute('aria-label')) {
details.text = element.getAttribute('aria-label');
}
// Limit text length
if (details.text && details.text.length > 50) {
details.text = details.text.substring(0, 47) + '...';
}
// Get element ID, class and href if available
if (element.id) {
details.id = element.id;
}
if (element.className) {
details.class = element.className;
}
if (element.href) {
const href = element.href;
// Only include if it's not too long
if (href.length < 100) {
details.href = href;
}
}
return details;
}
<?php foreach ($track_classes_array as $class) : ?>
// Track elements with class: <?php echo esc_js($class); ?>
const <?php echo str_replace('-', '_', sanitize_key($class)); ?>Elements = document.querySelectorAll('.<?php echo esc_js($class); ?>');
<?php echo str_replace('-', '_', sanitize_key($class)); ?>Elements.forEach(function(element) {
element.addEventListener('click', function(e) {
const details = getElementDetails(element);
tE('custom_element_click', { element: 'custom', class: '<?php echo esc_js($class); ?>', ...details });
});
});
<?php endforeach; ?>
// Track elements with data-track attribute
const trackedElements = document.querySelectorAll('[data-track]');
trackedElements.forEach(function(element) {
element.addEventListener('click', function(e) {
const eventName = element.getAttribute('data-track') || 'tracked_element';
const details = getElementDetails(element);
tE(eventName, { element: 'data-track', ...details });
});
});
});
</script>
<?php
}
/**
* Shortcode for tracking elements
*/
public function tracking_shortcode($atts, $content = null) {
$atts = shortcode_atts(array(
'event' => 'tracked_element',
'class' => '',
'style' => '',
), $atts, 'track_element');
// Sanitize attributes
$event = esc_attr($atts['event']);
$class = esc_attr($atts['class']);
$style = esc_attr($atts['style']);
// Return the div with content and onclick attribute
return sprintf(
'<div class="tracking-wrapper %s" style="%s" onclick="tE(\'%s\')">%s</div>',
$class,
$style,
$event,
do_shortcode($content)
);
}
/**
* Register Gutenberg tracking block
*/
public function register_tracking_block() {
// Only register block if Gutenberg is available
if (!function_exists('register_block_type')) {
return;
}
// Register block script
wp_register_script(
'tracking-block-editor',
plugins_url('block.js', __FILE__),
array(
'wp-blocks',
'wp-element',
'wp-block-editor',
'wp-components',
'wp-i18n'
),
filemtime(plugin_dir_path(__FILE__) . 'block.js')
);
// Register block styles
wp_register_style(
'tracking-block-editor-style',
plugins_url('block.css', __FILE__),
array(),
filemtime(plugin_dir_path(__FILE__) . 'tracking-block.css')
);
// Register the block
register_block_type('simplified-tracking/track-element', array(
'editor_script' => 'tracking-block-editor',
'editor_style' => 'tracking-block-editor-style',
'attributes' => array(
'eventName' => array(
'type' => 'string',
'default' => 'tracked_element',
),
),
));
// Add inline script to ensure tE function is available in editor
wp_add_inline_script('tracking-block-editor', "
// Mock tE function for the editor if it doesn't exist
if (typeof window.tE === 'undefined') {
window.tE = function(eventName) {
console.log('Tracking event: ' + eventName);
};
}
");
}
/**
* Register tracking endpoint
*/
public function register_tracking_endpoint() {
add_rewrite_rule('^track/?$', 'index.php?tracking_endpoint=1', 'top');
add_rewrite_tag('%tracking_endpoint%', '1');
add_action('parse_request', array($this, 'handle_tracking_request'));
// Flush rewrite rules only on plugin activation
if (get_option('simple_tracking_flush_rules', false)) {
flush_rewrite_rules();
update_option('simple_tracking_flush_rules', false);
}
}
/**
* Handle tracking request
*/
public function handle_tracking_request($wp) {
if (isset($wp->query_vars['tracking_endpoint'])) {
$this->forward_to_tracker();
exit;
}
}
/**
* Forward the request to the tracking server
*/
public function forward_to_tracker() {
// Get tracking server URL from settings
$tracking_server_url = get_option('simple_tracking_server_url', $this->tracking_server_url);
$forward_url = 'https://' . $tracking_server_url . '/spur';
// Create a new cURL resource
$ch = curl_init();
// Set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, $forward_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']);
// Get query parameters and add IP
$query = $_GET;
$query['ip'] = $this->get_real_ip();
// Build query string
$query_string = http_build_query($query);
curl_setopt($ch, CURLOPT_URL, $forward_url . '?' . $query_string);
// Prepare headers
$headers = array();
$all_headers = function_exists('getallheaders') ? getallheaders() : $this->get_all_headers();
// Forward all headers, checking for the final destination
foreach ($all_headers as $name => $value) {
if ($name == 'X-Final-Destination' && !empty($value)) {
// If a custom destination is provided in the header, use it
$forward_url = 'https://' . $value . '/spur';
curl_setopt($ch, CURLOPT_URL, $forward_url . '?' . $query_string);
}
$headers[] = "$name: $value";
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// Execute request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
$http_code = 500;
}
// Close cURL resource
curl_close($ch);
// Send response
http_response_code($http_code);
header('Content-Type: application/json');
echo '{}';
exit;
}
/**
* Fallback implementation of getallheaders() for servers that don't have it
*/
private function get_all_headers() {
$headers = [];
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
/**
* Get the real IP address
*/
private function get_real_ip() {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
/**
* Add settings page
*/
public function add_settings_page() {
add_options_page(
'Tracking Settings',
'Tracking Settings',
'manage_options',
'simple-tracking-settings',
array($this, 'render_settings_page')
);
}
/**
* Register settings
*/
public function register_settings() {
register_setting('simple_tracking_settings', 'simple_tracking_server_url');
register_setting('simple_tracking_settings', 'simple_tracking_track_classes');
add_settings_section(
'simple_tracking_section',
'Tracking Settings',
array($this, 'settings_section_callback'),
'simple-tracking-settings'
);
add_settings_field(
'simple_tracking_server_url',
'Tracking Server URL',
array($this, 'server_url_callback'),
'simple-tracking-settings',
'simple_tracking_section'
);
add_settings_section(
'simple_tracking_auto_section',
'Element Tracking Settings',
array($this, 'auto_settings_section_callback'),
'simple-tracking-settings'
);
add_settings_field(
'simple_tracking_track_classes',
'Track Elements with Classes',
array($this, 'track_classes_callback'),
'simple-tracking-settings',
'simple_tracking_auto_section'
);
}
/**
* Settings section callback
*/
public function settings_section_callback() {
echo '<p>Configure your tracking server settings.</p>';
}
/**
* Auto tracking settings section callback
*/
public function auto_settings_section_callback() {
echo '<p>Configure which elements to track based on CSS classes.</p>';
}
/**
* Server URL field callback
*/
public function server_url_callback() {
$value = get_option('simple_tracking_server_url', $this->tracking_server_url);
echo '<input type="text" id="simple_tracking_server_url" name="simple_tracking_server_url" value="' . esc_attr($value) . '" class="regular-text">';
echo '<p class="description">Enter the tracking server URL without https:// (e.g., tracking1.karlbreuer.com)</p>';
echo '<p class="description">You will find this in your domain screen in the Palantics app</p>';
}
/**
* Track classes callback
*/
public function track_classes_callback() {
$value = get_option('simple_tracking_track_classes', '');
echo '<input type="text" id="simple_tracking_track_classes" name="simple_tracking_track_classes" value="' . esc_attr($value) . '" class="regular-text">';
echo '<p class="description">Enter comma-separated class names to automatically track clicks on elements with these classes (e.g., cta-button, featured-product)</p>';
}
/**
* Render settings page
*/
public function render_settings_page() {
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields('simple_tracking_settings');
do_settings_sections('simple-tracking-settings');
submit_button();
?>
</form>
<div style="background-color: #fff; padding: 20px; margin-top: 20px; border: 1px solid #ccc; border-radius: 4px;">
<h2>Manual Tracking Methods</h2>
<div style="margin-bottom: 20px;">
<h3>Method 1: Using Data Attributes</h3>
<p>Add the <code>data-track</code> attribute to any HTML element to track clicks:</p>
<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px;">&lt;button data-track="signup_button_click"&gt;Sign Up&lt;/button&gt;</pre>
</div>
<div style="margin-bottom: 20px;">
<h3>Method 2: Using Shortcode</h3>
<p>Wrap any content with the shortcode to track clicks:</p>
<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px;">[track_element event="download_click" class="your-class" style="your-style"]
Your content here (can include other shortcodes)
[/track_element]</pre>
</div>
<div>
<h3>Method 3: Using Direct JavaScript</h3>
<p>You can also manually add <code>onclick="tE('your_event_name')"</code> to any HTML element.</p>
</div>
</div>
</div>
<?php
}
/**
* Activate plugin
*/
public static function activate() {
// Set flag to flush rewrite rules on next load
update_option('simple_tracking_flush_rules', true);
}
}
// Initialize the plugin
function simplified_tracking_init() {
Simplified_Tracking_Plugin::get_instance();
}
add_action('plugins_loaded', 'simplified_tracking_init');
// Register activation hook
register_activation_hook(__FILE__, array('Simplified_Tracking_Plugin', 'activate'));