diff --git a/admin/class-wptag-admin-controller.php b/admin/class-wptag-admin-controller.php
new file mode 100644
index 0000000..db89a6f
--- /dev/null
+++ b/admin/class-wptag-admin-controller.php
@@ -0,0 +1,301 @@
+snippet_manager = $snippet_manager;
+ $this->template_manager = $template_manager;
+
+ add_action('admin_menu', [$this, 'add_admin_menu']);
+ add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
+ add_action('admin_init', [$this, 'handle_form_submissions']);
+ add_action('admin_notices', [$this, 'display_admin_notices']);
+ }
+
+ public function add_admin_menu() {
+ $capability = 'manage_options';
+
+ $this->page_hook_suffix['main'] = add_menu_page(
+ __('WPTAG', 'wptag'),
+ __('WPTAG', 'wptag'),
+ $capability,
+ 'wptag',
+ [$this, 'render_dashboard_page'],
+ 'dashicons-code-standards',
+ 85
+ );
+
+ $this->page_hook_suffix['dashboard'] = add_submenu_page(
+ 'wptag',
+ __('Dashboard', 'wptag'),
+ __('Dashboard', 'wptag'),
+ $capability,
+ 'wptag',
+ [$this, 'render_dashboard_page']
+ );
+
+ $this->page_hook_suffix['snippets'] = add_submenu_page(
+ 'wptag',
+ __('Code Snippets', 'wptag'),
+ __('Code Snippets', 'wptag'),
+ $capability,
+ 'wptag-snippets',
+ [$this, 'render_snippets_page']
+ );
+
+ $this->page_hook_suffix['templates'] = add_submenu_page(
+ 'wptag',
+ __('Service Templates', 'wptag'),
+ __('Service Templates', 'wptag'),
+ $capability,
+ 'wptag-templates',
+ [$this, 'render_templates_page']
+ );
+
+ $this->page_hook_suffix['settings'] = add_submenu_page(
+ 'wptag',
+ __('Settings', 'wptag'),
+ __('Settings', 'wptag'),
+ $capability,
+ 'wptag-settings',
+ [$this, 'render_settings_page']
+ );
+ }
+
+ public function enqueue_admin_assets($hook) {
+ if (!in_array($hook, $this->page_hook_suffix)) {
+ return;
+ }
+
+ wp_enqueue_style(
+ 'wptag-admin',
+ WPTAG_PLUGIN_URL . 'assets/css/admin.css',
+ ['wp-components'],
+ WPTAG_VERSION
+ );
+
+ wp_enqueue_script(
+ 'wptag-admin',
+ WPTAG_PLUGIN_URL . 'assets/js/admin.js',
+ ['jquery', 'wp-api', 'wp-i18n', 'wp-components', 'wp-element'],
+ WPTAG_VERSION,
+ true
+ );
+
+ wp_localize_script('wptag-admin', 'wptagAdmin', [
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('wptag_admin'),
+ 'strings' => [
+ 'confirmDelete' => __('Are you sure you want to delete this snippet?', 'wptag'),
+ 'saved' => __('Settings saved successfully.', 'wptag'),
+ 'error' => __('An error occurred. Please try again.', 'wptag')
+ ]
+ ]);
+
+ if (isset($_GET['action']) && ($_GET['action'] === 'new' || $_GET['action'] === 'edit')) {
+ wp_enqueue_code_editor(['type' => 'text/html']);
+ }
+ }
+
+ public function render_dashboard_page() {
+ require_once WPTAG_PLUGIN_DIR . 'admin/partials/dashboard.php';
+ }
+
+ public function render_snippets_page() {
+ require_once WPTAG_PLUGIN_DIR . 'admin/partials/snippets.php';
+ }
+
+ public function render_templates_page() {
+ require_once WPTAG_PLUGIN_DIR . 'admin/partials/templates.php';
+ }
+
+ public function render_settings_page() {
+ require_once WPTAG_PLUGIN_DIR . 'admin/partials/settings.php';
+ }
+
+ public function handle_form_submissions() {
+ if (!isset($_POST['wptag_action'])) {
+ return;
+ }
+
+ $action = sanitize_key($_POST['wptag_action']);
+
+ if (!wp_verify_nonce($_POST['_wpnonce'], 'wptag_' . $action)) {
+ wp_die(__('Security check failed', 'wptag'));
+ }
+
+ if (!current_user_can('manage_options')) {
+ wp_die(__('Permission denied', 'wptag'));
+ }
+
+ switch ($action) {
+ case 'create_snippet':
+ $this->handle_create_snippet();
+ break;
+
+ case 'update_snippet':
+ $this->handle_update_snippet();
+ break;
+
+ case 'delete_snippet':
+ $this->handle_delete_snippet();
+ break;
+
+ case 'toggle_snippet':
+ $this->handle_toggle_snippet();
+ break;
+
+ case 'save_settings':
+ $this->handle_save_settings();
+ break;
+ }
+ }
+
+ private function handle_create_snippet() {
+ $data = [
+ 'name' => $_POST['name'] ?? '',
+ 'description' => $_POST['description'] ?? '',
+ 'code' => $_POST['code'] ?? '',
+ 'code_type' => $_POST['code_type'] ?? 'html',
+ 'position' => $_POST['position'] ?? 'head',
+ 'category' => $_POST['category'] ?? 'custom',
+ 'priority' => $_POST['priority'] ?? 10,
+ 'status' => isset($_POST['status']) ? 1 : 0,
+ 'device_type' => $_POST['device_type'] ?? 'all',
+ 'load_method' => $_POST['load_method'] ?? 'normal',
+ 'conditions' => $this->parse_conditions($_POST['conditions'] ?? [])
+ ];
+
+ $result = $this->snippet_manager->create_snippet($data);
+
+ if (is_wp_error($result)) {
+ $this->add_admin_notice($result->get_error_message(), 'error');
+ } else {
+ $this->add_admin_notice(__('Snippet created successfully', 'wptag'), 'success');
+ wp_redirect(admin_url('admin.php?page=wptag-snippets'));
+ exit;
+ }
+ }
+
+ private function handle_update_snippet() {
+ $id = intval($_POST['snippet_id'] ?? 0);
+
+ if (!$id) {
+ $this->add_admin_notice(__('Invalid snippet ID', 'wptag'), 'error');
+ return;
+ }
+
+ $data = [
+ 'name' => $_POST['name'] ?? '',
+ 'description' => $_POST['description'] ?? '',
+ 'code' => $_POST['code'] ?? '',
+ 'code_type' => $_POST['code_type'] ?? 'html',
+ 'position' => $_POST['position'] ?? 'head',
+ 'category' => $_POST['category'] ?? 'custom',
+ 'priority' => $_POST['priority'] ?? 10,
+ 'status' => isset($_POST['status']) ? 1 : 0,
+ 'device_type' => $_POST['device_type'] ?? 'all',
+ 'load_method' => $_POST['load_method'] ?? 'normal',
+ 'conditions' => $this->parse_conditions($_POST['conditions'] ?? [])
+ ];
+
+ $result = $this->snippet_manager->update_snippet($id, $data);
+
+ if (is_wp_error($result)) {
+ $this->add_admin_notice($result->get_error_message(), 'error');
+ } else {
+ $this->add_admin_notice(__('Snippet updated successfully', 'wptag'), 'success');
+ }
+ }
+
+ private function handle_delete_snippet() {
+ $id = intval($_GET['snippet_id'] ?? 0);
+
+ if (!$id) {
+ $this->add_admin_notice(__('Invalid snippet ID', 'wptag'), 'error');
+ return;
+ }
+
+ $result = $this->snippet_manager->delete_snippet($id);
+
+ if (is_wp_error($result)) {
+ $this->add_admin_notice($result->get_error_message(), 'error');
+ } else {
+ $this->add_admin_notice(__('Snippet deleted successfully', 'wptag'), 'success');
+ }
+
+ wp_redirect(admin_url('admin.php?page=wptag-snippets'));
+ exit;
+ }
+
+ private function handle_toggle_snippet() {
+ $id = intval($_GET['snippet_id'] ?? 0);
+
+ if (!$id) {
+ wp_die(json_encode(['success' => false, 'message' => 'Invalid ID']));
+ }
+
+ $result = $this->snippet_manager->toggle_status($id);
+
+ if (is_wp_error($result)) {
+ wp_die(json_encode(['success' => false, 'message' => $result->get_error_message()]));
+ }
+
+ wp_die(json_encode(['success' => true, 'status' => $result]));
+ }
+
+ private function handle_save_settings() {
+ $settings = [
+ 'enable_cache' => isset($_POST['enable_cache']) ? 1 : 0,
+ 'cache_ttl' => intval($_POST['cache_ttl'] ?? 3600),
+ 'enable_debug' => isset($_POST['enable_debug']) ? 1 : 0,
+ 'cleanup_on_uninstall' => isset($_POST['cleanup_on_uninstall']) ? 1 : 0
+ ];
+
+ update_option('wptag_settings', $settings);
+
+ $this->add_admin_notice(__('Settings saved successfully', 'wptag'), 'success');
+
+ wp_redirect(admin_url('admin.php?page=wptag-settings'));
+ exit;
+ }
+
+ private function parse_conditions($conditions_data) {
+ if (empty($conditions_data) || !is_array($conditions_data)) {
+ return null;
+ }
+
+ return $conditions_data;
+ }
+
+ private function add_admin_notice($message, $type = 'info') {
+ set_transient('wptag_admin_notice', [
+ 'message' => $message,
+ 'type' => $type
+ ], 30);
+ }
+
+ public function display_admin_notices() {
+ $notice = get_transient('wptag_admin_notice');
+
+ if (!$notice) {
+ return;
+ }
+
+ delete_transient('wptag_admin_notice');
+
+ $class = 'notice notice-' . esc_attr($notice['type']);
+ printf(
+ '
',
+ $class,
+ esc_html($notice['message'])
+ );
+ }
+}
diff --git a/admin/class-wptag-admin-interface.php b/admin/class-wptag-admin-interface.php
new file mode 100644
index 0000000..e447fa2
--- /dev/null
+++ b/admin/class-wptag-admin-interface.php
@@ -0,0 +1,195 @@
+' . esc_html($label);
+ if ($required) {
+ echo ' *';
+ }
+ echo '';
+ }
+
+ switch ($type) {
+ case 'text':
+ case 'email':
+ case 'url':
+ case 'number':
+ self::render_input_field($type, $name, $value, $required);
+ break;
+
+ case 'textarea':
+ self::render_textarea_field($name, $value, $required);
+ break;
+
+ case 'select':
+ self::render_select_field($name, $value, $options, $required);
+ break;
+
+ case 'checkbox':
+ self::render_checkbox_field($name, $value, $label);
+ break;
+
+ case 'radio':
+ self::render_radio_field($name, $value, $options);
+ break;
+ }
+
+ if ($description) {
+ echo '' . esc_html($description) . '
';
+ }
+ }
+
+ private static function render_input_field($type, $name, $value, $required) {
+ printf(
+ '',
+ esc_attr($type),
+ esc_attr($name),
+ esc_attr($name),
+ esc_attr($value),
+ $required ? 'required' : ''
+ );
+ }
+
+ private static function render_textarea_field($name, $value, $required) {
+ printf(
+ '',
+ esc_attr($name),
+ esc_attr($name),
+ $required ? 'required' : '',
+ esc_textarea($value)
+ );
+ }
+
+ private static function render_select_field($name, $value, $options, $required) {
+ printf(
+ '';
+ }
+
+ private static function render_checkbox_field($name, $value, $label) {
+ printf(
+ '',
+ esc_attr($name),
+ esc_attr($name),
+ checked($value, 1, false),
+ esc_html($label)
+ );
+ }
+
+ private static function render_radio_field($name, $value, $options) {
+ foreach ($options as $option_value => $option_label) {
+ printf(
+ '
',
+ esc_attr($name),
+ esc_attr($option_value),
+ checked($value, $option_value, false),
+ esc_html($option_label)
+ );
+ }
+ }
+
+ public static function render_modal($id, $title, $content, $footer = '') {
+ ?>
+
+ 'notice-info',
+ 'success' => 'notice-success',
+ 'warning' => 'notice-warning',
+ 'error' => 'notice-error'
+ ];
+
+ $class = $classes[$type] ?? 'notice-info';
+
+ printf(
+ '',
+ esc_attr($class),
+ esc_html($message)
+ );
+ }
+
+ public static function render_tabs($tabs, $current_tab) {
+ echo '';
+
+ foreach ($tabs as $tab_key => $tab_label) {
+ $class = ($tab_key === $current_tab) ? ' nav-tab-active' : '';
+ printf(
+ '%s',
+ esc_attr($_GET['page']),
+ esc_attr($tab_key),
+ esc_attr($class),
+ esc_html($tab_label)
+ );
+ }
+
+ echo '
';
+ }
+
+ public static function render_action_buttons($actions) {
+ echo '';
+ }
+}
diff --git a/admin/class-wptag-ajax-handler.php b/admin/class-wptag-ajax-handler.php
new file mode 100644
index 0000000..8d3ddfe
--- /dev/null
+++ b/admin/class-wptag-ajax-handler.php
@@ -0,0 +1,322 @@
+snippet_manager = $snippet_manager;
+ $this->template_manager = $template_manager;
+
+ $this->register_ajax_handlers();
+ }
+
+ private function register_ajax_handlers() {
+ $actions = [
+ 'wptag_toggle_snippet',
+ 'wptag_delete_snippet',
+ 'wptag_search_snippets',
+ 'wptag_validate_code',
+ 'wptag_preview_snippet',
+ 'wptag_get_template',
+ 'wptag_process_template',
+ 'wptag_export_snippets',
+ 'wptag_import_snippets',
+ 'wptag_clear_cache'
+ ];
+
+ foreach ($actions as $action) {
+ add_action('wp_ajax_' . $action, [$this, 'handle_' . str_replace('wptag_', '', $action)]);
+ }
+ }
+
+ public function handle_toggle_snippet() {
+ $this->verify_ajax_request();
+
+ $snippet_id = intval($_POST['snippet_id'] ?? 0);
+
+ if (!$snippet_id) {
+ wp_send_json_error(['message' => __('Invalid snippet ID', 'wptag')]);
+ }
+
+ $result = $this->snippet_manager->toggle_status($snippet_id);
+
+ if (is_wp_error($result)) {
+ wp_send_json_error(['message' => $result->get_error_message()]);
+ }
+
+ wp_send_json_success([
+ 'status' => $result,
+ 'message' => $result ? __('Snippet enabled', 'wptag') : __('Snippet disabled', 'wptag')
+ ]);
+ }
+
+ public function handle_delete_snippet() {
+ $this->verify_ajax_request();
+
+ $snippet_id = intval($_POST['snippet_id'] ?? 0);
+
+ if (!$snippet_id) {
+ wp_send_json_error(['message' => __('Invalid snippet ID', 'wptag')]);
+ }
+
+ $result = $this->snippet_manager->delete_snippet($snippet_id);
+
+ if (is_wp_error($result)) {
+ wp_send_json_error(['message' => $result->get_error_message()]);
+ }
+
+ wp_send_json_success(['message' => __('Snippet deleted successfully', 'wptag')]);
+ }
+
+ public function handle_search_snippets() {
+ $this->verify_ajax_request();
+
+ $args = [
+ 'search' => sanitize_text_field($_POST['search'] ?? ''),
+ 'category' => sanitize_key($_POST['category'] ?? ''),
+ 'position' => sanitize_key($_POST['position'] ?? ''),
+ 'status' => isset($_POST['status']) ? intval($_POST['status']) : null,
+ 'per_page' => intval($_POST['per_page'] ?? 20),
+ 'page' => intval($_POST['page'] ?? 1)
+ ];
+
+ $snippets = $this->snippet_manager->get_snippets($args);
+
+ wp_send_json_success(['snippets' => $snippets]);
+ }
+
+ public function handle_validate_code() {
+ $this->verify_ajax_request();
+
+ $code = $_POST['code'] ?? '';
+ $code_type = sanitize_key($_POST['code_type'] ?? 'html');
+
+ $errors = [];
+
+ switch ($code_type) {
+ case 'javascript':
+ $errors = $this->validate_javascript($code);
+ break;
+
+ case 'css':
+ $errors = $this->validate_css($code);
+ break;
+
+ case 'html':
+ $errors = $this->validate_html($code);
+ break;
+ }
+
+ if (empty($errors)) {
+ wp_send_json_success(['message' => __('Code is valid', 'wptag')]);
+ } else {
+ wp_send_json_error(['errors' => $errors]);
+ }
+ }
+
+ public function handle_preview_snippet() {
+ $this->verify_ajax_request();
+
+ $snippet_id = intval($_POST['snippet_id'] ?? 0);
+
+ if (!$snippet_id) {
+ wp_send_json_error(['message' => __('Invalid snippet ID', 'wptag')]);
+ }
+
+ $snippet = $this->snippet_manager->get_snippet($snippet_id);
+
+ if (!$snippet) {
+ wp_send_json_error(['message' => __('Snippet not found', 'wptag')]);
+ }
+
+ $preview_url = add_query_arg([
+ 'wptag_preview' => 1,
+ 'snippet_id' => $snippet_id
+ ], home_url());
+
+ wp_send_json_success(['preview_url' => $preview_url]);
+ }
+
+ public function handle_get_template() {
+ $this->verify_ajax_request();
+
+ $service_type = sanitize_key($_POST['service_type'] ?? '');
+
+ if (!$service_type) {
+ wp_send_json_error(['message' => __('Invalid service type', 'wptag')]);
+ }
+
+ $template = $this->template_manager->get_template($service_type);
+
+ if (!$template) {
+ wp_send_json_error(['message' => __('Template not found', 'wptag')]);
+ }
+
+ wp_send_json_success(['template' => $template]);
+ }
+
+ public function handle_process_template() {
+ $this->verify_ajax_request();
+
+ $service_type = sanitize_key($_POST['service_type'] ?? '');
+ $config_data = $_POST['config'] ?? [];
+
+ if (!$service_type) {
+ wp_send_json_error(['message' => __('Invalid service type', 'wptag')]);
+ }
+
+ $result = $this->template_manager->process_template_config($service_type, $config_data);
+
+ if (is_wp_error($result)) {
+ wp_send_json_error(['message' => $result->get_error_message()]);
+ }
+
+ $snippet_data = array_merge($result, [
+ 'description' => sprintf(__('Generated from %s template', 'wptag'), $result['name']),
+ 'status' => 1
+ ]);
+
+ $snippet_id = $this->snippet_manager->create_snippet($snippet_data);
+
+ if (is_wp_error($snippet_id)) {
+ wp_send_json_error(['message' => $snippet_id->get_error_message()]);
+ }
+
+ wp_send_json_success([
+ 'snippet_id' => $snippet_id,
+ 'message' => __('Snippet created successfully from template', 'wptag')
+ ]);
+ }
+
+ public function handle_export_snippets() {
+ $this->verify_ajax_request();
+
+ $snippet_ids = array_map('intval', $_POST['snippet_ids'] ?? []);
+
+ $export_data = [
+ 'version' => WPTAG_VERSION,
+ 'exported_at' => current_time('mysql'),
+ 'snippets' => []
+ ];
+
+ foreach ($snippet_ids as $id) {
+ $snippet = $this->snippet_manager->get_snippet($id);
+ if ($snippet) {
+ unset($snippet['id'], $snippet['created_by'], $snippet['last_modified_by']);
+ $export_data['snippets'][] = $snippet;
+ }
+ }
+
+ wp_send_json_success([
+ 'filename' => 'wptag-export-' . date('Y-m-d') . '.json',
+ 'data' => json_encode($export_data, JSON_PRETTY_PRINT)
+ ]);
+ }
+
+ public function handle_import_snippets() {
+ $this->verify_ajax_request();
+
+ $import_data = json_decode(stripslashes($_POST['import_data'] ?? ''), true);
+
+ if (!$import_data || !isset($import_data['snippets'])) {
+ wp_send_json_error(['message' => __('Invalid import data', 'wptag')]);
+ }
+
+ $imported = 0;
+ $errors = [];
+
+ foreach ($import_data['snippets'] as $snippet) {
+ $result = $this->snippet_manager->create_snippet($snippet);
+
+ if (is_wp_error($result)) {
+ $errors[] = sprintf(
+ __('Failed to import "%s": %s', 'wptag'),
+ $snippet['name'],
+ $result->get_error_message()
+ );
+ } else {
+ $imported++;
+ }
+ }
+
+ if ($imported > 0) {
+ $message = sprintf(
+ _n('%d snippet imported successfully', '%d snippets imported successfully', $imported, 'wptag'),
+ $imported
+ );
+
+ if (!empty($errors)) {
+ $message .= ' ' . __('Some snippets failed to import.', 'wptag');
+ }
+
+ wp_send_json_success([
+ 'message' => $message,
+ 'imported' => $imported,
+ 'errors' => $errors
+ ]);
+ } else {
+ wp_send_json_error([
+ 'message' => __('No snippets were imported', 'wptag'),
+ 'errors' => $errors
+ ]);
+ }
+ }
+
+ public function handle_clear_cache() {
+ $this->verify_ajax_request();
+
+ $cache_manager = new WPTag_Cache_Manager();
+ $cache_manager->flush();
+
+ wp_send_json_success(['message' => __('Cache cleared successfully', 'wptag')]);
+ }
+
+ private function verify_ajax_request() {
+ if (!check_ajax_referer('wptag_admin', 'nonce', false)) {
+ wp_send_json_error(['message' => __('Security check failed', 'wptag')]);
+ }
+
+ if (!current_user_can('manage_options')) {
+ wp_send_json_error(['message' => __('Permission denied', 'wptag')]);
+ }
+ }
+
+ private function validate_javascript($code) {
+ $errors = [];
+
+ if (preg_match('/\bdocument\.write\b/i', $code)) {
+ $errors[] = __('document.write is not recommended', 'wptag');
+ }
+
+ if (preg_match('/\beval\s*\(/i', $code)) {
+ $errors[] = __('eval() is potentially dangerous', 'wptag');
+ }
+
+ return $errors;
+ }
+
+ private function validate_css($code) {
+ $errors = [];
+
+ if (preg_match('/@import\s+url/i', $code)) {
+ $errors[] = __('@import may affect performance', 'wptag');
+ }
+
+ return $errors;
+ }
+
+ private function validate_html($code) {
+ $errors = [];
+
+ if (preg_match('/
+
+
diff --git a/admin/partials/snippet-form.php b/admin/partials/snippet-form.php
new file mode 100644
index 0000000..9990564
--- /dev/null
+++ b/admin/partials/snippet-form.php
@@ -0,0 +1,207 @@
+snippet_manager->get_snippet($snippet_id);
+ if (!$snippet) {
+ wp_die(__('Snippet not found', 'wptag'));
+ }
+ $is_edit = true;
+}
+
+$categories = $this->snippet_manager->get_categories();
+$positions = $this->snippet_manager->get_positions();
+?>
+
+
diff --git a/admin/partials/snippets.php b/admin/partials/snippets.php
new file mode 100644
index 0000000..5c5ec9f
--- /dev/null
+++ b/admin/partials/snippets.php
@@ -0,0 +1,149 @@
+ sanitize_text_field($_GET['search'] ?? ''),
+ 'category' => sanitize_key($_GET['filter_category'] ?? ''),
+ 'position' => sanitize_key($_GET['filter_position'] ?? ''),
+ 'status' => isset($_GET['filter_status']) ? intval($_GET['filter_status']) : null,
+ 'per_page' => $per_page,
+ 'page' => $current_page
+];
+
+$snippets = $this->snippet_manager->get_snippets($args);
+$categories = $this->snippet_manager->get_categories();
+$positions = $this->snippet_manager->get_positions();
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
diff --git a/admin/partials/templates.php b/admin/partials/templates.php
new file mode 100644
index 0000000..a6861b8
--- /dev/null
+++ b/admin/partials/templates.php
@@ -0,0 +1,119 @@
+template_manager->get_templates($selected_category);
+$categories = $this->template_manager->get_categories();
+
+$templates_by_category = [];
+foreach ($templates as $template) {
+ $cat = $template['service_category'];
+ if (!isset($templates_by_category[$cat])) {
+ $templates_by_category[$cat] = [];
+ }
+ $templates_by_category[$cat][] = $template;
+}
+?>
+
+
+
+
+
+
+
+
+
+
+
+ $label) : ?>
+
+
+
+
+
+
+
+
+
+ $cat_templates) : ?>
+
+
+
+
+
+
+
+
+
+
+
+ v
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __('Track website traffic and user behavior with Google Analytics 4', 'wptag'),
+ 'google_analytics_universal' => __('Track website traffic with Universal Analytics (legacy)', 'wptag'),
+ 'facebook_pixel' => __('Track conversions and build audiences for Facebook ads', 'wptag'),
+ 'google_ads' => __('Track conversions for Google Ads campaigns', 'wptag'),
+ 'google_search_console' => __('Verify site ownership for Google Search Console', 'wptag'),
+ 'baidu_tongji' => __('Track website traffic with Baidu Analytics', 'wptag'),
+ 'cnzz' => __('Track website traffic with CNZZ Analytics', 'wptag'),
+ '51la' => __('Track website traffic with 51.la Analytics', 'wptag'),
+ 'baidu_push' => __('Submit URLs to Baidu for faster indexing', 'wptag'),
+ 'toutiao_pixel' => __('Track conversions for Toutiao/TikTok ads', 'wptag'),
+ ];
+
+ return $descriptions[$service_type] ?? __('Configure and add this service to your site', 'wptag');
+}
+?>
diff --git a/assets/admin.css b/assets/admin.css
new file mode 100644
index 0000000..2b5d52e
--- /dev/null
+++ b/assets/admin.css
@@ -0,0 +1,374 @@
+.wptag-admin-wrap {
+ margin-top: 20px;
+}
+
+.wptag-header {
+ background: #fff;
+ border: 1px solid #ccd0d4;
+ border-bottom: 0;
+ padding: 20px;
+ margin-bottom: 0;
+}
+
+.wptag-header h1 {
+ margin: 0;
+ font-size: 24px;
+ font-weight: 400;
+ line-height: 1.3;
+}
+
+.wptag-header .page-title-action {
+ margin-left: 10px;
+}
+
+.wptag-content {
+ background: #fff;
+ border: 1px solid #ccd0d4;
+ padding: 20px;
+}
+
+.wptag-stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.wptag-stat-card {
+ background: #f8f9fa;
+ border: 1px solid #e2e4e7;
+ border-radius: 4px;
+ padding: 20px;
+ text-align: center;
+}
+
+.wptag-stat-card h3 {
+ margin: 0 0 10px;
+ font-size: 14px;
+ color: #666;
+ font-weight: 400;
+}
+
+.wptag-stat-card .stat-value {
+ font-size: 32px;
+ font-weight: 600;
+ color: #2271b1;
+}
+
+.wptag-table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+
+.wptag-table th,
+.wptag-table td {
+ padding: 12px;
+ text-align: left;
+ border-bottom: 1px solid #e2e4e7;
+}
+
+.wptag-table th {
+ background: #f8f9fa;
+ font-weight: 600;
+ color: #2c3338;
+}
+
+.wptag-table tbody tr:hover {
+ background: #f6f7f7;
+}
+
+.wptag-status-badge {
+ display: inline-block;
+ padding: 3px 8px;
+ border-radius: 3px;
+ font-size: 12px;
+ font-weight: 500;
+}
+
+.wptag-status-badge.active {
+ background: #d4f4dd;
+ color: #00a32a;
+}
+
+.wptag-status-badge.inactive {
+ background: #f5e6e6;
+ color: #d63638;
+}
+
+.wptag-actions {
+ display: flex;
+ gap: 10px;
+}
+
+.wptag-action-link {
+ color: #2271b1;
+ text-decoration: none;
+ font-size: 13px;
+}
+
+.wptag-action-link:hover {
+ color: #135e96;
+ text-decoration: underline;
+}
+
+.wptag-action-link.delete {
+ color: #d63638;
+}
+
+.wptag-action-link.delete:hover {
+ color: #a02222;
+}
+
+.wptag-form-table {
+ width: 100%;
+ max-width: 800px;
+}
+
+.wptag-form-table th {
+ width: 200px;
+ padding: 20px 10px 20px 0;
+ vertical-align: top;
+ text-align: left;
+ font-weight: 600;
+}
+
+.wptag-form-table td {
+ padding: 15px 10px;
+}
+
+.wptag-form-table input[type="text"],
+.wptag-form-table input[type="number"],
+.wptag-form-table select,
+.wptag-form-table textarea {
+ width: 100%;
+ max-width: 400px;
+}
+
+.wptag-code-editor {
+ width: 100%;
+ min-height: 300px;
+ font-family: Consolas, Monaco, monospace;
+ font-size: 13px;
+}
+
+.wptag-conditions-builder {
+ background: #f8f9fa;
+ border: 1px solid #e2e4e7;
+ border-radius: 4px;
+ padding: 20px;
+ margin-top: 10px;
+}
+
+.wptag-condition-group {
+ background: #fff;
+ border: 1px solid #e2e4e7;
+ border-radius: 4px;
+ padding: 15px;
+ margin-bottom: 15px;
+}
+
+.wptag-condition-row {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.wptag-condition-row select,
+.wptag-condition-row input {
+ flex: 1;
+ min-width: 0;
+}
+
+.wptag-templates-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 20px;
+ margin-top: 20px;
+}
+
+.wptag-template-card {
+ background: #fff;
+ border: 1px solid #e2e4e7;
+ border-radius: 4px;
+ padding: 20px;
+ transition: box-shadow 0.2s;
+}
+
+.wptag-template-card:hover {
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.wptag-template-card h3 {
+ margin: 0 0 10px;
+ font-size: 16px;
+ color: #2c3338;
+}
+
+.wptag-template-card p {
+ color: #666;
+ margin: 0 0 15px;
+ font-size: 14px;
+}
+
+.wptag-template-card .button {
+ width: 100%;
+ text-align: center;
+}
+
+.wptag-modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.7);
+ z-index: 100000;
+}
+
+.wptag-modal-content {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: #fff;
+ border-radius: 4px;
+ max-width: 600px;
+ width: 90%;
+ max-height: 90vh;
+ overflow: auto;
+}
+
+.wptag-modal-header {
+ padding: 20px;
+ border-bottom: 1px solid #e2e4e7;
+}
+
+.wptag-modal-header h2 {
+ margin: 0;
+ font-size: 20px;
+}
+
+.wptag-modal-body {
+ padding: 20px;
+}
+
+.wptag-modal-footer {
+ padding: 20px;
+ border-top: 1px solid #e2e4e7;
+ text-align: right;
+}
+
+.wptag-filters {
+ display: flex;
+ gap: 15px;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+}
+
+.wptag-filter-item {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+}
+
+.wptag-filter-item label {
+ font-size: 13px;
+ font-weight: 600;
+ color: #2c3338;
+}
+
+.wptag-empty-state {
+ text-align: center;
+ padding: 60px 20px;
+ color: #666;
+}
+
+.wptag-empty-state svg {
+ width: 64px;
+ height: 64px;
+ margin-bottom: 20px;
+ opacity: 0.3;
+}
+
+.wptag-empty-state h3 {
+ font-size: 18px;
+ font-weight: 400;
+ margin: 0 0 10px;
+}
+
+.wptag-empty-state p {
+ margin: 0 0 20px;
+}
+
+.wptag-tabs {
+ display: flex;
+ border-bottom: 1px solid #e2e4e7;
+ margin: -20px -20px 20px;
+ padding: 0 20px;
+}
+
+.wptag-tab {
+ padding: 15px 20px;
+ border-bottom: 2px solid transparent;
+ text-decoration: none;
+ color: #2c3338;
+ font-weight: 500;
+ transition: all 0.2s;
+}
+
+.wptag-tab:hover {
+ color: #2271b1;
+}
+
+.wptag-tab.active {
+ color: #2271b1;
+ border-bottom-color: #2271b1;
+}
+
+.wptag-notice {
+ padding: 12px 20px;
+ margin: 20px 0;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.wptag-notice.info {
+ background: #e5f5fa;
+ color: #0073aa;
+}
+
+.wptag-notice.success {
+ background: #d4f4dd;
+ color: #00a32a;
+}
+
+.wptag-notice.warning {
+ background: #fcf9e8;
+ color: #996800;
+}
+
+.wptag-notice.error {
+ background: #f5e6e6;
+ color: #d63638;
+}
+
+@media screen and (max-width: 782px) {
+ .wptag-form-table th {
+ width: auto;
+ display: block;
+ padding-bottom: 5px;
+ }
+
+ .wptag-filters {
+ flex-direction: column;
+ }
+
+ .wptag-condition-row {
+ flex-direction: column;
+ }
+}
diff --git a/assets/admin.js b/assets/admin.js
new file mode 100644
index 0000000..d3ff23e
--- /dev/null
+++ b/assets/admin.js
@@ -0,0 +1,322 @@
+(function($) {
+ 'use strict';
+
+ const WPTagAdmin = {
+ init: function() {
+ this.bindEvents();
+ this.initCodeEditor();
+ this.initConditionsBuilder();
+ },
+
+ bindEvents: function() {
+ $(document).on('click', '.wptag-toggle-status', this.toggleStatus);
+ $(document).on('click', '.wptag-delete-snippet', this.deleteSnippet);
+ $(document).on('change', '.wptag-filter', this.filterSnippets);
+ $(document).on('click', '.wptag-template-use', this.useTemplate);
+ $(document).on('submit', '.wptag-snippet-form', this.validateForm);
+ $(document).on('click', '.wptag-add-condition', this.addCondition);
+ $(document).on('click', '.wptag-remove-condition', this.removeCondition);
+ $(document).on('change', '#code_type', this.updateCodeEditor);
+ },
+
+ toggleStatus: function(e) {
+ e.preventDefault();
+
+ const $link = $(this);
+ const snippetId = $link.data('snippet-id');
+
+ $.ajax({
+ url: wptagAdmin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'wptag_toggle_snippet',
+ snippet_id: snippetId,
+ nonce: wptagAdmin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ location.reload();
+ } else {
+ alert(response.data.message || wptagAdmin.strings.error);
+ }
+ },
+ error: function() {
+ alert(wptagAdmin.strings.error);
+ }
+ });
+ },
+
+ deleteSnippet: function(e) {
+ e.preventDefault();
+
+ if (!confirm(wptagAdmin.strings.confirmDelete)) {
+ return;
+ }
+
+ const $link = $(this);
+ const snippetId = $link.data('snippet-id');
+
+ $.ajax({
+ url: wptagAdmin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'wptag_delete_snippet',
+ snippet_id: snippetId,
+ nonce: wptagAdmin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ $link.closest('tr').fadeOut(400, function() {
+ $(this).remove();
+ });
+ } else {
+ alert(response.data.message || wptagAdmin.strings.error);
+ }
+ },
+ error: function() {
+ alert(wptagAdmin.strings.error);
+ }
+ });
+ },
+
+ filterSnippets: function() {
+ const filters = {
+ search: $('#filter-search').val(),
+ category: $('#filter-category').val(),
+ position: $('#filter-position').val(),
+ status: $('#filter-status').val()
+ };
+
+ const params = new URLSearchParams(filters);
+ params.delete('page');
+
+ Object.keys(filters).forEach(key => {
+ if (!filters[key]) {
+ params.delete(key);
+ }
+ });
+
+ window.location.href = window.location.pathname + '?page=wptag-snippets&' + params.toString();
+ },
+
+ initCodeEditor: function() {
+ const $codeTextarea = $('#snippet-code');
+
+ if ($codeTextarea.length && typeof wp !== 'undefined' && wp.codeEditor) {
+ const editorSettings = wp.codeEditor.defaultSettings || {};
+ const codeType = $('#code_type').val();
+
+ editorSettings.codemirror = {
+ ...editorSettings.codemirror,
+ mode: this.getCodeMirrorMode(codeType),
+ lineNumbers: true,
+ lineWrapping: true,
+ indentUnit: 2,
+ tabSize: 2
+ };
+
+ this.codeEditor = wp.codeEditor.initialize($codeTextarea, editorSettings);
+ }
+ },
+
+ getCodeMirrorMode: function(codeType) {
+ const modes = {
+ 'html': 'htmlmixed',
+ 'javascript': 'javascript',
+ 'css': 'css'
+ };
+
+ return modes[codeType] || 'htmlmixed';
+ },
+
+ updateCodeEditor: function() {
+ const codeType = $(this).val();
+
+ if (WPTagAdmin.codeEditor && WPTagAdmin.codeEditor.codemirror) {
+ WPTagAdmin.codeEditor.codemirror.setOption('mode', WPTagAdmin.getCodeMirrorMode(codeType));
+ }
+ },
+
+ initConditionsBuilder: function() {
+ const $builder = $('.wptag-conditions-builder');
+
+ if (!$builder.length) {
+ return;
+ }
+
+ this.loadConditionTypes();
+ },
+
+ loadConditionTypes: function() {
+ $.ajax({
+ url: wptagAdmin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'wptag_get_condition_types',
+ nonce: wptagAdmin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ WPTagAdmin.conditionTypes = response.data.types;
+ }
+ }
+ });
+ },
+
+ addCondition: function(e) {
+ e.preventDefault();
+
+ const $group = $(this).closest('.wptag-condition-group');
+ const $newRow = WPTagAdmin.createConditionRow();
+
+ $group.find('.wptag-conditions-list').append($newRow);
+ },
+
+ removeCondition: function(e) {
+ e.preventDefault();
+
+ $(this).closest('.wptag-condition-row').fadeOut(300, function() {
+ $(this).remove();
+ });
+ },
+
+ createConditionRow: function() {
+ const html = `
+
+
+
+
+
+
+ `;
+
+ return $(html);
+ },
+
+ useTemplate: function(e) {
+ e.preventDefault();
+
+ const $button = $(this);
+ const serviceType = $button.data('service-type');
+
+ $.ajax({
+ url: wptagAdmin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'wptag_get_template',
+ service_type: serviceType,
+ nonce: wptagAdmin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ WPTagAdmin.showTemplateModal(response.data.template);
+ } else {
+ alert(response.data.message || wptagAdmin.strings.error);
+ }
+ }
+ });
+ },
+
+ showTemplateModal: function(template) {
+ const fields = template.config_fields.map(field => {
+ return `
+
+
+
+
+ `;
+ }).join('');
+
+ const modalHtml = `
+
+
+
+
+
+
+ `;
+
+ $('body').append(modalHtml);
+ $('#template-config-modal').fadeIn();
+
+ $('#template-config-form').on('submit', function(e) {
+ e.preventDefault();
+ WPTagAdmin.processTemplate(template.service_type, $(this).serialize());
+ });
+ },
+
+ processTemplate: function(serviceType, formData) {
+ $.ajax({
+ url: wptagAdmin.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'wptag_process_template',
+ service_type: serviceType,
+ config: formData,
+ nonce: wptagAdmin.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ window.location.href = `admin.php?page=wptag-snippets&action=edit&snippet_id=${response.data.snippet_id}`;
+ } else {
+ alert(response.data.message || wptagAdmin.strings.error);
+ }
+ }
+ });
+ },
+
+ closeModal: function() {
+ $('.wptag-modal').fadeOut(300, function() {
+ $(this).remove();
+ });
+ },
+
+ validateForm: function(e) {
+ const $form = $(this);
+ const name = $form.find('#snippet-name').val();
+ const code = WPTagAdmin.codeEditor ?
+ WPTagAdmin.codeEditor.codemirror.getValue() :
+ $form.find('#snippet-code').val();
+
+ if (!name.trim()) {
+ e.preventDefault();
+ alert('Please enter a snippet name');
+ return false;
+ }
+
+ if (!code.trim()) {
+ e.preventDefault();
+ alert('Please enter some code');
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ $(document).ready(function() {
+ WPTagAdmin.init();
+ });
+
+})(jQuery);
diff --git a/includes/class-wptag-cache-manager.php b/includes/class-wptag-cache-manager.php
new file mode 100644
index 0000000..c90d5ff
--- /dev/null
+++ b/includes/class-wptag-cache-manager.php
@@ -0,0 +1,160 @@
+cache_enabled = !defined('WPTAG_DISABLE_CACHE') || !WPTAG_DISABLE_CACHE;
+ $this->ttl = defined('WPTAG_CACHE_TTL') ? WPTAG_CACHE_TTL : 3600;
+ }
+
+ public function get($key) {
+ if (!$this->cache_enabled) {
+ return false;
+ }
+
+ return wp_cache_get($key, $this->cache_group);
+ }
+
+ public function set($key, $value, $ttl = null) {
+ if (!$this->cache_enabled) {
+ return false;
+ }
+
+ $ttl = $ttl ?? $this->ttl;
+ return wp_cache_set($key, $value, $this->cache_group, $ttl);
+ }
+
+ public function delete($key) {
+ return wp_cache_delete($key, $this->cache_group);
+ }
+
+ public function flush() {
+ wp_cache_flush();
+ }
+
+ public function clear_snippet_cache($snippet_id = null) {
+ if ($snippet_id) {
+ $this->delete('snippet_' . $snippet_id);
+ }
+
+ $this->delete('active_snippets');
+ $this->clear_output_cache();
+ }
+
+ public function clear_output_cache() {
+ $positions = ['head', 'footer', 'before_content', 'after_content'];
+
+ foreach ($positions as $position) {
+ $this->delete_by_prefix('output_' . $position);
+ }
+ }
+
+ public function clear_condition_cache() {
+ $this->delete_by_prefix('condition_');
+ }
+
+ private function delete_by_prefix($prefix) {
+ global $wp_object_cache;
+
+ if (method_exists($wp_object_cache, 'delete_by_group')) {
+ $wp_object_cache->delete_by_group($this->cache_group);
+ } else {
+ $this->delete($prefix);
+ }
+ }
+
+ public function warm_cache() {
+ if (!$this->cache_enabled) {
+ return;
+ }
+
+ global $wpdb;
+ $table = $wpdb->prefix . 'wptag_snippets';
+
+ $active_snippets = $wpdb->get_results(
+ "SELECT * FROM $table WHERE status = 1 ORDER BY priority ASC",
+ ARRAY_A
+ );
+
+ $by_position = [];
+ foreach ($active_snippets as $snippet) {
+ $position = $snippet['position'];
+ if (!isset($by_position[$position])) {
+ $by_position[$position] = [];
+ }
+ $by_position[$position][] = $snippet;
+ }
+
+ foreach ($by_position as $position => $snippets) {
+ $this->set('snippets_' . $position, $snippets);
+ }
+
+ $this->set('active_snippets', $active_snippets);
+ }
+
+ public function get_cache_stats() {
+ global $wp_object_cache;
+
+ $stats = [
+ 'enabled' => $this->cache_enabled,
+ 'ttl' => $this->ttl,
+ 'hits' => 0,
+ 'misses' => 0,
+ 'size' => 0
+ ];
+
+ if (method_exists($wp_object_cache, 'stats')) {
+ $cache_stats = $wp_object_cache->stats();
+ $stats['hits'] = $cache_stats['hits'] ?? 0;
+ $stats['misses'] = $cache_stats['misses'] ?? 0;
+ }
+
+ return $stats;
+ }
+
+ public function schedule_cleanup() {
+ if (!wp_next_scheduled('wptag_cache_cleanup')) {
+ wp_schedule_event(time(), 'daily', 'wptag_cache_cleanup');
+ }
+
+ add_action('wptag_cache_cleanup', [$this, 'cleanup_expired']);
+ }
+
+ public function cleanup_expired() {
+ global $wpdb;
+ $logs_table = $wpdb->prefix . 'wptag_logs';
+
+ $thirty_days_ago = date('Y-m-d H:i:s', strtotime('-30 days'));
+
+ $wpdb->query($wpdb->prepare(
+ "DELETE FROM $logs_table WHERE created_at < %s",
+ $thirty_days_ago
+ ));
+
+ $this->clear_output_cache();
+ }
+
+ public function invalidate_on_save($post_id) {
+ if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
+ return;
+ }
+
+ $this->clear_output_cache();
+ }
+
+ public function init_hooks() {
+ add_action('save_post', [$this, 'invalidate_on_save']);
+ add_action('switch_theme', [$this, 'flush']);
+ add_action('activated_plugin', [$this, 'flush']);
+ add_action('deactivated_plugin', [$this, 'flush']);
+
+ $this->schedule_cleanup();
+ }
+}
diff --git a/includes/class-wptag-condition-engine.php b/includes/class-wptag-condition-engine.php
new file mode 100644
index 0000000..1d91720
--- /dev/null
+++ b/includes/class-wptag-condition-engine.php
@@ -0,0 +1,311 @@
+context_cache[$cache_key])) {
+ return $this->context_cache[$cache_key];
+ }
+
+ $result = $this->process_condition_group($conditions, $context);
+ $this->context_cache[$cache_key] = $result;
+
+ return $result;
+ }
+
+ private function process_condition_group($conditions, $context) {
+ $logic = $conditions['logic'] ?? 'AND';
+ $rules = $conditions['rules'] ?? [];
+
+ if (empty($rules)) {
+ return true;
+ }
+
+ $results = [];
+ foreach ($rules as $rule) {
+ if (isset($rule['rules'])) {
+ $results[] = $this->process_condition_group($rule, $context);
+ } else {
+ $results[] = $this->evaluate_single_condition($rule, $context);
+ }
+ }
+
+ if ($logic === 'OR') {
+ return in_array(true, $results, true);
+ } else {
+ return !in_array(false, $results, true);
+ }
+ }
+
+ private function evaluate_single_condition($rule, $context) {
+ $type = $rule['type'] ?? '';
+ $operator = $rule['operator'] ?? 'equals';
+ $value = $rule['value'] ?? '';
+
+ switch ($type) {
+ case 'page_type':
+ return $this->check_page_type($value, $operator);
+
+ case 'user_status':
+ return $this->check_user_status($value, $operator);
+
+ case 'user_role':
+ return $this->check_user_role($value, $operator);
+
+ case 'device_type':
+ return $this->check_device_type($value, $operator);
+
+ case 'post_id':
+ return $this->check_post_id($value, $operator);
+
+ case 'category':
+ return $this->check_category($value, $operator);
+
+ case 'tag':
+ return $this->check_tag($value, $operator);
+
+ case 'url':
+ return $this->check_url($value, $operator);
+
+ case 'date_range':
+ return $this->check_date_range($value, $operator);
+
+ case 'time':
+ return $this->check_time($value, $operator);
+
+ case 'day_of_week':
+ return $this->check_day_of_week($value, $operator);
+
+ default:
+ return apply_filters('wptag_custom_condition', true, $type, $value, $operator, $context);
+ }
+ }
+
+ private function check_page_type($value, $operator) {
+ $page_types = [
+ 'home' => is_home() || is_front_page(),
+ 'single' => is_single(),
+ 'page' => is_page(),
+ 'archive' => is_archive(),
+ 'category' => is_category(),
+ 'tag' => is_tag(),
+ 'search' => is_search(),
+ '404' => is_404(),
+ 'author' => is_author(),
+ 'date' => is_date()
+ ];
+
+ $is_type = $page_types[$value] ?? false;
+
+ return $operator === 'not_equals' ? !$is_type : $is_type;
+ }
+
+ private function check_user_status($value, $operator) {
+ $is_logged_in = is_user_logged_in();
+
+ if ($value === 'logged_in') {
+ return $operator === 'not_equals' ? !$is_logged_in : $is_logged_in;
+ } else {
+ return $operator === 'not_equals' ? $is_logged_in : !$is_logged_in;
+ }
+ }
+
+ private function check_user_role($value, $operator) {
+ if (!is_user_logged_in()) {
+ return $operator === 'not_equals';
+ }
+
+ $user = wp_get_current_user();
+ $has_role = in_array($value, $user->roles);
+
+ return $operator === 'not_equals' ? !$has_role : $has_role;
+ }
+
+ private function check_device_type($value, $operator) {
+ $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
+ $is_mobile = wp_is_mobile();
+
+ $device_checks = [
+ 'mobile' => $is_mobile && !$this->is_tablet($user_agent),
+ 'tablet' => $this->is_tablet($user_agent),
+ 'desktop' => !$is_mobile
+ ];
+
+ $is_device = $device_checks[$value] ?? false;
+
+ return $operator === 'not_equals' ? !$is_device : $is_device;
+ }
+
+ private function is_tablet($user_agent) {
+ $tablet_patterns = '/iPad|Android.*Tablet|Tablet.*Android|Kindle|Silk|Galaxy Tab/i';
+ return preg_match($tablet_patterns, $user_agent);
+ }
+
+ private function check_post_id($value, $operator) {
+ $current_id = get_the_ID();
+ $ids = array_map('intval', explode(',', $value));
+
+ switch ($operator) {
+ case 'equals':
+ return in_array($current_id, $ids);
+ case 'not_equals':
+ return !in_array($current_id, $ids);
+ case 'greater_than':
+ return $current_id > $ids[0];
+ case 'less_than':
+ return $current_id < $ids[0];
+ default:
+ return false;
+ }
+ }
+
+ private function check_category($value, $operator) {
+ if (!is_single() && !is_category()) {
+ return $operator === 'not_equals';
+ }
+
+ $categories = explode(',', $value);
+ $has_category = false;
+
+ if (is_single()) {
+ foreach ($categories as $cat) {
+ if (has_category($cat)) {
+ $has_category = true;
+ break;
+ }
+ }
+ } elseif (is_category()) {
+ $current_cat = get_queried_object();
+ $has_category = in_array($current_cat->slug, $categories) ||
+ in_array($current_cat->term_id, $categories);
+ }
+
+ return $operator === 'not_equals' ? !$has_category : $has_category;
+ }
+
+ private function check_tag($value, $operator) {
+ if (!is_single() && !is_tag()) {
+ return $operator === 'not_equals';
+ }
+
+ $tags = explode(',', $value);
+ $has_tag = false;
+
+ if (is_single()) {
+ foreach ($tags as $tag) {
+ if (has_tag($tag)) {
+ $has_tag = true;
+ break;
+ }
+ }
+ } elseif (is_tag()) {
+ $current_tag = get_queried_object();
+ $has_tag = in_array($current_tag->slug, $tags) ||
+ in_array($current_tag->term_id, $tags);
+ }
+
+ return $operator === 'not_equals' ? !$has_tag : $has_tag;
+ }
+
+ private function check_url($value, $operator) {
+ $current_url = $_SERVER['REQUEST_URI'] ?? '';
+
+ switch ($operator) {
+ case 'contains':
+ return strpos($current_url, $value) !== false;
+ case 'not_contains':
+ return strpos($current_url, $value) === false;
+ case 'equals':
+ return $current_url === $value;
+ case 'not_equals':
+ return $current_url !== $value;
+ case 'starts_with':
+ return strpos($current_url, $value) === 0;
+ case 'ends_with':
+ return substr($current_url, -strlen($value)) === $value;
+ default:
+ return false;
+ }
+ }
+
+ private function check_date_range($value, $operator) {
+ $current_time = current_time('timestamp');
+ $dates = explode('|', $value);
+
+ if (count($dates) !== 2) {
+ return false;
+ }
+
+ $start_date = strtotime($dates[0]);
+ $end_date = strtotime($dates[1] . ' 23:59:59');
+
+ $in_range = $current_time >= $start_date && $current_time <= $end_date;
+
+ return $operator === 'not_in' ? !$in_range : $in_range;
+ }
+
+ private function check_time($value, $operator) {
+ $current_time = current_time('H:i');
+ $times = explode('|', $value);
+
+ if (count($times) !== 2) {
+ return false;
+ }
+
+ $in_range = $current_time >= $times[0] && $current_time <= $times[1];
+
+ return $operator === 'not_in' ? !$in_range : $in_range;
+ }
+
+ private function check_day_of_week($value, $operator) {
+ $current_day = strtolower(current_time('l'));
+ $days = array_map('strtolower', explode(',', $value));
+
+ $is_day = in_array($current_day, $days);
+
+ return $operator === 'not_in' ? !$is_day : $is_day;
+ }
+
+ public function get_condition_types() {
+ return [
+ 'page_type' => [
+ 'label' => __('Page Type', 'wptag'),
+ 'values' => [
+ 'home' => __('Home Page', 'wptag'),
+ 'single' => __('Single Post', 'wptag'),
+ 'page' => __('Page', 'wptag'),
+ 'archive' => __('Archive', 'wptag'),
+ 'category' => __('Category', 'wptag'),
+ 'tag' => __('Tag', 'wptag'),
+ 'search' => __('Search', 'wptag'),
+ '404' => __('404 Page', 'wptag')
+ ]
+ ],
+ 'user_status' => [
+ 'label' => __('User Status', 'wptag'),
+ 'values' => [
+ 'logged_in' => __('Logged In', 'wptag'),
+ 'logged_out' => __('Logged Out', 'wptag')
+ ]
+ ],
+ 'device_type' => [
+ 'label' => __('Device Type', 'wptag'),
+ 'values' => [
+ 'mobile' => __('Mobile', 'wptag'),
+ 'tablet' => __('Tablet', 'wptag'),
+ 'desktop' => __('Desktop', 'wptag')
+ ]
+ ]
+ ];
+ }
+}
diff --git a/includes/class-wptag-core.php b/includes/class-wptag-core.php
new file mode 100644
index 0000000..eac4dbb
--- /dev/null
+++ b/includes/class-wptag-core.php
@@ -0,0 +1,267 @@
+load_dependencies();
+ $this->init();
+ }
+
+ private function load_dependencies() {
+ require_once WPTAG_PLUGIN_DIR . 'includes/class-wptag-snippet-manager.php';
+ require_once WPTAG_PLUGIN_DIR . 'includes/class-wptag-condition-engine.php';
+ require_once WPTAG_PLUGIN_DIR . 'includes/class-wptag-output-controller.php';
+ require_once WPTAG_PLUGIN_DIR . 'includes/class-wptag-template-manager.php';
+ require_once WPTAG_PLUGIN_DIR . 'includes/class-wptag-cache-manager.php';
+
+ if (is_admin()) {
+ require_once WPTAG_PLUGIN_DIR . 'admin/class-wptag-admin-controller.php';
+ require_once WPTAG_PLUGIN_DIR . 'admin/class-wptag-ajax-handler.php';
+ require_once WPTAG_PLUGIN_DIR . 'admin/class-wptag-admin-interface.php';
+ }
+ }
+
+ private function init() {
+ $this->snippet_manager = new WPTag_Snippet_Manager();
+ $this->condition_engine = new WPTag_Condition_Engine();
+ $this->template_manager = new WPTag_Template_Manager();
+ $this->cache_manager = new WPTag_Cache_Manager();
+ $this->output_controller = new WPTag_Output_Controller(
+ $this->snippet_manager,
+ $this->condition_engine,
+ $this->cache_manager
+ );
+
+ if (is_admin()) {
+ new WPTag_Admin_Controller($this->snippet_manager, $this->template_manager);
+ new WPTag_Ajax_Handler($this->snippet_manager, $this->template_manager);
+ } else {
+ $this->register_output_hooks();
+ }
+
+ add_action('init', [$this, 'check_version']);
+ }
+
+ private function register_output_hooks() {
+ add_action('wp_head', [$this->output_controller, 'render_head'], 1);
+ add_action('wp_footer', [$this->output_controller, 'render_footer'], 999);
+ add_filter('the_content', [$this->output_controller, 'filter_content'], 10);
+ }
+
+ public function check_version() {
+ $installed_version = get_option('wptag_db_version');
+ if ($installed_version !== WPTAG_DB_VERSION) {
+ self::create_tables();
+ update_option('wptag_db_version', WPTAG_DB_VERSION);
+ }
+ }
+
+ public static function activate() {
+ if (version_compare(PHP_VERSION, '8.0', '<')) {
+ deactivate_plugins(plugin_basename(WPTAG_PLUGIN_FILE));
+ wp_die('WPTAG requires PHP 8.0 or higher.');
+ }
+
+ if (version_compare(get_bloginfo('version'), '6.8', '<')) {
+ deactivate_plugins(plugin_basename(WPTAG_PLUGIN_FILE));
+ wp_die('WPTAG requires WordPress 6.8 or higher.');
+ }
+
+ self::create_tables();
+ self::create_default_templates();
+ flush_rewrite_rules();
+ }
+
+ public static function deactivate() {
+ wp_clear_scheduled_hook('wptag_cleanup_logs');
+ flush_rewrite_rules();
+ }
+
+ private static function create_tables() {
+ global $wpdb;
+ $charset_collate = $wpdb->get_charset_collate();
+
+ $sql_snippets = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wptag_snippets (
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ name varchar(255) NOT NULL,
+ description text,
+ code longtext NOT NULL,
+ code_type varchar(50) DEFAULT 'html',
+ position varchar(100) NOT NULL,
+ category varchar(100) DEFAULT 'custom',
+ priority int(11) DEFAULT 10,
+ status tinyint(1) DEFAULT 1,
+ conditions longtext,
+ device_type varchar(50) DEFAULT 'all',
+ load_method varchar(50) DEFAULT 'normal',
+ created_by bigint(20) unsigned,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP,
+ updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ last_modified_by bigint(20) unsigned,
+ PRIMARY KEY (id),
+ KEY idx_status_position (status, position),
+ KEY idx_category (category),
+ KEY idx_priority (priority)
+ ) $charset_collate;";
+
+ $sql_templates = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wptag_templates (
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ service_type varchar(100) NOT NULL,
+ service_name varchar(255) NOT NULL,
+ service_category varchar(100) NOT NULL,
+ config_fields longtext,
+ code_template longtext NOT NULL,
+ default_position varchar(100) NOT NULL,
+ is_active tinyint(1) DEFAULT 1,
+ version varchar(20) DEFAULT '1.0',
+ created_at datetime DEFAULT CURRENT_TIMESTAMP,
+ updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ UNIQUE KEY idx_service_type (service_type),
+ KEY idx_category (service_category)
+ ) $charset_collate;";
+
+ $sql_logs = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wptag_logs (
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ user_id bigint(20) unsigned NOT NULL,
+ action varchar(100) NOT NULL,
+ object_type varchar(50) NOT NULL,
+ object_id bigint(20) unsigned,
+ old_value longtext,
+ new_value longtext,
+ ip_address varchar(45),
+ user_agent text,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ KEY idx_user_action (user_id, action),
+ KEY idx_created_at (created_at)
+ ) $charset_collate;";
+
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
+ dbDelta($sql_snippets);
+ dbDelta($sql_templates);
+ dbDelta($sql_logs);
+ }
+
+ private static function create_default_templates() {
+ global $wpdb;
+ $table = $wpdb->prefix . 'wptag_templates';
+
+ $templates = [
+ [
+ 'service_type' => 'google_analytics_4',
+ 'service_name' => 'Google Analytics 4',
+ 'service_category' => 'analytics',
+ 'config_fields' => json_encode([
+ ['name' => 'measurement_id', 'label' => 'Measurement ID', 'type' => 'text', 'required' => true]
+ ]),
+ 'code_template' => '
+',
+ 'default_position' => 'head'
+ ],
+ [
+ 'service_type' => 'facebook_pixel',
+ 'service_name' => 'Facebook Pixel',
+ 'service_category' => 'marketing',
+ 'config_fields' => json_encode([
+ ['name' => 'pixel_id', 'label' => 'Pixel ID', 'type' => 'text', 'required' => true]
+ ]),
+ 'code_template' => '
+',
+ 'default_position' => 'head'
+ ],
+ [
+ 'service_type' => 'google_ads',
+ 'service_name' => 'Google Ads Conversion',
+ 'service_category' => 'marketing',
+ 'config_fields' => json_encode([
+ ['name' => 'conversion_id', 'label' => 'Conversion ID', 'type' => 'text', 'required' => true],
+ ['name' => 'conversion_label', 'label' => 'Conversion Label', 'type' => 'text', 'required' => true]
+ ]),
+ 'code_template' => '
+',
+ 'default_position' => 'head'
+ ],
+ [
+ 'service_type' => 'google_search_console',
+ 'service_name' => 'Google Search Console',
+ 'service_category' => 'seo',
+ 'config_fields' => json_encode([
+ ['name' => 'verification_code', 'label' => 'Verification Code', 'type' => 'text', 'required' => true]
+ ]),
+ 'code_template' => '',
+ 'default_position' => 'head'
+ ],
+ [
+ 'service_type' => 'baidu_tongji',
+ 'service_name' => 'Baidu Tongji',
+ 'service_category' => 'analytics',
+ 'config_fields' => json_encode([
+ ['name' => 'site_id', 'label' => 'Site ID', 'type' => 'text', 'required' => true]
+ ]),
+ 'code_template' => '',
+ 'default_position' => 'head'
+ ]
+ ];
+
+ foreach ($templates as $template) {
+ $exists = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM $table WHERE service_type = %s",
+ $template['service_type']
+ ));
+
+ if (!$exists) {
+ $wpdb->insert($table, $template);
+ }
+ }
+ }
+}
diff --git a/includes/class-wptag-output-controller.php b/includes/class-wptag-output-controller.php
new file mode 100644
index 0000000..4cdbbb9
--- /dev/null
+++ b/includes/class-wptag-output-controller.php
@@ -0,0 +1,178 @@
+snippet_manager = $snippet_manager;
+ $this->condition_engine = $condition_engine;
+ $this->cache_manager = $cache_manager;
+ }
+
+ public function render_head() {
+ $this->render_snippets('head');
+ }
+
+ public function render_footer() {
+ $this->render_snippets('footer');
+ }
+
+ public function filter_content($content) {
+ if (!in_the_loop() || !is_main_query()) {
+ return $content;
+ }
+
+ $before = $this->get_rendered_snippets('before_content');
+ $after = $this->get_rendered_snippets('after_content');
+
+ return $before . $content . $after;
+ }
+
+ private function render_snippets($position) {
+ echo $this->get_rendered_snippets($position);
+ }
+
+ private function get_rendered_snippets($position) {
+ $cache_key = 'wptag_output_' . $position . '_' . $this->get_cache_context();
+ $cached = $this->cache_manager->get($cache_key);
+
+ if ($cached !== false && !$this->is_preview_mode()) {
+ return $cached;
+ }
+
+ $snippets = $this->snippet_manager->get_active_snippets_by_position($position);
+ $output = '';
+
+ foreach ($snippets as $snippet) {
+ if ($this->should_render_snippet($snippet)) {
+ $output .= $this->render_single_snippet($snippet);
+ $this->rendered_snippets[] = $snippet['id'];
+ }
+ }
+
+ if (!empty($output)) {
+ $output = "\n\n" . $output . "\n";
+ }
+
+ $this->cache_manager->set($cache_key, $output, 3600);
+
+ return $output;
+ }
+
+ private function should_render_snippet($snippet) {
+ if (in_array($snippet['id'], $this->rendered_snippets)) {
+ return false;
+ }
+
+ if ($this->is_preview_mode() && !current_user_can('manage_options')) {
+ return false;
+ }
+
+ if (!empty($snippet['device_type']) && $snippet['device_type'] !== 'all') {
+ $device_check = $this->condition_engine->evaluate_conditions([
+ 'rules' => [[
+ 'type' => 'device_type',
+ 'operator' => 'equals',
+ 'value' => $snippet['device_type']
+ ]]
+ ]);
+
+ if (!$device_check) {
+ return false;
+ }
+ }
+
+ if (!empty($snippet['conditions'])) {
+ return $this->condition_engine->evaluate_conditions($snippet['conditions']);
+ }
+
+ return true;
+ }
+
+ private function render_single_snippet($snippet) {
+ $code = $snippet['code'];
+
+ if ($snippet['load_method'] === 'async' && $snippet['code_type'] === 'javascript') {
+ $code = $this->wrap_async_script($code);
+ } elseif ($snippet['load_method'] === 'defer' && $snippet['code_type'] === 'javascript') {
+ $code = $this->wrap_defer_script($code);
+ }
+
+ $code = apply_filters('wptag_snippet_output', $code, $snippet);
+
+ if ($this->is_preview_mode() && current_user_can('manage_options')) {
+ $code = $this->wrap_preview_mode($code, $snippet);
+ }
+
+ return $code . "\n";
+ }
+
+ private function wrap_async_script($code) {
+ if (strpos($code, '';
+ }
+
+ return str_replace('';
+ }
+
+ return str_replace('