fixed wp plugin

This commit is contained in:
2025-04-16 16:13:31 +02:00
parent 78ab65fccf
commit 3a7f791fa9
18 changed files with 921 additions and 693 deletions
@@ -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;
}
@@ -0,0 +1,2 @@
<?php
// silence is golden
@@ -0,0 +1,26 @@
/**
* Palantics Tracking Block Styles
*/
.palantics-tracking-block-wrapper {
border: 1px dashed #007cba;
padding: 10px;
position: relative;
}
.palantics-tracking-block-notice {
background: #007cba;
color: white;
padding: 5px 10px;
font-size: 12px;
position: absolute;
top: 0;
right: 0;
border-bottom-left-radius: 4px;
display: flex;
align-items: center;
}
/* Frontend styles */
/*
.palantics-tracking-wrapper {
} */
@@ -0,0 +1,2 @@
<?php
// silence is golden
@@ -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>
);
},
});
@@ -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);
});
}
});
@@ -0,0 +1,2 @@
<?php
// silence is golden
@@ -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);
}
});
@@ -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
}
}
@@ -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
);
}
}
@@ -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;
}
}
@@ -0,0 +1 @@
<?php // silence is golden
@@ -0,0 +1 @@
<?php // silence is golden
@@ -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');