PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.87
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.87
51.1.87 51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 All 41 releases
king-addons / includes / extensions / Header_Footer_Builder / Header_Footer_Builder.php

Header_Footer_Builder.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.87, at includes/extensions/Header_Footer_Builder/Header_Footer_Builder.php

4,450 lines 187.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php /** @noinspection PhpMissingFieldTypeInspection, DuplicatedCode */
2
3 namespace King_Addons;
4
5 use Elementor;
6 use WP_Query;
7
8 if (!defined('ABSPATH')) {
9 exit;
10 }
11
12 final class Header_Footer_Builder
13 {
14 private static ?Header_Footer_Builder $instance = null;
15 private static ?string $current_page_type = null;
16 private static array $current_page_data = array();
17 private static $location_selection;
18 private static $user_selection;
19 private static $elementor_instance;
20
21 /**
22 * Admin menu slug for the new page.
23 *
24 * @var string
25 */
26 private string $menu_slug = 'king-addons-el-hf';
27
28 public static function instance(): ?Header_Footer_Builder
29 {
30 if (is_null(self::$instance)) {
31 self::$instance = new self();
32 }
33 return self::$instance;
34 }
35
36 public function __construct()
37 {
38 add_action('init', [$this, 'addPostType']);
39 add_action('add_meta_boxes', [$this, 'registerMetabox']);
40 add_action('save_post', [$this, 'saveMetaboxData']);
41 add_action('template_redirect', [$this, 'checkUserCanEdit']);
42 add_filter('screen_options_show_screen', [$this, 'disableScreenOptions'], 10, 2);
43
44 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/ELHF_Render_On_Canvas.php');
45 add_filter('single_template', [$this, 'loadElementorCanvasTemplate']);
46 add_filter('template_include', [$this, 'forceElementorCanvasTemplate'], 99);
47
48 self::setCompatibility();
49 add_action('admin_enqueue_scripts', array($this, 'enqueueScripts'));
50 add_action('admin_action_edit', array($this, 'initialize_options'));
51 add_action('wp_ajax_king_addons_el_hf_get_posts_by_query', array($this, 'king_addons_el_hf_get_posts_by_query'));
52 add_action('pre_get_posts', [$this, 'forcePreviewQuery']);
53
54 // Handle template creation and actions
55 add_action('admin_post_ka_hf_builder_create', [$this, 'handleCreateTemplate']);
56 add_action('admin_post_ka_hf_builder_quick_update', [$this, 'handleQuickUpdate']);
57
58 // AJAX handler for conditions popup
59 add_action('wp_ajax_ka_hf_save_conditions', [$this, 'handleAjaxSaveConditions']);
60
61 // AJAX handlers for rename and toggle status
62 add_action('wp_ajax_ka_hf_rename_template', [$this, 'handleAjaxRenameTemplate']);
63 add_action('wp_ajax_ka_hf_toggle_template_status', [$this, 'handleAjaxToggleTemplateStatus']);
64
65 if (is_admin()) {
66 add_action('manage_king-addons-el-hf_posts_custom_column', [$this, 'columnContent'], 10, 2);
67 add_filter('manage_king-addons-el-hf_posts_columns', [$this, 'columnHeadings']);
68 }
69 }
70
71 /**
72 * Register admin menu entry as top-level menu.
73 *
74 * @return void
75 */
76 public function registerAdminMenu(): void
77 {
78 global $menu;
79 $menu['54.6'] = array('', 'read', 'separator-king-addons-hf', '', 'wp-menu-separator');
80
81 add_menu_page(
82 esc_html__('Header & Footer Builder', 'king-addons'),
83 esc_html__('Header & Footer', 'king-addons'),
84 'manage_options',
85 $this->menu_slug,
86 [$this, 'renderAdminPage'],
87 'dashicons-align-full-width',
88 54.7
89 );
90 }
91
92 /**
93 * Render modern admin page for Header & Footer Builder.
94 *
95 * @return void
96 */
97 public function renderAdminPage(): void
98 {
99 if (!current_user_can('manage_options')) {
100 return;
101 }
102
103 // Include shared dark theme support
104 include KING_ADDONS_PATH . 'includes/admin/shared/dark-theme.php';
105
106 // Handle tab navigation
107 $current_tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : 'templates'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
108 if (!in_array($current_tab, ['templates', 'settings'], true)) {
109 $current_tab = 'templates';
110 }
111
112 $this->handleInlineActions();
113 $templates = $this->prepareAdminTemplates();
114
115 $base_url = admin_url('admin.php?page=' . $this->menu_slug);
116 $status_filter = 'all';
117 $filtered_templates = $templates;
118
119 $type_cards = [
120 'header' => [
121 'label' => esc_html__('Header', 'king-addons'),
122 'desc' => esc_html__('Site header templates', 'king-addons'),
123 'value' => 'king_addons_el_hf_type_header',
124 ],
125 'footer' => [
126 'label' => esc_html__('Footer', 'king-addons'),
127 'desc' => esc_html__('Site footer templates', 'king-addons'),
128 'value' => 'king_addons_el_hf_type_footer',
129 ],
130 ];
131
132 $this->renderModernStyles();
133
134 // Render dark theme styles and init
135 ka_render_dark_theme_styles();
136 ka_render_dark_theme_init();
137 ?>
138 <script>
139 if (document.body) {
140 document.body.classList.add('ka-admin-v3');
141 } else {
142 document.addEventListener('DOMContentLoaded', function() {
143 document.body.classList.add('ka-admin-v3');
144 });
145 }
146 </script>
147
148 <div class="ka-hf">
149 <header class="ka-hf-header">
150 <div class="ka-hf-header-content">
151 <span class="ka-hf-title-icon" aria-hidden="true">
152 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
153 <path d="M3 3h18v6H3zM3 15h18v6H3z" />
154 </svg>
155 </span>
156 <div class="ka-hf-header-titles">
157 <h1><span class="ka-hf-title-text"><?php esc_html_e('Header & Footer Builder', 'king-addons'); ?></span></h1>
158 <p><?php esc_html_e('Create custom headers and footers with display conditions', 'king-addons'); ?></p>
159 </div>
160 </div>
161 <div class="ka-hf-header-actions">
162 <?php if ('templates' === $current_tab) : ?>
163 <button type="button" id="ka-hf-add-new" class="ka-hf-btn ka-hf-btn-primary">
164 <span class="ka-hf-btn-icon" aria-hidden="true">+</span>
165 <?php esc_html_e('Add New Template', 'king-addons'); ?>
166 </button>
167 <?php endif; ?>
168 <?php ka_render_dark_theme_toggle(); ?>
169 </div>
170 </header>
171
172 <!-- Navigation Tabs -->
173 <nav class="ka-hf-nav-tabs">
174 <a href="<?php echo esc_url($base_url); ?>" class="ka-hf-nav-tab<?php echo 'templates' === $current_tab ? ' is-active' : ''; ?>">
175 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
176 <path d="M3 3h18v6H3zM3 15h18v6H3z" />
177 </svg>
178 <?php esc_html_e('Templates', 'king-addons'); ?>
179 </a>
180 <a href="<?php echo esc_url(add_query_arg('tab', 'settings', $base_url)); ?>" class="ka-hf-nav-tab<?php echo 'settings' === $current_tab ? ' is-active' : ''; ?>">
181 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
182 <circle cx="12" cy="12" r="3"/>
183 <path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/>
184 </svg>
185 <?php esc_html_e('Display Settings', 'king-addons'); ?>
186 </a>
187 </nav>
188
189 <?php if ('templates' === $current_tab) : ?>
190
191 <div class="ka-hf-types" role="list">
192 <?php foreach ($type_cards as $type_slug => $data) : ?>
193 <?php $type_icon_svg = $this->getTypeIconSvg($type_slug); ?>
194 <button
195 type="button"
196 class="ka-hf-type"
197 role="listitem"
198 data-ka-hf-type="<?php echo esc_attr($data['value']); ?>"
199 >
200 <div class="ka-hf-type-icon" aria-hidden="true">
201 <?php echo $type_icon_svg; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
202 </div>
203 <div class="ka-hf-type-label"><?php echo esc_html($data['label']); ?></div>
204 <div class="ka-hf-type-desc"><?php echo esc_html($data['desc']); ?></div>
205 </button>
206 <?php endforeach; ?>
207 </div>
208
209 <section class="ka-hf-section">
210 <div class="ka-hf-section-header">
211 <h2 class="ka-hf-section-title"><?php esc_html_e('Templates', 'king-addons'); ?></h2>
212 <div class="ka-hf-section-actions">
213 <div class="ka-hf-filters" role="navigation">
214 <button type="button" class="ka-hf-filter is-active" data-filter="all"><?php esc_html_e('All', 'king-addons'); ?></button>
215 <button type="button" class="ka-hf-filter" data-filter="header"><?php esc_html_e('Headers', 'king-addons'); ?></button>
216 <button type="button" class="ka-hf-filter" data-filter="footer"><?php esc_html_e('Footers', 'king-addons'); ?></button>
217 </div>
218 <div class="ka-hf-section-count"><?php echo esc_html(count($filtered_templates) . ' ' . _n('item', 'items', count($filtered_templates), 'king-addons')); ?></div>
219 </div>
220 </div>
221
222 <div class="ka-hf-templates" role="list">
223 <?php if (empty($filtered_templates)) : ?>
224 <div class="ka-hf-empty">
225 <h3 class="ka-hf-empty-title"><?php esc_html_e('No templates yet', 'king-addons'); ?></h3>
226 <p class="ka-hf-empty-desc"><?php esc_html_e('Create your first header or footer template to get started.', 'king-addons'); ?></p>
227 <button type="button" class="ka-hf-btn ka-hf-btn-primary" id="ka-hf-add-new-empty">
228 <span class="ka-hf-btn-icon" aria-hidden="true">+</span>
229 <?php esc_html_e('Add New Template', 'king-addons'); ?>
230 </button>
231 </div>
232 <?php else : ?>
233 <?php foreach ($filtered_templates as $template) : ?>
234 <?php
235 $template_id = (int) ($template['id'] ?? 0);
236 if (!$template_id) {
237 continue;
238 }
239
240 $title = !empty($template['title']) ? (string) $template['title'] : sprintf(
241 esc_html__('Template #%d', 'king-addons'),
242 $template_id
243 );
244
245 $edit_elementor_url = admin_url('post.php?post=' . $template_id . '&action=elementor');
246 $edit_settings_url = admin_url('post.php?post=' . $template_id . '&action=edit');
247 $type_value = $template['type'] ?? '';
248 $type_label = 'king_addons_el_hf_type_header' === $type_value ? esc_html__('Header', 'king-addons') : ('king_addons_el_hf_type_footer' === $type_value ? esc_html__('Footer', 'king-addons') : esc_html__('Not Set', 'king-addons'));
249 $type_slug = 'king_addons_el_hf_type_header' === $type_value ? 'header' : ('king_addons_el_hf_type_footer' === $type_value ? 'footer' : 'unset');
250 $delete_url = wp_nonce_url(add_query_arg(['action' => 'delete_template', 'template_id' => $template_id], $base_url), 'ka_hf_delete_' . $template_id);
251 $conditions_text = $this->summarizeConditions($template);
252 $title_icon_svg = $this->getTypeIconSvg($type_slug);
253 $post_status = get_post_status($template_id);
254 $is_disabled = 'publish' !== $post_status;
255 $status_label = $is_disabled ? esc_html__('Disabled', 'king-addons') : $type_label;
256 ?>
257 <div class="ka-hf-template" role="listitem" data-template-type="<?php echo esc_attr($type_slug); ?>" data-template-id="<?php echo esc_attr($template_id); ?>" data-status="<?php echo esc_attr($post_status); ?>" data-type-label="<?php echo esc_attr($type_label); ?>">
258 <div class="ka-hf-template-status <?php echo $is_disabled ? 'is-disabled' : 'is-enabled'; ?>">
259 <?php echo esc_html($status_label); ?>
260 </div>
261 <div class="ka-hf-template-info">
262 <a class="ka-hf-template-title" href="<?php echo esc_url($edit_elementor_url); ?>">
263 <span class="ka-hf-template-title-icon" aria-hidden="true">
264 <?php echo $title_icon_svg; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
265 </span>
266 <span class="ka-hf-template-title-text"><?php echo esc_html($title); ?></span>
267 </a>
268 <div class="ka-hf-template-meta">
269 <span class="ka-hf-template-type"><?php echo esc_html($type_label); ?></span>
270 </div>
271 </div>
272 <?php
273 // Prepare conditions data for the popup
274 $include_locs = $template['include_locations'] ?? [];
275 $exclude_locs = $template['exclude_locations'] ?? [];
276 $user_roles_arr = $template['user_roles'] ?? [];
277 $template_data = [
278 'id' => $template_id,
279 'title' => $title,
280 'type' => $type_value,
281 'include' => $include_locs,
282 'exclude' => $exclude_locs,
283 'userRoles' => $user_roles_arr,
284 ];
285 ?>
286 <button type="button" class="ka-hf-template-condition ka-hf-open-conditions" title="<?php echo esc_attr__('Edit Display Conditions', 'king-addons'); ?>" data-template='<?php echo esc_attr(wp_json_encode($template_data)); ?>'>
287 <svg class="ka-hf-condition-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
288 <circle cx="12" cy="12" r="3"/>
289 <path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/>
290 </svg>
291 <?php echo esc_html($conditions_text); ?>
292 </button>
293 <div class="ka-hf-template-actions">
294 <a class="ka-hf-btn ka-hf-btn-primary" href="<?php echo esc_url($edit_elementor_url); ?>"><?php esc_html_e('Edit with Elementor', 'king-addons'); ?></a>
295 <div class="ka-hf-dropdown" data-ka-dropdown>
296 <button type="button" class="ka-hf-dropdown-trigger" aria-label="<?php echo esc_attr(esc_html__('More actions', 'king-addons')); ?>">
297 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
298 <circle cx="12" cy="5" r="1" />
299 <circle cx="12" cy="12" r="1" />
300 <circle cx="12" cy="19" r="1" />
301 </svg>
302 </button>
303 <div class="ka-hf-dropdown-menu" role="menu">
304 <button type="button" class="ka-hf-dropdown-item ka-hf-rename-btn" role="menuitem" data-id="<?php echo esc_attr($template_id); ?>" data-title="<?php echo esc_attr($title); ?>">
305 <?php esc_html_e('Rename', 'king-addons'); ?>
306 </button>
307 <a class="ka-hf-dropdown-item" role="menuitem" href="<?php echo esc_url($edit_settings_url); ?>">
308 <?php esc_html_e('WP Edit', 'king-addons'); ?>
309 </a>
310 <button type="button" class="ka-hf-dropdown-item ka-hf-toggle-status-btn" role="menuitem" data-id="<?php echo esc_attr($template_id); ?>" data-status="<?php echo esc_attr(get_post_status($template_id)); ?>">
311 <?php echo get_post_status($template_id) === 'publish' ? esc_html__('Disable', 'king-addons') : esc_html__('Enable', 'king-addons'); ?>
312 </button>
313 <a class="ka-hf-dropdown-item is-danger" role="menuitem" href="<?php echo esc_url($delete_url); ?>" onclick="return confirm('<?php echo esc_js(esc_html__('Move template to trash?', 'king-addons')); ?>');">
314 <?php esc_html_e('Delete', 'king-addons'); ?>
315 </a>
316 </div>
317 </div>
318 </div>
319 </div>
320 <?php endforeach; ?>
321 <?php endif; ?>
322 </div>
323
324 <div class="ka-hf-filter-empty" style="display: none;">
325 <h3 class="ka-hf-empty-title"><?php esc_html_e('No templates found', 'king-addons'); ?></h3>
326 <p class="ka-hf-empty-desc"><?php esc_html_e('Try a different filter or create a new template.', 'king-addons'); ?></p>
327 <button type="button" class="ka-hf-btn ka-hf-btn-primary" id="ka-hf-add-new-filter-empty">
328 <span class="ka-hf-btn-icon" aria-hidden="true">+</span>
329 <?php esc_html_e('Add New Template', 'king-addons'); ?>
330 </button>
331 </div>
332 </section>
333
334 <?php $this->renderAddNewModal(); ?>
335 <?php $this->renderRenameModal(); ?>
336 <?php $this->renderConditionsPopup(); ?>
337
338 <?php else : // settings tab ?>
339
340 <?php $this->renderDisplaySettingsTab(); ?>
341
342 <?php endif; ?>
343 </div>
344 <?php
345
346 // Render dark theme script at the end
347 ka_render_dark_theme_script();
348 }
349
350 /**
351 * Render the Display Settings tab content.
352 *
353 * @return void
354 */
355 private function renderDisplaySettingsTab(): void
356 {
357 $chosen_option = get_option('king_addons_el_hf_compatibility_option', '3');
358 ?>
359 <section class="ka-hf-section ka-hf-settings-section">
360 <div class="ka-hf-section-header">
361 <h2 class="ka-hf-section-title"><?php esc_html_e('Display Settings', 'king-addons'); ?></h2>
362 </div>
363
364 <form method="post" action="options.php" class="ka-hf-settings-form">
365 <?php settings_fields('king-addons-el-hf-ext-options'); ?>
366
367 <div class="ka-hf-settings-card">
368 <h3 class="ka-hf-settings-card-title"><?php esc_html_e('Compatibility Mode', 'king-addons'); ?></h3>
369 <p class="ka-hf-settings-card-desc"><?php esc_html_e('To ensure compatibility with the current theme, three methods are available:', 'king-addons'); ?></p>
370
371 <div class="ka-hf-settings-options">
372
373 <label class="ka-hf-settings-option">
374 <input type="radio" name="king_addons_el_hf_compatibility_option" value="1" <?php checked($chosen_option, '1'); ?>>
375 <div class="ka-hf-settings-option-content">
376 <span class="ka-hf-settings-option-title"><?php esc_html_e('Method 1 - Replace Theme Templates', 'king-addons'); ?></span>
377 <span class="ka-hf-settings-option-desc"><?php esc_html_e('This method replaces the theme header (header.php) and footer (footer.php) templates with custom templates. Works well with classic themes that use standard WordPress template structure.', 'king-addons'); ?></span>
378 </div>
379 </label>
380
381 <label class="ka-hf-settings-option">
382 <input type="radio" name="king_addons_el_hf_compatibility_option" value="2" <?php checked($chosen_option, '2'); ?>>
383 <div class="ka-hf-settings-option-content">
384 <span class="ka-hf-settings-option-title"><?php esc_html_e('Method 2 - CSS Hide + Inject', 'king-addons'); ?></span>
385 <span class="ka-hf-settings-option-desc"><?php esc_html_e('This method hides the theme header and footer using CSS (display: none;) and injects custom templates via wp_body_open and wp_footer hooks.', 'king-addons'); ?></span>
386 </div>
387 </label>
388
389 <label class="ka-hf-settings-option">
390 <input type="radio" name="king_addons_el_hf_compatibility_option" value="3" <?php checked($chosen_option, '3'); ?>>
391 <div class="ka-hf-settings-option-content">
392 <span class="ka-hf-settings-option-title"><?php esc_html_e('Method 3 - Universal (Recommended)', 'king-addons'); ?></span>
393 <span class="ka-hf-settings-option-desc"><?php esc_html_e('This method combines multiple approaches for maximum theme compatibility. It uses hooks, output buffering, CSS hiding of native theme headers/footers, and JavaScript fallback to ensure headers and footers display correctly on all themes including Block Themes (FSE).', 'king-addons'); ?></span>
394 </div>
395 </label>
396
397 </div>
398
399 <div class="ka-hf-settings-actions">
400 <button type="submit" class="ka-hf-btn ka-hf-btn-primary"><?php esc_html_e('Save Settings', 'king-addons'); ?></button>
401 </div>
402 </div>
403 </form>
404 </section>
405 <?php
406 }
407
408 /**
409 * Render the Add New Template modal.
410 *
411 * @return void
412 */
413 private function renderAddNewModal(): void
414 {
415 self::$location_selection = self::getLocationSelections();
416 self::$user_selection = self::get_user_selections();
417 ?>
418 <div id="ka-hf-modal" class="ka-hf-modal-overlay" aria-hidden="true">
419 <div class="ka-hf-modal" role="dialog" aria-modal="true" aria-labelledby="ka-hf-create-title">
420 <h3 id="ka-hf-create-title"><?php echo esc_html__('Create Header / Footer Template', 'king-addons'); ?></h3>
421 <p class="ka-hf-modal-desc"><?php echo esc_html__('Choose the template type and configure display conditions.', 'king-addons'); ?></p>
422
423 <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
424 <input type="hidden" name="action" value="ka_hf_builder_create" />
425 <?php wp_nonce_field('ka_hf_builder_create', 'ka_hf_builder_create_nonce'); ?>
426
427 <div class="ka-hf-form-group">
428 <label class="ka-hf-form-label" for="ka-hf-title"><?php echo esc_html__('Template Name', 'king-addons'); ?></label>
429 <input type="text" id="ka-hf-title" name="ka_hf_title" class="ka-hf-modal-input" value="<?php echo esc_attr__('My Template', 'king-addons'); ?>" />
430 </div>
431
432 <div class="ka-hf-form-group">
433 <label class="ka-hf-form-label" for="ka-hf-type"><?php echo esc_html__('Template Type', 'king-addons'); ?></label>
434 <select id="ka-hf-type" name="ka_hf_type" class="ka-hf-form-select">
435 <option value="king_addons_el_hf_type_header"><?php echo esc_html__('Header', 'king-addons'); ?></option>
436 <option value="king_addons_el_hf_type_footer"><?php echo esc_html__('Footer', 'king-addons'); ?></option>
437 </select>
438 </div>
439
440 <div class="ka-hf-form-group">
441 <label class="ka-hf-form-label"><?php echo esc_html__('Display On', 'king-addons'); ?></label>
442 <p class="ka-hf-form-desc"><?php echo esc_html__('Add locations where this template should appear', 'king-addons'); ?></p>
443 <div class="ka-hf-conditions-wrap">
444 <div class="ka-hf-create-rule-row">
445 <select id="ka-hf-display-rule" name="ka_hf_display_rule" class="ka-hf-form-select">
446 <?php foreach (self::$location_selection as $group_data) : ?>
447 <optgroup label="<?php echo esc_attr($group_data['label']); ?>">
448 <?php foreach ($group_data['value'] as $opt_key => $opt_value) : ?>
449 <option value="<?php echo esc_attr($opt_key); ?>" <?php selected($opt_key, 'basic-global'); ?>><?php echo esc_html($opt_value); ?></option>
450 <?php endforeach; ?>
451 </optgroup>
452 <?php endforeach; ?>
453 </select>
454 <input type="text" id="ka-hf-display-specific" name="ka_hf_display_specific" class="ka-hf-modal-input ka-hf-specific-input" placeholder="<?php echo esc_attr__('Enter page/post IDs (comma separated)', 'king-addons'); ?>" style="display: none;" />
455 </div>
456 </div>
457 </div>
458
459 <div class="ka-hf-form-group">
460 <label class="ka-hf-form-label"><?php echo esc_html__('User Roles (Optional)', 'king-addons'); ?></label>
461 <p class="ka-hf-form-desc"><?php echo esc_html__('Display template for specific user roles.', 'king-addons'); ?></p>
462 <div id="ka-hf-create-user-roles" class="ka-hf-rules-container">
463 <!-- Role rows will be added dynamically via JS -->
464 </div>
465 <button type="button" class="ka-hf-add-rule-btn" id="ka-hf-create-add-user-role">
466 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M12 5v14M5 12h14"/></svg>
467 <?php echo esc_html__('Add User Role', 'king-addons'); ?>
468 </button>
469 </div>
470
471 <div class="ka-hf-form-group">
472 <label class="ka-hf-form-label">
473 <input type="checkbox" id="ka-hf-display-canvas" name="ka_hf_display_canvas" value="1" />
474 <?php echo esc_html__('Enable for Elementor Canvas Template', 'king-addons'); ?>
475 </label>
476 <p class="ka-hf-form-desc"><?php echo esc_html__('Show this template on pages using Elementor Canvas Template', 'king-addons'); ?></p>
477 </div>
478
479 <div class="ka-hf-modal-actions">
480 <button type="button" class="ka-hf-btn ka-hf-btn-secondary ka-hf-modal-close"><?php echo esc_html__('Cancel', 'king-addons'); ?></button>
481 <button type="submit" class="ka-hf-btn ka-hf-btn-primary"><?php echo esc_html__('Create and Edit with Elementor', 'king-addons'); ?></button>
482 </div>
483 </form>
484 </div>
485 </div>
486 <?php
487 }
488
489 /**
490 * Render the Rename Template modal.
491 *
492 * @return void
493 */
494 private function renderRenameModal(): void
495 {
496 ?>
497 <div id="ka-hf-rename-modal" class="ka-hf-modal-overlay" aria-hidden="true">
498 <div class="ka-hf-modal" role="dialog" aria-modal="true" aria-labelledby="ka-hf-rename-title">
499 <h3 id="ka-hf-rename-title"><?php echo esc_html__('Rename Template', 'king-addons'); ?></h3>
500 <p class="ka-hf-modal-desc"><?php echo esc_html__('Enter a new name for this template.', 'king-addons'); ?></p>
501
502 <input type="hidden" id="ka-hf-rename-id" value="" />
503 <div class="ka-hf-form-group">
504 <label class="ka-hf-form-label" for="ka-hf-rename-title-input"><?php echo esc_html__('Template Name', 'king-addons'); ?></label>
505 <input type="text" id="ka-hf-rename-title-input" class="ka-hf-modal-input" value="" />
506 </div>
507
508 <div class="ka-hf-modal-actions">
509 <button type="button" class="ka-hf-btn ka-hf-btn-secondary ka-hf-rename-close"><?php echo esc_html__('Cancel', 'king-addons'); ?></button>
510 <button type="button" class="ka-hf-btn ka-hf-btn-primary" id="ka-hf-rename-save"><?php echo esc_html__('Save', 'king-addons'); ?></button>
511 </div>
512 </div>
513 </div>
514 <?php
515 }
516
517 /**
518 * Render the Conditions Popup for editing existing templates.
519 *
520 * @return void
521 */
522 private function renderConditionsPopup(): void
523 {
524 self::$location_selection = self::getLocationSelections();
525 self::$user_selection = self::get_user_selections();
526
527 // Prepare location options JSON for JS
528 $location_options = [];
529 foreach (self::$location_selection as $group_key => $group_data) {
530 foreach ($group_data['value'] as $opt_key => $opt_value) {
531 $location_options[$opt_key] = $opt_value;
532 }
533 }
534
535 $user_options = [];
536 foreach (self::$user_selection as $group_data) {
537 foreach ($group_data['value'] as $opt_key => $opt_value) {
538 $user_options[$opt_key] = $opt_value;
539 }
540 }
541 ?>
542 <div id="ka-hf-conditions-modal" class="ka-hf-modal-overlay" aria-hidden="true">
543 <div class="ka-hf-modal ka-hf-modal-conditions" role="dialog" aria-modal="true" aria-labelledby="ka-hf-conditions-title">
544 <h3 id="ka-hf-conditions-title"><?php echo esc_html__('Template Settings', 'king-addons'); ?></h3>
545 <p class="ka-hf-modal-desc"><?php echo esc_html__('Configure template type and display conditions.', 'king-addons'); ?></p>
546
547 <input type="hidden" id="ka-hf-cond-template-id" value="" />
548
549 <!-- Template Type Section -->
550 <div class="ka-hf-form-group">
551 <label class="ka-hf-form-label"><?php echo esc_html__('Template Type', 'king-addons'); ?></label>
552 <select id="ka-hf-cond-template-type" class="ka-hf-form-select">
553 <option value="king_addons_el_hf_type_header"><?php echo esc_html__('Header', 'king-addons'); ?></option>
554 <option value="king_addons_el_hf_type_footer"><?php echo esc_html__('Footer', 'king-addons'); ?></option>
555 </select>
556 </div>
557
558 <!-- Include Rules Section -->
559 <div class="ka-hf-form-group">
560 <label class="ka-hf-form-label"><?php echo esc_html__('Display On', 'king-addons'); ?></label>
561 <div id="ka-hf-include-rules" class="ka-hf-rules-container">
562 <!-- Rules will be added dynamically via JS -->
563 </div>
564 <button type="button" class="ka-hf-add-rule-btn" data-rule-type="include">
565 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M12 5v14M5 12h14"/></svg>
566 <?php echo esc_html__('Add Display Rule', 'king-addons'); ?>
567 </button>
568 </div>
569
570 <!-- Exclude Rules Section -->
571 <div class="ka-hf-form-group">
572 <label class="ka-hf-form-label"><?php echo esc_html__('Do Not Display On', 'king-addons'); ?></label>
573 <div id="ka-hf-exclude-rules" class="ka-hf-rules-container">
574 <!-- Exclusion rules will be added dynamically via JS -->
575 </div>
576 <button type="button" class="ka-hf-add-rule-btn" data-rule-type="exclude">
577 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M12 5v14M5 12h14"/></svg>
578 <?php echo esc_html__('Add Exclusion Rule', 'king-addons'); ?>
579 </button>
580 </div>
581
582 <!-- User Roles Section -->
583 <div class="ka-hf-form-group">
584 <label class="ka-hf-form-label"><?php echo esc_html__('User Roles (Optional)', 'king-addons'); ?></label>
585 <div id="ka-hf-user-roles" class="ka-hf-rules-container">
586 <!-- User role rows will be added dynamically via JS -->
587 </div>
588 <button type="button" class="ka-hf-add-rule-btn" id="ka-hf-add-user-role">
589 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M12 5v14M5 12h14"/></svg>
590 <?php echo esc_html__('Add User Role', 'king-addons'); ?>
591 </button>
592 </div>
593
594 <div class="ka-hf-modal-actions">
595 <button type="button" class="ka-hf-btn ka-hf-btn-secondary ka-hf-conditions-close"><?php echo esc_html__('Cancel', 'king-addons'); ?></button>
596 <button type="button" id="ka-hf-save-conditions" class="ka-hf-btn ka-hf-btn-primary"><?php echo esc_html__('Save Conditions', 'king-addons'); ?></button>
597 </div>
598
599 <div id="ka-hf-conditions-saving" class="ka-hf-saving-overlay" style="display: none;">
600 <span class="ka-hf-spinner"></span>
601 <?php echo esc_html__('Saving...', 'king-addons'); ?>
602 </div>
603 </div>
604 </div>
605
606 <!-- Rule template for JS cloning -->
607 <template id="ka-hf-rule-template">
608 <div class="ka-hf-rule-row">
609 <select class="ka-hf-form-select ka-hf-rule-select">
610 <?php foreach (self::$location_selection as $group_data) : ?>
611 <optgroup label="<?php echo esc_attr($group_data['label']); ?>">
612 <?php foreach ($group_data['value'] as $opt_key => $opt_value) : ?>
613 <option value="<?php echo esc_attr($opt_key); ?>"><?php echo esc_html($opt_value); ?></option>
614 <?php endforeach; ?>
615 </optgroup>
616 <?php endforeach; ?>
617 </select>
618 <input type="text" class="ka-hf-modal-input ka-hf-specific-input" placeholder="<?php echo esc_attr__('Specific IDs (comma separated)', 'king-addons'); ?>" style="display: none;" />
619 <button type="button" class="ka-hf-remove-rule-btn" title="<?php echo esc_attr__('Remove rule', 'king-addons'); ?>">
620 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M18 6L6 18M6 6l12 12"/></svg>
621 </button>
622 </div>
623 </template>
624
625 <!-- User role template for JS cloning -->
626 <template id="ka-hf-user-role-template">
627 <div class="ka-hf-rule-row ka-hf-user-role-row">
628 <select class="ka-hf-form-select ka-hf-user-role-select">
629 <?php foreach (self::$user_selection as $group_data) : ?>
630 <optgroup label="<?php echo esc_attr($group_data['label']); ?>">
631 <?php foreach ($group_data['value'] as $opt_key => $opt_value) : ?>
632 <option value="<?php echo esc_attr($opt_key); ?>"><?php echo esc_html($opt_value); ?></option>
633 <?php endforeach; ?>
634 </optgroup>
635 <?php endforeach; ?>
636 </select>
637 <button type="button" class="ka-hf-remove-rule-btn" title="<?php echo esc_attr__('Remove role', 'king-addons'); ?>">
638 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18"><path d="M18 6L6 18M6 6l12 12"/></svg>
639 </button>
640 </div>
641 </template>
642
643 <script>
644 var kaHfLocationOptions = <?php echo wp_json_encode($location_options); ?>;
645 var kaHfUserOptions = <?php echo wp_json_encode($user_options); ?>;
646 var kaHfAjaxUrl = <?php echo wp_json_encode(admin_url('admin-ajax.php')); ?>;
647 var kaHfNonce = <?php echo wp_json_encode(wp_create_nonce('ka_hf_save_conditions')); ?>;
648 </script>
649 <?php
650 }
651
652 /**
653 * AJAX handler to save conditions for a template.
654 *
655 * @return void
656 */
657 public function handleAjaxSaveConditions(): void
658 {
659 if (!current_user_can('manage_options')) {
660 wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')]);
661 }
662
663 if (!check_ajax_referer('ka_hf_save_conditions', 'nonce', false)) {
664 wp_send_json_error(['message' => esc_html__('Invalid nonce.', 'king-addons')]);
665 }
666
667 $template_id = isset($_POST['template_id']) ? (int) $_POST['template_id'] : 0;
668 if (!$template_id || 'king-addons-el-hf' !== get_post_type($template_id)) {
669 wp_send_json_error(['message' => esc_html__('Invalid template.', 'king-addons')]);
670 }
671
672 // Parse include rules
673 $include_rules = [];
674 $include_specific = [];
675 if (!empty($_POST['include_rules']) && is_array($_POST['include_rules'])) {
676 foreach ($_POST['include_rules'] as $rule) {
677 $rule_val = sanitize_text_field(wp_unslash($rule['rule'] ?? ''));
678 if ($rule_val) {
679 $include_rules[] = $rule_val;
680 if ('specifics' === $rule_val && !empty($rule['specific'])) {
681 $specific_ids = array_map('intval', array_filter(explode(',', sanitize_text_field(wp_unslash($rule['specific'])))));
682 $include_specific = array_merge($include_specific, $specific_ids);
683 }
684 }
685 }
686 }
687
688 // Parse exclude rules
689 $exclude_rules = [];
690 $exclude_specific = [];
691 if (!empty($_POST['exclude_rules']) && is_array($_POST['exclude_rules'])) {
692 foreach ($_POST['exclude_rules'] as $rule) {
693 $rule_val = sanitize_text_field(wp_unslash($rule['rule'] ?? ''));
694 if ($rule_val) {
695 $exclude_rules[] = $rule_val;
696 if ('specifics' === $rule_val && !empty($rule['specific'])) {
697 $specific_ids = array_map('intval', array_filter(explode(',', sanitize_text_field(wp_unslash($rule['specific'])))));
698 $exclude_specific = array_merge($exclude_specific, $specific_ids);
699 }
700 }
701 }
702 }
703
704 // Parse user roles (multiple)
705 $user_roles = [];
706 if (!empty($_POST['user_roles']) && is_array($_POST['user_roles'])) {
707 foreach ($_POST['user_roles'] as $role) {
708 $role_val = sanitize_text_field(wp_unslash($role));
709 if ($role_val) {
710 $user_roles[] = $role_val;
711 }
712 }
713 }
714 if (empty($user_roles)) {
715 $user_roles = ['all'];
716 }
717 $user_roles = array_values(array_unique($user_roles));
718 if (in_array('all', $user_roles, true)) {
719 $user_roles = ['all'];
720 }
721
722 // Parse template type
723 $template_type = isset($_POST['template_type']) ? sanitize_text_field(wp_unslash($_POST['template_type'])) : '';
724 if (!empty($template_type) && in_array($template_type, ['king_addons_el_hf_type_header', 'king_addons_el_hf_type_footer'], true)) {
725 update_post_meta($template_id, 'king_addons_el_hf_template_type', $template_type);
726 }
727
728 // Save meta
729 $include_locations = [
730 'rule' => $include_rules,
731 'specific' => $include_specific,
732 ];
733 $exclude_locations = [
734 'rule' => $exclude_rules,
735 'specific' => $exclude_specific,
736 ];
737
738 update_post_meta($template_id, 'king_addons_el_hf_target_include_locations', $include_locations);
739 update_post_meta($template_id, 'king_addons_el_hf_target_exclude_locations', $exclude_locations);
740 update_post_meta($template_id, 'king_addons_el_hf_target_user_roles', $user_roles);
741
742 wp_send_json_success(['message' => esc_html__('Settings saved successfully.', 'king-addons')]);
743 }
744
745 /**
746 * AJAX handler to rename a template.
747 *
748 * @return void
749 */
750 public function handleAjaxRenameTemplate(): void
751 {
752 if (!current_user_can('manage_options')) {
753 wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')]);
754 }
755
756 if (!check_ajax_referer('ka_hf_save_conditions', 'nonce', false)) {
757 wp_send_json_error(['message' => esc_html__('Invalid nonce.', 'king-addons')]);
758 }
759
760 $template_id = isset($_POST['template_id']) ? (int) $_POST['template_id'] : 0;
761 $new_title = isset($_POST['new_title']) ? sanitize_text_field(wp_unslash($_POST['new_title'])) : '';
762
763 if (!$template_id || 'king-addons-el-hf' !== get_post_type($template_id)) {
764 wp_send_json_error(['message' => esc_html__('Invalid template.', 'king-addons')]);
765 }
766
767 if (empty($new_title)) {
768 wp_send_json_error(['message' => esc_html__('Title cannot be empty.', 'king-addons')]);
769 }
770
771 $result = wp_update_post([
772 'ID' => $template_id,
773 'post_title' => $new_title,
774 ]);
775
776 if (is_wp_error($result)) {
777 wp_send_json_error(['message' => esc_html__('Failed to rename template.', 'king-addons')]);
778 }
779
780 wp_send_json_success(['message' => esc_html__('Template renamed successfully.', 'king-addons')]);
781 }
782
783 /**
784 * AJAX handler to toggle template status (publish/draft).
785 *
786 * @return void
787 */
788 public function handleAjaxToggleTemplateStatus(): void
789 {
790 if (!current_user_can('manage_options')) {
791 wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')]);
792 }
793
794 if (!check_ajax_referer('ka_hf_save_conditions', 'nonce', false)) {
795 wp_send_json_error(['message' => esc_html__('Invalid nonce.', 'king-addons')]);
796 }
797
798 $template_id = isset($_POST['template_id']) ? (int) $_POST['template_id'] : 0;
799 $new_status = isset($_POST['new_status']) ? sanitize_text_field(wp_unslash($_POST['new_status'])) : '';
800
801 if (!$template_id || 'king-addons-el-hf' !== get_post_type($template_id)) {
802 wp_send_json_error(['message' => esc_html__('Invalid template.', 'king-addons')]);
803 }
804
805 if (!in_array($new_status, ['publish', 'draft'], true)) {
806 wp_send_json_error(['message' => esc_html__('Invalid status.', 'king-addons')]);
807 }
808
809 $result = wp_update_post([
810 'ID' => $template_id,
811 'post_status' => $new_status,
812 ]);
813
814 if (is_wp_error($result)) {
815 wp_send_json_error(['message' => esc_html__('Failed to update template status.', 'king-addons')]);
816 }
817
818 wp_send_json_success(['message' => esc_html__('Template status updated.', 'king-addons')]);
819 }
820
821 /**
822 * Handle "Add New" template submission.
823 *
824 * @return void
825 */
826 public function handleCreateTemplate(): void
827 {
828 if (!current_user_can('manage_options')) {
829 wp_die(esc_html__('You do not have permission to perform this action.', 'king-addons'));
830 }
831
832 if (!isset($_POST['ka_hf_builder_create_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['ka_hf_builder_create_nonce'])), 'ka_hf_builder_create')) {
833 wp_die(esc_html__('Invalid nonce.', 'king-addons'));
834 }
835
836 $title = isset($_POST['ka_hf_title']) ? sanitize_text_field(wp_unslash($_POST['ka_hf_title'])) : esc_html__('My Template', 'king-addons');
837 $type = isset($_POST['ka_hf_type']) ? sanitize_text_field(wp_unslash($_POST['ka_hf_type'])) : 'king_addons_el_hf_type_header';
838 $display_rule = isset($_POST['ka_hf_display_rule']) ? sanitize_text_field(wp_unslash($_POST['ka_hf_display_rule'])) : 'basic-global';
839 $display_specific = isset($_POST['ka_hf_display_specific']) ? sanitize_text_field(wp_unslash($_POST['ka_hf_display_specific'])) : '';
840 $display_canvas = isset($_POST['ka_hf_display_canvas']) ? '1' : '';
841
842 // Parse user roles (multiple selection)
843 $user_roles = [];
844 if (!empty($_POST['ka_hf_user_role']) && is_array($_POST['ka_hf_user_role'])) {
845 foreach ($_POST['ka_hf_user_role'] as $role) {
846 $role_val = sanitize_text_field(wp_unslash($role));
847 if ($role_val) {
848 $user_roles[] = $role_val;
849 }
850 }
851 }
852 if (empty($user_roles)) {
853 $user_roles = ['all'];
854 }
855 $user_roles = array_values(array_unique($user_roles));
856 if (in_array('all', $user_roles, true)) {
857 $user_roles = ['all'];
858 }
859
860 // Validate type
861 if (!in_array($type, ['king_addons_el_hf_type_header', 'king_addons_el_hf_type_footer'], true)) {
862 $type = 'king_addons_el_hf_type_header';
863 }
864
865 // Create the post
866 $post_id = wp_insert_post([
867 'post_type' => 'king-addons-el-hf',
868 'post_status' => 'publish',
869 'post_title' => $title,
870 ]);
871
872 if (is_wp_error($post_id)) {
873 wp_die(esc_html__('Unable to create template.', 'king-addons'));
874 }
875
876 // Save template type immediately
877 update_post_meta($post_id, 'king_addons_el_hf_template_type', $type);
878
879 // Save display conditions
880 $specific_ids = [];
881 if ('specifics' === $display_rule && !empty($display_specific)) {
882 $specific_ids = array_map('intval', array_filter(explode(',', $display_specific)));
883 }
884 $target_locations = [
885 'rule' => [$display_rule],
886 'specific' => $specific_ids,
887 ];
888 update_post_meta($post_id, 'king_addons_el_hf_target_include_locations', $target_locations);
889 update_post_meta($post_id, 'king_addons_el_hf_target_exclude_locations', []);
890
891 // Save user roles (multiple)
892 update_post_meta($post_id, 'king_addons_el_hf_target_user_roles', $user_roles);
893
894 // Save canvas display option
895 if ($display_canvas) {
896 update_post_meta($post_id, 'king-addons-el-hf-display-on-canvas', '1');
897 }
898
899 // Redirect to Elementor editor
900 wp_safe_redirect(admin_url('post.php?post=' . $post_id . '&action=elementor'));
901 exit;
902 }
903
904 /**
905 * Handle quick update submission.
906 *
907 * @return void
908 */
909 public function handleQuickUpdate(): void
910 {
911 if (!current_user_can('manage_options')) {
912 wp_die(esc_html__('You do not have permission to perform this action.', 'king-addons'));
913 }
914
915 if (!isset($_POST['ka_hf_quick_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['ka_hf_quick_nonce'])), 'ka_hf_quick')) {
916 wp_die(esc_html__('Invalid nonce.', 'king-addons'));
917 }
918
919 $template_id = isset($_POST['template_id']) ? (int) $_POST['template_id'] : 0;
920 $type = isset($_POST['type']) ? sanitize_text_field(wp_unslash($_POST['type'])) : '';
921
922 if ($template_id > 0 && !empty($type)) {
923 update_post_meta($template_id, 'king_addons_el_hf_template_type', $type);
924 }
925
926 wp_safe_redirect(admin_url('admin.php?page=' . $this->menu_slug));
927 exit;
928 }
929
930 /**
931 * Handle inline toggle/delete actions.
932 *
933 * @return void
934 */
935 private function handleInlineActions(): void
936 {
937 $action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
938 $template_id = isset($_GET['template_id']) ? (int) $_GET['template_id'] : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
939
940 if (!$template_id || empty($action)) {
941 return;
942 }
943
944 if ('delete_template' === $action) {
945 if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'] ?? '')), 'ka_hf_delete_' . $template_id)) {
946 return;
947 }
948 wp_trash_post($template_id);
949 }
950 }
951
952 /**
953 * Prepare data for admin list table.
954 *
955 * @return array
956 */
957 private function prepareAdminTemplates(): array
958 {
959 $args = [
960 'post_type' => 'king-addons-el-hf',
961 'post_status' => ['publish', 'draft'],
962 'posts_per_page' => -1,
963 'orderby' => 'date',
964 'order' => 'DESC',
965 ];
966
967 $query = new WP_Query($args);
968 $templates = [];
969
970 if ($query->have_posts()) {
971 while ($query->have_posts()) {
972 $query->the_post();
973 $post_id = get_the_ID();
974 $templates[] = [
975 'id' => $post_id,
976 'title' => get_the_title(),
977 'type' => get_post_meta($post_id, 'king_addons_el_hf_template_type', true),
978 'include_locations' => get_post_meta($post_id, 'king_addons_el_hf_target_include_locations', true),
979 'exclude_locations' => get_post_meta($post_id, 'king_addons_el_hf_target_exclude_locations', true),
980 'user_roles' => get_post_meta($post_id, 'king_addons_el_hf_target_user_roles', true),
981 ];
982 }
983 }
984 wp_reset_postdata();
985
986 return $templates;
987 }
988
989 /**
990 * Summarize conditions to a short label for admin list.
991 *
992 * @param array $template Template data.
993 *
994 * @return string
995 */
996 private function summarizeConditions(array $template): string
997 {
998 $locations = $template['include_locations'] ?? [];
999
1000 if (empty($locations) || empty($locations['rule'])) {
1001 return esc_html__('All', 'king-addons');
1002 }
1003
1004 $rules = $locations['rule'];
1005 $count = is_array($rules) ? count($rules) : 0;
1006
1007 if ($count === 0) {
1008 return esc_html__('All', 'king-addons');
1009 }
1010
1011 // Get the first rule label
1012 $first_rule = $rules[0] ?? '';
1013 $label = self::getLocation($first_rule);
1014
1015 if ($count > 1) {
1016 return sprintf(
1017 esc_html__('%s + %d more', 'king-addons'),
1018 $label,
1019 $count - 1
1020 );
1021 }
1022
1023 return $label ?: esc_html__('All', 'king-addons');
1024 }
1025
1026 /**
1027 * SVG icon for template type.
1028 *
1029 * @param string $type_slug Template type slug.
1030 *
1031 * @return string
1032 */
1033 private function getTypeIconSvg(string $type_slug): string
1034 {
1035 switch ($type_slug) {
1036 case 'header':
1037 return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="3" width="18" height="6" rx="1"/><path d="M3 13h18M3 17h10"/></svg>';
1038 case 'footer':
1039 return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="15" width="18" height="6" rx="1"/><path d="M3 7h18M3 11h10"/></svg>';
1040 default:
1041 return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M3 3h18v6H3zM3 15h18v6H3z"/></svg>';
1042 }
1043 }
1044
1045 /**
1046 * Render modern CSS styles for admin page.
1047 *
1048 * @return void
1049 */
1050 private function renderModernStyles(): void
1051 {
1052 ?>
1053 <style>
1054 /* ================================================
1055 Header & Footer Builder - Premium Admin Design
1056 ================================================ */
1057
1058 :root {
1059 --ka-hf-font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
1060 --ka-hf-bg: #f5f5f7;
1061 --ka-hf-surface: #ffffff;
1062 --ka-hf-text: #1d1d1f;
1063 --ka-hf-text-secondary: #86868b;
1064 --ka-hf-border: rgba(0, 0, 0, 0.06);
1065 --ka-hf-accent: #0071e3;
1066 --ka-hf-accent-hover: #0077ed;
1067 --ka-hf-radius: 20px;
1068 --ka-hf-radius-sm: 12px;
1069 --ka-hf-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
1070 --ka-hf-shadow-hover: 0 8px 32px rgba(0, 0, 0, 0.10);
1071 --ka-hf-transition: all 0.3s cubic-bezier(0.25, 1, 0.5, 1);
1072 }
1073
1074 /* Dark theme support */
1075 body.ka-v3-dark {
1076 --ka-hf-bg: #1c1c1e;
1077 --ka-hf-surface: #2c2c2e;
1078 --ka-hf-text: #f5f5f7;
1079 --ka-hf-text-secondary: #98989d;
1080 --ka-hf-border: rgba(255, 255, 255, 0.1);
1081 --ka-hf-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
1082 --ka-hf-shadow-hover: 0 8px 32px rgba(0, 0, 0, 0.4);
1083 }
1084
1085 body.wp-admin #wpcontent,
1086 body.wp-admin #wpbody,
1087 body.wp-admin #wpbody-content {
1088 background: var(--ka-hf-bg) !important;
1089 padding: 0 !important;
1090 }
1091
1092 .ka-hf {
1093 font-family: var(--ka-hf-font);
1094 max-width: 1100px;
1095 margin: 0 auto;
1096 padding: 48px 40px 80px;
1097 color: var(--ka-hf-text);
1098 -webkit-font-smoothing: antialiased;
1099 }
1100 .ka-hf * { box-sizing: border-box; }
1101
1102 /* Header */
1103 .ka-hf-header {
1104 display: flex;
1105 justify-content: space-between;
1106 align-items: flex-start;
1107 margin-bottom: 40px;
1108 gap: 20px;
1109 }
1110 .ka-hf-header-content {
1111 display: flex;
1112 align-items: flex-start;
1113 gap: 16px;
1114 }
1115
1116 .ka-hf-header-titles {
1117 display: flex;
1118 flex-direction: column;
1119 gap: 8px;
1120 min-width: 0;
1121 }
1122
1123 .ka-hf-header-titles h1 {
1124 font-size: 48px;
1125 font-weight: 700;
1126 letter-spacing: -0.025em;
1127 margin: 0;
1128 line-height: 1;
1129 }
1130 .ka-hf-header-titles p {
1131 font-size: 21px;
1132 color: var(--ka-hf-text-secondary);
1133 margin: 0;
1134 font-weight: 400;
1135 }
1136 .ka-hf-title-icon {
1137 width: 76px;
1138 height: 76px;
1139 display: inline-flex;
1140 align-items: center;
1141 justify-content: center;
1142 border-radius: 22px;
1143 background: rgba(10, 132, 255, 0.18) !important;
1144 color: #0a84ff !important;
1145 flex: 0 0 auto;
1146 }
1147 .ka-hf-title-icon svg { width: 36px; height: 36px; }
1148 .ka-hf-title-text {
1149 background: linear-gradient(135deg, var(--ka-hf-text) 0%, var(--ka-hf-text-secondary) 100%);
1150 -webkit-background-clip: text;
1151 -webkit-text-fill-color: transparent;
1152 background-clip: text;
1153 }
1154 .ka-hf-header-actions {
1155 display: flex;
1156 align-items: center;
1157 gap: 12px;
1158 flex-wrap: wrap;
1159 justify-content: flex-end;
1160 }
1161
1162 /* Buttons */
1163 .ka-hf-btn {
1164 display: inline-flex;
1165 align-items: center;
1166 justify-content: center;
1167 gap: 8px;
1168 padding: 10px 18px;
1169 font-size: 14px;
1170 font-weight: 500;
1171 text-decoration: none;
1172 border-radius: 980px;
1173 border: none;
1174 cursor: pointer;
1175 transition: var(--ka-hf-transition);
1176 white-space: nowrap;
1177 font-family: inherit;
1178 }
1179 .ka-hf-btn-icon {
1180 font-size: 16px;
1181 line-height: 1;
1182 }
1183 .ka-hf-btn-primary {
1184 background: var(--ka-hf-accent);
1185 color: #fff;
1186 }
1187 .ka-hf-btn-primary:hover {
1188 background: var(--ka-hf-accent-hover);
1189 color: #fff;
1190 transform: scale(1.02);
1191 }
1192 .ka-hf-btn-secondary {
1193 background: rgba(0, 0, 0, 0.04);
1194 color: var(--ka-hf-text);
1195 }
1196 .ka-hf-btn-secondary:hover {
1197 background: rgba(0, 0, 0, 0.08);
1198 color: var(--ka-hf-text);
1199 }
1200
1201 /* Template Types - Compact Cards */
1202 .ka-hf-types {
1203 display: grid;
1204 grid-template-columns: repeat(2, 1fr);
1205 gap: 12px;
1206 margin-bottom: 36px;
1207 max-width: 480px;
1208 }
1209 .ka-hf-type {
1210 position: relative;
1211 display: flex;
1212 flex-direction: column;
1213 align-items: center;
1214 padding: 20px 16px;
1215 background: var(--ka-hf-surface);
1216 border: 1px solid var(--ka-hf-border);
1217 border-radius: var(--ka-hf-radius-sm);
1218 color: var(--ka-hf-text);
1219 transition: var(--ka-hf-transition);
1220 cursor: pointer;
1221 overflow: hidden;
1222 }
1223 .ka-hf-type:hover {
1224 transform: translateY(-2px);
1225 box-shadow: var(--ka-hf-shadow-hover);
1226 border-color: var(--ka-hf-accent);
1227 }
1228 .ka-hf-type-icon {
1229 width: 36px;
1230 height: 36px;
1231 margin-bottom: 10px;
1232 color: var(--ka-hf-text-secondary);
1233 transition: var(--ka-hf-transition);
1234 }
1235 .ka-hf-type:hover .ka-hf-type-icon {
1236 transform: scale(1.1);
1237 color: var(--ka-hf-accent);
1238 }
1239 .ka-hf-type-icon svg { width: 100%; height: 100%; }
1240 .ka-hf-type-label {
1241 font-size: 15px;
1242 font-weight: 600;
1243 margin-bottom: 2px;
1244 text-align: center;
1245 }
1246 .ka-hf-type-desc {
1247 font-size: 12px;
1248 color: var(--ka-hf-text-secondary);
1249 text-align: center;
1250 }
1251
1252 /* Section */
1253 .ka-hf-section { margin-bottom: 48px; }
1254 .ka-hf-section-header {
1255 display: flex;
1256 justify-content: space-between;
1257 align-items: center;
1258 margin-bottom: 18px;
1259 gap: 12px;
1260 }
1261 .ka-hf-section-title {
1262 font-size: 28px;
1263 font-weight: 600;
1264 letter-spacing: -0.01em;
1265 margin: 0;
1266 }
1267 .ka-hf-section-actions {
1268 display: flex;
1269 align-items: center;
1270 gap: 12px;
1271 }
1272 .ka-hf-section-count {
1273 font-size: 15px;
1274 color: var(--ka-hf-text-secondary);
1275 background: var(--ka-hf-surface);
1276 padding: 6px 14px;
1277 border-radius: 20px;
1278 border: 1px solid var(--ka-hf-border);
1279 white-space: nowrap;
1280 }
1281
1282 /* Filters */
1283 .ka-hf-filters {
1284 display: inline-flex;
1285 align-items: center;
1286 gap: 6px;
1287 padding: 6px;
1288 border-radius: 980px;
1289 border: 1px solid var(--ka-hf-border);
1290 background: var(--ka-hf-surface);
1291 }
1292 .ka-hf-filter {
1293 display: inline-flex;
1294 align-items: center;
1295 justify-content: center;
1296 padding: 8px 12px;
1297 border-radius: 980px;
1298 text-decoration: none;
1299 font-size: 13px;
1300 font-weight: 600;
1301 color: var(--ka-hf-text-secondary);
1302 transition: var(--ka-hf-transition);
1303 background: transparent;
1304 border: none;
1305 cursor: pointer;
1306 font-family: inherit;
1307 }
1308 .ka-hf-filter.is-active {
1309 background: rgba(0, 113, 227, 0.12);
1310 color: var(--ka-hf-accent);
1311 }
1312 .ka-hf-filter:hover {
1313 background: rgba(0, 0, 0, 0.04);
1314 color: var(--ka-hf-text);
1315 }
1316
1317 /* Templates List */
1318 .ka-hf-templates {
1319 background: var(--ka-hf-surface);
1320 border-radius: var(--ka-hf-radius);
1321 border: 1px solid var(--ka-hf-border);
1322 overflow: visible;
1323 }
1324 .ka-hf-template {
1325 display: grid;
1326 grid-template-columns: auto 1fr auto auto;
1327 align-items: center;
1328 gap: 20px;
1329 padding: 20px 24px;
1330 border-bottom: 1px solid var(--ka-hf-border);
1331 transition: var(--ka-hf-transition);
1332 }
1333 .ka-hf-template:last-child { border-bottom: none; }
1334 .ka-hf-template:hover { background: rgba(0, 113, 227, 0.03); }
1335
1336 .ka-hf-template-info {
1337 display: flex;
1338 flex-direction: column;
1339 gap: 4px;
1340 min-width: 0;
1341 }
1342 .ka-hf-template-title {
1343 font-size: 16px;
1344 font-weight: 500;
1345 color: var(--ka-hf-text);
1346 text-decoration: none;
1347 display: inline-flex;
1348 align-items: center;
1349 gap: 8px;
1350 min-width: 0;
1351 transition: var(--ka-hf-transition);
1352 }
1353 .ka-hf-template-title-icon {
1354 width: 18px;
1355 height: 18px;
1356 display: inline-flex;
1357 align-items: center;
1358 justify-content: center;
1359 color: var(--ka-hf-text-secondary);
1360 flex: 0 0 auto;
1361 }
1362 .ka-hf-template-title-icon svg { width: 18px; height: 18px; }
1363 .ka-hf-template-title-text {
1364 min-width: 0;
1365 white-space: nowrap;
1366 overflow: hidden;
1367 text-overflow: ellipsis;
1368 }
1369 .ka-hf-template-title:hover { color: var(--ka-hf-accent); }
1370 .ka-hf-template-meta {
1371 display: flex;
1372 align-items: center;
1373 gap: 12px;
1374 font-size: 13px;
1375 color: var(--ka-hf-text-secondary);
1376 flex-wrap: wrap;
1377 }
1378 .ka-hf-template-type {
1379 display: inline-flex;
1380 align-items: center;
1381 gap: 6px;
1382 }
1383 .ka-hf-template-type::before {
1384 content: '';
1385 width: 6px;
1386 height: 6px;
1387 background: var(--ka-hf-accent);
1388 border-radius: 50%;
1389 }
1390
1391 .ka-hf-template-status {
1392 display: inline-flex;
1393 align-items: center;
1394 padding: 6px 14px;
1395 font-size: 13px;
1396 font-weight: 600;
1397 border-radius: 8px;
1398 white-space: nowrap;
1399 min-width: 90px;
1400 justify-content: center;
1401 justify-self: start;
1402 }
1403 .ka-hf-template-status.is-enabled {
1404 background: rgba(52, 199, 89, 0.15);
1405 color: #30d158;
1406 }
1407 .ka-hf-template-status.is-disabled {
1408 background: rgba(255, 149, 0, 0.15);
1409 color: #ff9f0a;
1410 }
1411
1412 .ka-hf-template-condition {
1413 display: inline-flex;
1414 align-items: center;
1415 gap: 8px;
1416 padding: 10px 18px;
1417 min-height: 40px;
1418 font-size: 14px;
1419 font-weight: 500;
1420 border-radius: 980px;
1421 background: rgba(0, 113, 227, 0.10);
1422 color: var(--ka-hf-accent);
1423 max-width: 240px;
1424 overflow: hidden;
1425 text-overflow: ellipsis;
1426 white-space: nowrap;
1427 border: none;
1428 cursor: default;
1429 }
1430 .ka-hf-template-condition svg {
1431 width: 18px;
1432 height: 18px;
1433 flex-shrink: 0;
1434 opacity: 0.7;
1435 }
1436
1437 .ka-hf-template-actions {
1438 display: flex;
1439 align-items: center;
1440 gap: 12px;
1441 flex-wrap: nowrap;
1442 }
1443
1444 /* Dropdown */
1445 .ka-hf-dropdown { position: relative; }
1446 .ka-hf-dropdown-trigger {
1447 display: flex;
1448 align-items: center;
1449 justify-content: center;
1450 width: 40px;
1451 height: 40px;
1452 border-radius: 10px;
1453 background: transparent;
1454 border: 1px solid transparent;
1455 cursor: pointer;
1456 transition: var(--ka-hf-transition);
1457 color: var(--ka-hf-text-secondary);
1458 }
1459 .ka-hf-dropdown-trigger:hover {
1460 background: var(--ka-hf-surface);
1461 border-color: var(--ka-hf-border);
1462 color: var(--ka-hf-text);
1463 }
1464 .ka-hf-dropdown-trigger svg { width: 20px; height: 20px; }
1465 .ka-hf-dropdown-menu {
1466 position: absolute;
1467 top: 100%;
1468 right: 0;
1469 z-index: 100;
1470 min-width: 180px;
1471 padding: 8px 0;
1472 background: var(--ka-hf-surface);
1473 border: 1px solid var(--ka-hf-border);
1474 border-radius: var(--ka-hf-radius-sm);
1475 box-shadow: var(--ka-hf-shadow-hover);
1476 opacity: 0;
1477 visibility: hidden;
1478 transform: translateY(-8px);
1479 transition: var(--ka-hf-transition);
1480 }
1481 .ka-hf-dropdown.is-open .ka-hf-dropdown-menu {
1482 opacity: 1;
1483 visibility: visible;
1484 transform: translateY(4px);
1485 }
1486 .ka-hf-dropdown-item {
1487 display: flex;
1488 align-items: center;
1489 gap: 10px;
1490 padding: 10px 16px;
1491 font-size: 14px;
1492 color: var(--ka-hf-text);
1493 text-decoration: none;
1494 cursor: pointer;
1495 transition: var(--ka-hf-transition);
1496 background: transparent;
1497 border: none;
1498 width: 100%;
1499 text-align: left;
1500 font-family: inherit;
1501 }
1502 .ka-hf-dropdown-item:hover { background: var(--ka-hf-border); }
1503 .ka-hf-dropdown-item.is-danger { color: #ff3b30; }
1504
1505 /* Empty state */
1506 .ka-hf-empty {
1507 padding: 60px 24px;
1508 text-align: center;
1509 }
1510 .ka-hf-filter-empty {
1511 margin-top: 12px;
1512 padding: 48px 24px;
1513 text-align: center;
1514 background: var(--ka-hf-surface);
1515 border-radius: var(--ka-hf-radius);
1516 border: 1px solid var(--ka-hf-border);
1517 }
1518 .ka-hf-empty-title {
1519 margin: 0 0 8px;
1520 font-size: 20px;
1521 font-weight: 600;
1522 }
1523 .ka-hf-empty-desc {
1524 margin: 0 0 18px;
1525 color: var(--ka-hf-text-secondary);
1526 font-size: 15px;
1527 }
1528
1529 /* Modal */
1530 .ka-hf-modal-overlay {
1531 position: fixed;
1532 top: 0;
1533 left: 0;
1534 right: 0;
1535 bottom: 0;
1536 z-index: 100000;
1537 background: rgba(0, 0, 0, 0.55);
1538 backdrop-filter: blur(12px);
1539 -webkit-backdrop-filter: blur(12px);
1540 display: flex;
1541 align-items: center;
1542 justify-content: center;
1543 opacity: 0;
1544 visibility: hidden;
1545 transition: opacity 0.25s ease, visibility 0.25s ease;
1546 padding: 20px;
1547 }
1548 .ka-hf-modal-overlay.is-open {
1549 opacity: 1;
1550 visibility: visible;
1551 }
1552 .ka-hf-modal {
1553 width: 100%;
1554 max-width: 500px;
1555 max-height: 90vh;
1556 overflow-y: auto;
1557 padding: 32px 36px 28px;
1558 background: var(--ka-hf-surface);
1559 border-radius: 20px;
1560 box-shadow: 0 32px 100px rgba(0, 0, 0, 0.35);
1561 transform: scale(0.92) translateY(20px);
1562 transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease;
1563 opacity: 0;
1564 }
1565 .ka-hf-modal-overlay.is-open .ka-hf-modal {
1566 transform: scale(1) translateY(0);
1567 opacity: 1;
1568 }
1569 .ka-hf-modal h3 {
1570 font-size: 24px;
1571 font-weight: 700;
1572 margin: 0 0 4px;
1573 color: var(--ka-hf-text);
1574 letter-spacing: -0.02em;
1575 line-height: 1.2;
1576 }
1577 .ka-hf-modal-desc {
1578 font-size: 14px;
1579 color: var(--ka-hf-text-secondary);
1580 margin: 0 0 28px;
1581 line-height: 1.5;
1582 }
1583 .ka-hf-form-group {
1584 margin-bottom: 20px;
1585 }
1586 .ka-hf-form-label {
1587 display: block;
1588 font-size: 12px;
1589 font-weight: 600;
1590 color: var(--ka-hf-text-secondary);
1591 margin-bottom: 8px;
1592 text-transform: uppercase;
1593 letter-spacing: 0.6px;
1594 }
1595 .ka-hf-form-desc {
1596 font-size: 13px;
1597 color: var(--ka-hf-text-secondary);
1598 margin: 4px 0 8px;
1599 }
1600 .ka-hf-modal-input {
1601 width: 100% !important;
1602 padding: 16px 20px !important;
1603 font-size: 17px !important;
1604 font-family: inherit !important;
1605 font-weight: 400 !important;
1606 border: 2px solid rgba(0, 0, 0, 0.12) !important;
1607 border-radius: 14px !important;
1608 background: #f5f5f7 !important;
1609 color: #1d1d1f !important;
1610 transition: all 0.2s ease !important;
1611 box-shadow: none !important;
1612 -webkit-appearance: none !important;
1613 appearance: none !important;
1614 line-height: 1.4 !important;
1615 height: auto !important;
1616 margin: 0 !important;
1617 }
1618 .ka-hf-modal-input:focus {
1619 outline: none !important;
1620 border-color: #0071e3 !important;
1621 background: #ffffff !important;
1622 box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.15) !important;
1623 }
1624 .ka-hf-form-select {
1625 width: 100% !important;
1626 padding: 16px 52px 16px 20px !important;
1627 font-size: 17px !important;
1628 font-family: inherit !important;
1629 font-weight: 400 !important;
1630 border: 2px solid rgba(0, 0, 0, 0.12) !important;
1631 border-radius: 14px !important;
1632 background-color: #f5f5f7 !important;
1633 color: #1d1d1f !important;
1634 transition: all 0.2s ease !important;
1635 cursor: pointer !important;
1636 appearance: none !important;
1637 -webkit-appearance: none !important;
1638 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%231d1d1f' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important;
1639 background-repeat: no-repeat !important;
1640 background-position: right 18px center !important;
1641 background-size: 20px !important;
1642 line-height: 1.4 !important;
1643 height: auto !important;
1644 margin: 0 !important;
1645 box-shadow: none !important;
1646 }
1647 .ka-hf-form-select:focus {
1648 outline: none !important;
1649 border-color: #0071e3 !important;
1650 background-color: #ffffff !important;
1651 box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.15) !important;
1652 }
1653 /* Multi-select styles */
1654 .ka-hf-form-select.ka-hf-multi-select {
1655 background-image: none !important;
1656 padding-right: 20px !important;
1657 min-height: 120px !important;
1658 }
1659 .ka-hf-form-select.ka-hf-multi-select option {
1660 padding: 8px 12px !important;
1661 border-radius: 6px !important;
1662 margin: 2px 4px !important;
1663 }
1664 .ka-hf-form-select.ka-hf-multi-select option:checked {
1665 background: linear-gradient(0deg, rgba(0, 113, 227, 0.15) 0%, rgba(0, 113, 227, 0.15) 100%) !important;
1666 color: #0071e3 !important;
1667 }
1668 .ka-hf-form-select.ka-hf-multi-select optgroup {
1669 font-weight: 600 !important;
1670 font-style: normal !important;
1671 padding: 8px 4px 4px !important;
1672 color: var(--ka-hf-text-secondary) !important;
1673 }
1674 .ka-hf-modal-actions {
1675 display: flex;
1676 gap: 10px;
1677 margin-top: 24px;
1678 justify-content: flex-end;
1679 flex-wrap: wrap;
1680 }
1681 .ka-hf-modal-actions .ka-hf-btn {
1682 padding: 12px 20px;
1683 font-size: 14px;
1684 font-weight: 500;
1685 }
1686 .ka-hf-modal-actions .ka-hf-btn-primary {
1687 min-width: 180px;
1688 }
1689
1690 .ka-hf-conditions-wrap {
1691 margin-bottom: 8px;
1692 }
1693
1694 /* Create modal rule row */
1695 .ka-hf-create-rule-row {
1696 display: flex;
1697 flex-direction: column;
1698 gap: 10px;
1699 }
1700 .ka-hf-create-rule-row .ka-hf-form-select {
1701 flex: 1;
1702 }
1703 .ka-hf-create-rule-row .ka-hf-specific-input {
1704 margin-top: 0;
1705 }
1706
1707 /* Form spacing improvements */
1708 .ka-hf-form-group {
1709 margin-bottom: 18px;
1710 }
1711 .ka-hf-form-group:last-of-type {
1712 margin-bottom: 8px;
1713 }
1714 .ka-hf-modal-desc {
1715 margin-bottom: 24px !important;
1716 }
1717 .ka-hf-form-desc {
1718 margin: 2px 0 6px;
1719 font-size: 12px;
1720 opacity: 0.8;
1721 }
1722
1723 /* Smaller inputs */
1724 .ka-hf-modal-input,
1725 .ka-hf-form-select {
1726 padding: 12px 16px !important;
1727 font-size: 15px !important;
1728 border-radius: 12px !important;
1729 }
1730 .ka-hf-form-select {
1731 padding-right: 44px !important;
1732 background-position: right 14px center !important;
1733 }
1734
1735 /* Specific input styling */
1736 .ka-hf-specific-input {
1737 flex: 1;
1738 }
1739 .ka-hf-specific-input[style*="display: block"] {
1740 margin-top: 8px;
1741 }
1742
1743 /* Clickable condition button */
1744 .ka-hf-template-condition.ka-hf-open-conditions {
1745 cursor: pointer;
1746 transition: var(--ka-hf-transition);
1747 }
1748 .ka-hf-template-condition.ka-hf-open-conditions:hover {
1749 background: rgba(0, 113, 227, 0.18);
1750 transform: scale(1.02);
1751 }
1752
1753 /* Conditions Modal - Larger width */
1754 .ka-hf-modal.ka-hf-modal-conditions {
1755 max-width: 600px;
1756 position: relative;
1757 }
1758
1759 /* Rules container */
1760 .ka-hf-rules-container {
1761 display: flex;
1762 flex-direction: column;
1763 gap: 8px;
1764 margin-bottom: 10px;
1765 }
1766
1767 .ka-hf-rule-row {
1768 display: flex;
1769 gap: 8px;
1770 align-items: center;
1771 }
1772
1773 .ka-hf-rule-row .ka-hf-rule-select {
1774 flex: 1;
1775 min-width: 180px;
1776 }
1777
1778 .ka-hf-rule-row .ka-hf-specific-input {
1779 flex: 0 0 180px;
1780 max-width: 180px;
1781 }
1782
1783 .ka-hf-remove-rule-btn {
1784 width: 36px;
1785 height: 36px;
1786 display: flex;
1787 align-items: center;
1788 justify-content: center;
1789 background: rgba(255, 59, 48, 0.1);
1790 border: none;
1791 border-radius: 10px;
1792 color: #ff3b30;
1793 cursor: pointer;
1794 transition: var(--ka-hf-transition);
1795 flex-shrink: 0;
1796 }
1797 .ka-hf-remove-rule-btn:hover {
1798 background: rgba(255, 59, 48, 0.2);
1799 }
1800
1801 .ka-hf-add-rule-btn {
1802 display: inline-flex;
1803 align-items: center;
1804 gap: 5px;
1805 padding: 8px 14px;
1806 font-size: 12px;
1807 font-weight: 500;
1808 color: var(--ka-hf-accent);
1809 background: rgba(0, 113, 227, 0.06);
1810 border: 1px dashed rgba(0, 113, 227, 0.25);
1811 border-radius: 8px;
1812 cursor: pointer;
1813 transition: var(--ka-hf-transition);
1814 font-family: inherit;
1815 }
1816 .ka-hf-add-rule-btn:hover {
1817 background: rgba(0, 113, 227, 0.12);
1818 border-color: var(--ka-hf-accent);
1819 }
1820 .ka-hf-add-rule-btn svg {
1821 width: 14px;
1822 height: 14px;
1823 }
1824
1825 /* Saving overlay */
1826 .ka-hf-saving-overlay {
1827 position: absolute;
1828 top: 0;
1829 left: 0;
1830 right: 0;
1831 bottom: 0;
1832 background: rgba(255, 255, 255, 0.9);
1833 display: flex;
1834 flex-direction: column;
1835 align-items: center;
1836 justify-content: center;
1837 gap: 12px;
1838 border-radius: 24px;
1839 font-size: 15px;
1840 font-weight: 500;
1841 color: var(--ka-hf-text);
1842 z-index: 10;
1843 }
1844 body.ka-v3-dark .ka-hf-saving-overlay {
1845 background: rgba(44, 44, 46, 0.95);
1846 }
1847
1848 .ka-hf-spinner {
1849 width: 32px;
1850 height: 32px;
1851 border: 3px solid var(--ka-hf-border);
1852 border-top-color: var(--ka-hf-accent);
1853 border-radius: 50%;
1854 animation: ka-hf-spin 0.8s linear infinite;
1855 }
1856
1857 @keyframes ka-hf-spin {
1858 to { transform: rotate(360deg); }
1859 }
1860
1861 /* Dark theme overrides for secondary button */
1862 body.ka-v3-dark .ka-hf-btn-secondary {
1863 background: rgba(255, 255, 255, 0.08);
1864 color: var(--ka-hf-text);
1865 }
1866 body.ka-v3-dark .ka-hf-btn-secondary:hover {
1867 background: rgba(255, 255, 255, 0.14);
1868 }
1869
1870 /* Dark theme modal inputs */
1871 body.ka-v3-dark .ka-hf-modal-input,
1872 body.ka-v3-dark .ka-hf-form-select {
1873 background-color: #3a3a3c !important;
1874 border-color: rgba(255, 255, 255, 0.15) !important;
1875 color: #f5f5f7 !important;
1876 }
1877 body.ka-v3-dark .ka-hf-modal-input:focus,
1878 body.ka-v3-dark .ka-hf-form-select:focus {
1879 background-color: #48484a !important;
1880 border-color: #0071e3 !important;
1881 }
1882 body.ka-v3-dark .ka-hf-form-select {
1883 background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f7' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important;
1884 }
1885
1886 /* Dark theme modal */
1887 body.ka-v3-dark .ka-hf-modal {
1888 background: var(--ka-hf-surface);
1889 }
1890
1891 /* Responsive */
1892 @media (max-width: 782px) {
1893 .ka-hf { padding: 36px 20px 70px; }
1894 .ka-hf-header { flex-direction: column; align-items: flex-start; }
1895 .ka-hf-header-titles h1 { font-size: 36px; }
1896 .ka-hf-title-icon { width: 64px; height: 64px; border-radius: 18px; }
1897 .ka-hf-title-icon svg { width: 30px; height: 30px; }
1898 .ka-hf-template { grid-template-columns: 1fr; gap: 12px; }
1899 .ka-hf-template-actions { justify-content: flex-start; flex-wrap: wrap; }
1900 .ka-hf-nav-tabs { flex-wrap: wrap; }
1901 .ka-hf-section-header { flex-direction: column; align-items: stretch; }
1902 .ka-hf-section-actions { flex-wrap: wrap; width: 100%; }
1903 .ka-hf-filters { flex-wrap: wrap; border-radius: 16px; max-width: 100%; }
1904 }
1905
1906 /* Navigation Tabs */
1907 .ka-hf-nav-tabs {
1908 display: inline-flex;
1909 align-items: center;
1910 gap: 4px;
1911 padding: 4px;
1912 background: var(--ka-hf-surface);
1913 border: 1px solid var(--ka-hf-border);
1914 border-radius: 14px;
1915 margin-bottom: 32px;
1916 }
1917 .ka-hf-nav-tab {
1918 display: inline-flex;
1919 align-items: center;
1920 gap: 8px;
1921 padding: 12px 20px;
1922 font-size: 14px;
1923 font-weight: 500;
1924 color: var(--ka-hf-text-secondary);
1925 text-decoration: none;
1926 border-radius: 10px;
1927 transition: var(--ka-hf-transition);
1928 }
1929 .ka-hf-nav-tab svg {
1930 opacity: 0.7;
1931 transition: var(--ka-hf-transition);
1932 }
1933 .ka-hf-nav-tab:hover {
1934 color: var(--ka-hf-text);
1935 background: rgba(0, 0, 0, 0.03);
1936 }
1937 .ka-hf-nav-tab:hover svg {
1938 opacity: 1;
1939 }
1940 .ka-hf-nav-tab.is-active {
1941 background: var(--ka-hf-accent);
1942 color: #fff;
1943 }
1944 .ka-hf-nav-tab.is-active svg {
1945 opacity: 1;
1946 }
1947 body.ka-v3-dark .ka-hf-nav-tab:hover {
1948 background: rgba(255, 255, 255, 0.05);
1949 }
1950
1951 /* Settings Tab Styles */
1952 .ka-hf-settings-section {
1953 max-width: 700px;
1954 }
1955 .ka-hf-settings-form {
1956 margin: 0;
1957 }
1958 .ka-hf-settings-card {
1959 background: var(--ka-hf-surface);
1960 border: 1px solid var(--ka-hf-border);
1961 border-radius: var(--ka-hf-radius);
1962 padding: 32px;
1963 }
1964 .ka-hf-settings-card-title {
1965 font-size: 20px;
1966 font-weight: 600;
1967 margin: 0 0 8px 0;
1968 color: var(--ka-hf-text);
1969 }
1970 .ka-hf-settings-card-desc {
1971 font-size: 14px;
1972 color: var(--ka-hf-text-secondary);
1973 margin: 0 0 24px 0;
1974 line-height: 1.5;
1975 }
1976 .ka-hf-settings-options {
1977 display: flex;
1978 flex-direction: column;
1979 gap: 16px;
1980 margin-bottom: 28px;
1981 }
1982 .ka-hf-settings-option {
1983 display: flex;
1984 align-items: flex-start;
1985 gap: 14px;
1986 padding: 20px;
1987 background: rgba(0, 0, 0, 0.02);
1988 border: 1px solid var(--ka-hf-border);
1989 border-radius: var(--ka-hf-radius-sm);
1990 cursor: pointer;
1991 transition: var(--ka-hf-transition);
1992 }
1993 .ka-hf-settings-option:hover {
1994 border-color: var(--ka-hf-accent);
1995 background: rgba(0, 113, 227, 0.02);
1996 }
1997 .ka-hf-settings-option:has(input:checked) {
1998 border-color: var(--ka-hf-accent);
1999 background: rgba(0, 113, 227, 0.05);
2000 }
2001 .ka-hf-settings-option input[type="radio"] {
2002 margin: 3px 0 0 0;
2003 flex-shrink: 0;
2004 accent-color: var(--ka-hf-accent);
2005 width: 18px;
2006 height: 18px;
2007 }
2008 .ka-hf-settings-option-content {
2009 display: flex;
2010 flex-direction: column;
2011 gap: 4px;
2012 }
2013 .ka-hf-settings-option-title {
2014 font-size: 15px;
2015 font-weight: 600;
2016 color: var(--ka-hf-text);
2017 }
2018 .ka-hf-settings-option-desc {
2019 font-size: 13px;
2020 color: var(--ka-hf-text-secondary);
2021 line-height: 1.5;
2022 }
2023 .ka-hf-settings-actions {
2024 padding-top: 4px;
2025 }
2026 body.ka-v3-dark .ka-hf-settings-option {
2027 background: rgba(255, 255, 255, 0.02);
2028 }
2029 body.ka-v3-dark .ka-hf-settings-option:hover {
2030 background: rgba(0, 113, 227, 0.08);
2031 }
2032 body.ka-v3-dark .ka-hf-settings-option:has(input:checked) {
2033 background: rgba(0, 113, 227, 0.12);
2034 }
2035 </style>
2036
2037 <script>
2038 document.addEventListener('DOMContentLoaded', function() {
2039 var addNewButton = document.getElementById('ka-hf-add-new');
2040 var addNewEmptyButton = document.getElementById('ka-hf-add-new-empty');
2041 var addNewFilterEmptyButton = document.getElementById('ka-hf-add-new-filter-empty');
2042 var modal = document.getElementById('ka-hf-modal');
2043 var closeButtons = document.querySelectorAll('.ka-hf-modal-close');
2044 var typeSelect = document.getElementById('ka-hf-type');
2045 var createUserRolesContainer = document.getElementById('ka-hf-create-user-roles');
2046 var createAddUserRoleBtn = document.getElementById('ka-hf-create-add-user-role');
2047
2048 var renameModal = document.getElementById('ka-hf-rename-modal');
2049 var renameCloseButtons = document.querySelectorAll('.ka-hf-rename-close');
2050 var renameSaveBtn = document.getElementById('ka-hf-rename-save');
2051 var renameIdInput = document.getElementById('ka-hf-rename-id');
2052 var renameTitleInput = document.getElementById('ka-hf-rename-title-input');
2053
2054 var openModal = function() {
2055 if (modal) {
2056 modal.classList.add('is-open');
2057 modal.setAttribute('aria-hidden', 'false');
2058 }
2059
2060 // Ensure at least one user role row exists in create modal
2061 if (createUserRolesContainer && typeof addUserRoleRow === 'function') {
2062 if (createUserRolesContainer.children.length === 0) {
2063 addUserRoleRow(createUserRolesContainer, 'all', true);
2064 }
2065 }
2066 };
2067
2068 var closeModal = function() {
2069 if (modal) {
2070 modal.classList.remove('is-open');
2071 modal.setAttribute('aria-hidden', 'true');
2072 }
2073 };
2074
2075 if (addNewButton) {
2076 addNewButton.addEventListener('click', function(e) {
2077 e.preventDefault();
2078 openModal();
2079 });
2080 }
2081
2082 if (addNewEmptyButton) {
2083 addNewEmptyButton.addEventListener('click', function(e) {
2084 e.preventDefault();
2085 openModal();
2086 });
2087 }
2088
2089 if (addNewFilterEmptyButton) {
2090 addNewFilterEmptyButton.addEventListener('click', function(e) {
2091 e.preventDefault();
2092 openModal();
2093 });
2094 }
2095
2096 // Type cards: preselect type and open create modal
2097 var typeButtons = document.querySelectorAll('.ka-hf-type');
2098 typeButtons.forEach(function(btn) {
2099 btn.addEventListener('click', function(e) {
2100 e.preventDefault();
2101 var type = btn.getAttribute('data-ka-hf-type');
2102 if (typeSelect && type) {
2103 typeSelect.value = type;
2104 }
2105 openModal();
2106 });
2107 });
2108
2109 closeButtons.forEach(function(button) {
2110 button.addEventListener('click', closeModal);
2111 });
2112
2113 // Backdrop click closes
2114 if (modal) {
2115 modal.addEventListener('click', function(e) {
2116 if (e.target === modal) {
2117 closeModal();
2118 }
2119 });
2120 }
2121
2122 // ESC closes
2123 document.addEventListener('keydown', function(e) {
2124 if (e.key === 'Escape') {
2125 closeModal();
2126 }
2127 });
2128
2129 // Dropdown toggles
2130 var dropdowns = document.querySelectorAll('[data-ka-dropdown]');
2131 dropdowns.forEach(function(dropdown) {
2132 var trigger = dropdown.querySelector('.ka-hf-dropdown-trigger');
2133 if (trigger) {
2134 trigger.addEventListener('click', function(e) {
2135 e.stopPropagation();
2136 dropdown.classList.toggle('is-open');
2137 });
2138 }
2139 });
2140
2141 document.addEventListener('click', function() {
2142 dropdowns.forEach(function(dropdown) {
2143 dropdown.classList.remove('is-open');
2144 });
2145 });
2146
2147 // ==========================================
2148 // JS Filtering (All/Headers/Footers)
2149 // ==========================================
2150 var filterButtons = document.querySelectorAll('.ka-hf-filter[data-filter]');
2151 var templateItems = document.querySelectorAll('.ka-hf-template[data-template-type]');
2152 var templateCount = document.querySelector('.ka-hf-section-count');
2153 var filterEmptyState = document.querySelector('.ka-hf-filter-empty');
2154 var templatesList = document.querySelector('.ka-hf-templates');
2155
2156 var updateCount = function(count) {
2157 if (templateCount) {
2158 templateCount.textContent = count + ' ' + (count === 1 ? '<?php echo esc_js(__('item', 'king-addons')); ?>' : '<?php echo esc_js(__('items', 'king-addons')); ?>');
2159 }
2160 };
2161
2162 filterButtons.forEach(function(btn) {
2163 btn.addEventListener('click', function(e) {
2164 e.preventDefault();
2165 var filter = btn.getAttribute('data-filter');
2166
2167 // Update active state
2168 filterButtons.forEach(function(b) { b.classList.remove('is-active'); });
2169 btn.classList.add('is-active');
2170
2171 // Filter templates
2172 var visibleCount = 0;
2173 templateItems.forEach(function(item) {
2174 var type = item.getAttribute('data-template-type');
2175 if (filter === 'all' || type === filter) {
2176 item.style.display = '';
2177 visibleCount++;
2178 } else {
2179 item.style.display = 'none';
2180 }
2181 });
2182
2183 updateCount(visibleCount);
2184
2185 if (filterEmptyState && templatesList) {
2186 if (visibleCount === 0) {
2187 filterEmptyState.style.display = 'block';
2188 templatesList.style.display = 'none';
2189 } else {
2190 filterEmptyState.style.display = 'none';
2191 templatesList.style.display = '';
2192 }
2193 }
2194 });
2195 });
2196
2197 // ==========================================
2198 // Rename Template
2199 // ==========================================
2200 var renameBtns = document.querySelectorAll('.ka-hf-rename-btn');
2201 renameBtns.forEach(function(btn) {
2202 btn.addEventListener('click', function(e) {
2203 e.preventDefault();
2204 e.stopPropagation();
2205 var templateId = btn.getAttribute('data-id');
2206 var currentTitle = btn.getAttribute('data-title');
2207 if (renameModal && renameIdInput && renameTitleInput) {
2208 renameIdInput.value = templateId || '';
2209 renameTitleInput.value = currentTitle || '';
2210 renameModal.classList.add('is-open');
2211 renameModal.setAttribute('aria-hidden', 'false');
2212 renameTitleInput.focus();
2213 renameTitleInput.select();
2214 }
2215 });
2216 });
2217
2218 if (renameCloseButtons.length) {
2219 renameCloseButtons.forEach(function(btn) {
2220 btn.addEventListener('click', function() {
2221 if (renameModal) {
2222 renameModal.classList.remove('is-open');
2223 renameModal.setAttribute('aria-hidden', 'true');
2224 }
2225 });
2226 });
2227 }
2228
2229 if (renameModal) {
2230 renameModal.addEventListener('click', function(e) {
2231 if (e.target === renameModal) {
2232 renameModal.classList.remove('is-open');
2233 renameModal.setAttribute('aria-hidden', 'true');
2234 }
2235 });
2236 }
2237
2238 if (renameSaveBtn) {
2239 renameSaveBtn.addEventListener('click', function() {
2240 if (!renameIdInput || !renameTitleInput) return;
2241 var templateId = renameIdInput.value;
2242 var newTitle = (renameTitleInput.value || '').trim();
2243 if (!templateId || !newTitle) return;
2244
2245 var formData = new FormData();
2246 formData.append('action', 'ka_hf_rename_template');
2247 formData.append('nonce', kaHfNonce);
2248 formData.append('template_id', templateId);
2249 formData.append('new_title', newTitle);
2250
2251 fetch(kaHfAjaxUrl, {
2252 method: 'POST',
2253 body: formData
2254 })
2255 .then(function(response) { return response.json(); })
2256 .then(function(data) {
2257 if (data.success) {
2258 var templateEl = document.querySelector('.ka-hf-template[data-template-id="' + templateId + '"]');
2259 if (templateEl) {
2260 var titleText = templateEl.querySelector('.ka-hf-template-title-text');
2261 if (titleText) titleText.textContent = newTitle;
2262 var renameBtn = templateEl.querySelector('.ka-hf-rename-btn');
2263 if (renameBtn) renameBtn.setAttribute('data-title', newTitle);
2264 }
2265 if (renameModal) {
2266 renameModal.classList.remove('is-open');
2267 renameModal.setAttribute('aria-hidden', 'true');
2268 }
2269 } else {
2270 alert(data.data && data.data.message ? data.data.message : '<?php echo esc_js(__('Error renaming template', 'king-addons')); ?>');
2271 }
2272 })
2273 .catch(function(err) {
2274 alert('<?php echo esc_js(__('Error renaming template', 'king-addons')); ?>');
2275 // console.error(err);
2276 });
2277 });
2278 }
2279
2280 // ==========================================
2281 // Enable/Disable Template
2282 // ==========================================
2283 var toggleStatusBtns = document.querySelectorAll('.ka-hf-toggle-status-btn');
2284 toggleStatusBtns.forEach(function(btn) {
2285 btn.addEventListener('click', function(e) {
2286 e.preventDefault();
2287 e.stopPropagation();
2288 var templateId = btn.getAttribute('data-id');
2289 var currentStatus = btn.getAttribute('data-status');
2290 var newStatus = (currentStatus === 'publish') ? 'draft' : 'publish';
2291
2292 var formData = new FormData();
2293 formData.append('action', 'ka_hf_toggle_template_status');
2294 formData.append('nonce', kaHfNonce);
2295 formData.append('template_id', templateId);
2296 formData.append('new_status', newStatus);
2297
2298 fetch(kaHfAjaxUrl, {
2299 method: 'POST',
2300 body: formData
2301 })
2302 .then(function(response) { return response.json(); })
2303 .then(function(data) {
2304 if (data.success) {
2305 // Update button text and data attribute
2306 btn.setAttribute('data-status', newStatus);
2307 btn.textContent = (newStatus === 'publish') ? '<?php echo esc_js(__('Disable', 'king-addons')); ?>' : '<?php echo esc_js(__('Enable', 'king-addons')); ?>';
2308
2309 // Update status badge
2310 var templateEl = document.querySelector('.ka-hf-template[data-template-id="' + templateId + '"]');
2311 if (templateEl) {
2312 templateEl.setAttribute('data-status', newStatus);
2313 var statusBadge = templateEl.querySelector('.ka-hf-template-status');
2314 if (statusBadge) {
2315 if (newStatus === 'publish') {
2316 statusBadge.classList.remove('is-disabled');
2317 statusBadge.classList.add('is-enabled');
2318 var typeLabel = templateEl.getAttribute('data-type-label') || '';
2319 statusBadge.textContent = typeLabel || '<?php echo esc_js(__('Enabled', 'king-addons')); ?>';
2320 } else {
2321 statusBadge.classList.remove('is-enabled');
2322 statusBadge.classList.add('is-disabled');
2323 statusBadge.textContent = '<?php echo esc_js(__('Disabled', 'king-addons')); ?>';
2324 }
2325 }
2326 }
2327 } else {
2328 alert(data.data && data.data.message ? data.data.message : '<?php echo esc_js(__('Error updating template status', 'king-addons')); ?>');
2329 }
2330 })
2331 .catch(function(err) {
2332 alert('<?php echo esc_js(__('Error updating template status', 'king-addons')); ?>');
2333 // console.error(err);
2334 });
2335 });
2336 });
2337
2338 // ==========================================
2339 // Create Modal: Show/hide specific input
2340 // ==========================================
2341 var displayRuleSelect = document.getElementById('ka-hf-display-rule');
2342 var displaySpecificInput = document.getElementById('ka-hf-display-specific');
2343
2344 if (displayRuleSelect && displaySpecificInput) {
2345 displayRuleSelect.addEventListener('change', function() {
2346 if (this.value === 'specifics') {
2347 displaySpecificInput.style.display = 'block';
2348 } else {
2349 displaySpecificInput.style.display = 'none';
2350 displaySpecificInput.value = '';
2351 }
2352 });
2353 }
2354
2355 if (createAddUserRoleBtn) {
2356 createAddUserRoleBtn.addEventListener('click', function() {
2357 if (createUserRolesContainer && typeof addUserRoleRow === 'function') {
2358 addUserRoleRow(createUserRolesContainer, 'all', true);
2359 }
2360 });
2361 }
2362
2363 // ==========================================
2364 // Conditions Popup Functionality
2365 // ==========================================
2366 var conditionsModal = document.getElementById('ka-hf-conditions-modal');
2367 var conditionsCloseButtons = document.querySelectorAll('.ka-hf-conditions-close');
2368 var conditionButtons = document.querySelectorAll('.ka-hf-open-conditions');
2369 var saveConditionsBtn = document.getElementById('ka-hf-save-conditions');
2370 var ruleTemplate = document.getElementById('ka-hf-rule-template');
2371 var userRoleTemplate = document.getElementById('ka-hf-user-role-template');
2372 var userRolesContainer = document.getElementById('ka-hf-user-roles');
2373 var addUserRoleBtn = document.getElementById('ka-hf-add-user-role');
2374
2375 var openConditionsModal = function() {
2376 if (conditionsModal) {
2377 conditionsModal.classList.add('is-open');
2378 conditionsModal.setAttribute('aria-hidden', 'false');
2379 }
2380 };
2381
2382 var closeConditionsModal = function() {
2383 if (conditionsModal) {
2384 conditionsModal.classList.remove('is-open');
2385 conditionsModal.setAttribute('aria-hidden', 'true');
2386 }
2387 };
2388
2389 // Add rule row
2390 var addRuleRow = function(container, ruleValue, specificValue) {
2391 var templateContent = ruleTemplate.content.cloneNode(true);
2392 var row = templateContent.querySelector('.ka-hf-rule-row');
2393 var select = row.querySelector('.ka-hf-rule-select');
2394 var specificInput = row.querySelector('.ka-hf-specific-input');
2395 var removeBtn = row.querySelector('.ka-hf-remove-rule-btn');
2396
2397 if (ruleValue) {
2398 select.value = ruleValue;
2399 }
2400
2401 // Show specific input if needed
2402 if (ruleValue === 'specifics') {
2403 specificInput.style.display = 'block';
2404 if (specificValue) {
2405 specificInput.value = specificValue;
2406 }
2407 }
2408
2409 // Handle select change
2410 select.addEventListener('change', function() {
2411 if (this.value === 'specifics') {
2412 specificInput.style.display = 'block';
2413 } else {
2414 specificInput.style.display = 'none';
2415 specificInput.value = '';
2416 }
2417 });
2418
2419 // Handle remove
2420 removeBtn.addEventListener('click', function() {
2421 row.remove();
2422 });
2423
2424 container.appendChild(row);
2425 };
2426
2427 // Add user role row
2428 var addUserRoleRow = function(container, roleValue, includeNameAttr) {
2429 if (!container || !userRoleTemplate) return;
2430 var templateContent = userRoleTemplate.content.cloneNode(true);
2431 var row = templateContent.querySelector('.ka-hf-user-role-row');
2432 var select = row.querySelector('.ka-hf-user-role-select');
2433 var removeBtn = row.querySelector('.ka-hf-remove-rule-btn');
2434
2435 if (includeNameAttr) {
2436 select.setAttribute('name', 'ka_hf_user_role[]');
2437 }
2438 if (roleValue) {
2439 select.value = roleValue;
2440 }
2441
2442 removeBtn.addEventListener('click', function() {
2443 row.remove();
2444 });
2445
2446 container.appendChild(row);
2447 };
2448
2449 // Create modal default row (after helper is defined)
2450 if (createUserRolesContainer && createUserRolesContainer.children.length === 0) {
2451 addUserRoleRow(createUserRolesContainer, 'all', true);
2452 }
2453
2454 // Load template data into popup
2455 var loadConditionsData = function(templateData) {
2456 var includeContainer = document.getElementById('ka-hf-include-rules');
2457 var excludeContainer = document.getElementById('ka-hf-exclude-rules');
2458 var templateIdInput = document.getElementById('ka-hf-cond-template-id');
2459 var templateTypeSelect = document.getElementById('ka-hf-cond-template-type');
2460
2461 // Clear existing rules
2462 includeContainer.innerHTML = '';
2463 excludeContainer.innerHTML = '';
2464 if (userRolesContainer) {
2465 userRolesContainer.innerHTML = '';
2466 }
2467
2468 // Set template ID
2469 templateIdInput.value = templateData.id;
2470
2471 // Set template type
2472 if (templateTypeSelect && templateData.type) {
2473 templateTypeSelect.value = templateData.type;
2474 }
2475
2476 // Load include rules
2477 var includeRules = templateData.include && templateData.include.rule ? templateData.include.rule : [];
2478 var includeSpecific = templateData.include && templateData.include.specific ? templateData.include.specific : [];
2479
2480 if (includeRules.length === 0) {
2481 addRuleRow(includeContainer, 'basic-global', '');
2482 } else {
2483 includeRules.forEach(function(rule, idx) {
2484 var specificVal = (rule === 'specifics' && includeSpecific.length) ? includeSpecific.join(',') : '';
2485 addRuleRow(includeContainer, rule, specificVal);
2486 });
2487 }
2488
2489 // Load exclude rules
2490 var excludeRules = templateData.exclude && templateData.exclude.rule ? templateData.exclude.rule : [];
2491 var excludeSpecific = templateData.exclude && templateData.exclude.specific ? templateData.exclude.specific : [];
2492
2493 excludeRules.forEach(function(rule, idx) {
2494 var specificVal = (rule === 'specifics' && excludeSpecific.length) ? excludeSpecific.join(',') : '';
2495 addRuleRow(excludeContainer, rule, specificVal);
2496 });
2497
2498 // Load user roles (rows)
2499 var userRoles = templateData.userRoles || [];
2500 if (!Array.isArray(userRoles) || userRoles.length === 0) {
2501 userRoles = ['all'];
2502 }
2503 userRoles.forEach(function(role) {
2504 addUserRoleRow(userRolesContainer, role, false);
2505 });
2506 };
2507
2508 // Condition button click handlers
2509 conditionButtons.forEach(function(btn) {
2510 btn.addEventListener('click', function(e) {
2511 e.preventDefault();
2512 e.stopPropagation();
2513 var templateData = JSON.parse(btn.getAttribute('data-template'));
2514 loadConditionsData(templateData);
2515 openConditionsModal();
2516 });
2517 });
2518
2519 // Add rule buttons
2520 document.querySelectorAll('.ka-hf-add-rule-btn').forEach(function(btn) {
2521 btn.addEventListener('click', function() {
2522 var ruleType = btn.getAttribute('data-rule-type');
2523 var container = document.getElementById('ka-hf-' + ruleType + '-rules');
2524 addRuleRow(container, 'basic-global', '');
2525 });
2526 });
2527
2528 if (addUserRoleBtn) {
2529 addUserRoleBtn.addEventListener('click', function() {
2530 addUserRoleRow(userRolesContainer, 'all', false);
2531 });
2532 }
2533
2534 // Close buttons
2535 conditionsCloseButtons.forEach(function(btn) {
2536 btn.addEventListener('click', closeConditionsModal);
2537 });
2538
2539 // Backdrop click
2540 if (conditionsModal) {
2541 conditionsModal.addEventListener('click', function(e) {
2542 if (e.target === conditionsModal) {
2543 closeConditionsModal();
2544 }
2545 });
2546 }
2547
2548 // Save conditions via AJAX
2549 if (saveConditionsBtn) {
2550 saveConditionsBtn.addEventListener('click', function() {
2551 var savingOverlay = document.getElementById('ka-hf-conditions-saving');
2552 savingOverlay.style.display = 'flex';
2553
2554 var templateId = document.getElementById('ka-hf-cond-template-id').value;
2555 var templateType = document.getElementById('ka-hf-cond-template-type').value;
2556
2557 // Collect user roles
2558 var selectedRoles = [];
2559 document.querySelectorAll('#ka-hf-user-roles .ka-hf-user-role-select').forEach(function(sel) {
2560 if (sel && sel.value) {
2561 selectedRoles.push(sel.value);
2562 }
2563 });
2564 // De-dupe and default
2565 selectedRoles = Array.from(new Set(selectedRoles));
2566 if (selectedRoles.length === 0) {
2567 selectedRoles = ['all'];
2568 }
2569 if (selectedRoles.indexOf('all') !== -1) {
2570 selectedRoles = ['all'];
2571 }
2572
2573 // Collect include rules
2574 var includeRules = [];
2575 document.querySelectorAll('#ka-hf-include-rules .ka-hf-rule-row').forEach(function(row) {
2576 var rule = row.querySelector('.ka-hf-rule-select').value;
2577 var specific = row.querySelector('.ka-hf-specific-input').value;
2578 includeRules.push({ rule: rule, specific: specific });
2579 });
2580
2581 // Collect exclude rules
2582 var excludeRules = [];
2583 document.querySelectorAll('#ka-hf-exclude-rules .ka-hf-rule-row').forEach(function(row) {
2584 var rule = row.querySelector('.ka-hf-rule-select').value;
2585 var specific = row.querySelector('.ka-hf-specific-input').value;
2586 excludeRules.push({ rule: rule, specific: specific });
2587 });
2588
2589 // Build form data
2590 var formData = new FormData();
2591 formData.append('action', 'ka_hf_save_conditions');
2592 formData.append('nonce', kaHfNonce);
2593 formData.append('template_id', templateId);
2594 formData.append('template_type', templateType);
2595
2596 // Append multiple user roles
2597 selectedRoles.forEach(function(role, idx) {
2598 formData.append('user_roles[' + idx + ']', role);
2599 });
2600
2601 includeRules.forEach(function(rule, idx) {
2602 formData.append('include_rules[' + idx + '][rule]', rule.rule);
2603 formData.append('include_rules[' + idx + '][specific]', rule.specific);
2604 });
2605
2606 excludeRules.forEach(function(rule, idx) {
2607 formData.append('exclude_rules[' + idx + '][rule]', rule.rule);
2608 formData.append('exclude_rules[' + idx + '][specific]', rule.specific);
2609 });
2610
2611 fetch(kaHfAjaxUrl, {
2612 method: 'POST',
2613 body: formData
2614 })
2615 .then(function(response) { return response.json(); })
2616 .then(function(data) {
2617 savingOverlay.style.display = 'none';
2618 if (data.success) {
2619 closeConditionsModal();
2620 // Reload page to show updated conditions
2621 window.location.reload();
2622 } else {
2623 alert(data.data && data.data.message ? data.data.message : 'Error saving conditions');
2624 }
2625 })
2626 .catch(function(err) {
2627 savingOverlay.style.display = 'none';
2628 alert('Error saving conditions');
2629 // console.error(err);
2630 });
2631 });
2632 }
2633 });
2634 </script>
2635 <?php
2636 }
2637
2638 public function disableScreenOptions($show_screen, $screen)
2639 {
2640 if ($screen->id === 'edit-king-addons-el-hf') {
2641 return false;
2642 }
2643 return $show_screen;
2644 }
2645
2646 function king_addons_el_hf_get_posts_by_query()
2647 {
2648 // Security fix: Add authorization check
2649 if (!current_user_can('edit_posts')) {
2650 wp_send_json_error('Insufficient permissions');
2651 return;
2652 }
2653
2654 check_ajax_referer('king-addons-el-hf-get-posts-by-query', 'nonce');
2655
2656 $search_string = isset($_POST['q']) ? sanitize_text_field($_POST['q']) : '';
2657 $result = array();
2658
2659 $args = array(
2660 'public' => true,
2661 '_builtin' => false,
2662 );
2663
2664 $output = 'names';
2665 $operator = 'and';
2666 $post_types = get_post_types($args, $output, $operator);
2667
2668 unset($post_types['elementor-hf']);
2669
2670 $post_types['Posts'] = 'post';
2671 $post_types['Pages'] = 'page';
2672
2673 foreach ($post_types as $key => $post_type) {
2674 $data = array();
2675
2676 add_filter('posts_search', array($this, 'search_only_titles'), 10, 2);
2677
2678 $query = new WP_Query(
2679 array(
2680 's' => $search_string,
2681 'post_type' => $post_type,
2682 'posts_per_page' => -1,
2683 )
2684 );
2685
2686 if ($query->have_posts()) {
2687 while ($query->have_posts()) {
2688 $query->the_post();
2689 $title = get_the_title();
2690 $title .= (0 != $query->post->post_parent) ? ' (' . get_the_title($query->post->post_parent) . ')' : '';
2691 $id = get_the_id();
2692 $data[] = array(
2693 'id' => 'post-' . $id,
2694 'text' => $title,
2695 );
2696 }
2697 }
2698
2699 if (is_array($data) && !empty($data)) {
2700 $result[] = array(
2701 'text' => $key,
2702 'children' => $data,
2703 );
2704 }
2705 }
2706
2707 wp_reset_postdata();
2708
2709 $args = array(
2710 'public' => true,
2711 );
2712
2713 $output = 'objects';
2714 $taxonomies = get_taxonomies($args, $output, $operator);
2715
2716 foreach ($taxonomies as $taxonomy) {
2717 $terms = get_terms(
2718 $taxonomy->name,
2719 array(
2720 'orderby' => 'count',
2721 'hide_empty' => 0,
2722 'name__like' => $search_string,
2723 )
2724 );
2725
2726 $data = array();
2727
2728 $label = ucwords($taxonomy->label);
2729
2730 if (!empty($terms)) {
2731 foreach ($terms as $term) {
2732
2733 $data[] = array(
2734 'id' => 'tax-' . $term->term_id,
2735 'text' => $term->name . ' archive page',
2736 );
2737
2738 $data[] = array(
2739 'id' => 'tax-' . $term->term_id . '-single-' . $taxonomy->name,
2740 'text' => 'All singulars from ' . $term->name,
2741 );
2742 }
2743 }
2744
2745 if (is_array($data) && !empty($data)) {
2746 $result[] = array(
2747 'text' => $label,
2748 'children' => $data,
2749 );
2750 }
2751 }
2752
2753 wp_send_json($result);
2754 }
2755
2756 public function initialize_options()
2757 {
2758 self::$user_selection = self::get_user_selections();
2759 self::$location_selection = self::getLocationSelections();
2760 }
2761
2762 public function renderAdminCustomHeader()
2763 {
2764 $current_screen = get_current_screen()->id;
2765 if ($current_screen !== 'edit-king-addons-el-hf'
2766 && $current_screen !== 'header-footer_page_king-addons-el-hf-settings') {
2767 return;
2768 }
2769
2770 ?>
2771 <div class="king-addons-pb-settings-page-header">
2772 <h1><?php esc_html_e('Elementor Header & Footer Builder', 'king-addons'); ?></h1>
2773 <p>
2774 <?php esc_html_e('Create fully customizable headers and footers with display conditions to control where they appear', 'king-addons'); ?>
2775 </p>
2776 <div class="king-addons-pb-preview-buttons">
2777 <a href="<?php echo admin_url('post-new.php?post_type=king-addons-el-hf'); ?>">
2778 <div class="king-addons-pb-user-template">
2779 <span><?php esc_html_e('Create New', 'king-addons'); ?></span>
2780 <span class="plus-icon">+</span>
2781 </div>
2782 </a>
2783 <?php if (!king_addons_freemius()->can_use_premium_code__premium_only()): ?>
2784 <div class="kng-promo-btn-wrap">
2785 <a href="https://kingaddons.com/pricing/?utm_source=king-addons-hf-builder" target="_blank">
2786 <div class="kng-promo-btn-txt">
2787 <?php esc_html_e('Unlock Premium Features & 650+ Templates Today!', 'king-addons'); ?>
2788 </div>
2789 <img width="16px"
2790 src="<?php echo esc_url(KING_ADDONS_URL) . 'includes/admin/img/share-v2.svg'; ?>"
2791 alt="<?php echo esc_html__('Open link in the new tab', 'king-addons'); ?>">
2792 </a>
2793 </div>
2794 <?php endif; ?>
2795 </div>
2796 </div>
2797 <?php
2798
2799 $counts = wp_count_posts('king-addons-el-hf');
2800 $total = (int)$counts->publish + (int)$counts->draft;
2801
2802 if (0 === $total) {
2803 echo '<div class="notice notice-info">';
2804 echo '<p>';
2805 echo esc_html__("Create the first header or footer by clicking the 'Create New' button above.", 'king addons');
2806 echo '</p>';
2807 echo '</div>';
2808 }
2809
2810 }
2811
2812 function addPostType(): void
2813 {
2814 if (!current_user_can('manage_options')) {
2815 return;
2816 }
2817
2818 $labels = [
2819 'name' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2820 'singular_name' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2821 'menu_name' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2822 'name_admin_bar' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2823 'add_new' => esc_html__('Add New', 'king-addons'),
2824 'add_new_item' => esc_html__('Add New', 'king-addons'),
2825 'new_item' => esc_html__('New Template', 'king-addons'),
2826 'edit_item' => esc_html__('Edit Template', 'king-addons'),
2827 'view_item' => esc_html__('View Template', 'king-addons'),
2828 'all_items' => esc_html__('All Templates', 'king-addons'),
2829 'search_items' => esc_html__('Search Templates', 'king-addons'),
2830 'parent_item_colon' => esc_html__('Parent Templates:', 'king-addons'),
2831 'not_found' => esc_html__('No Templates found.', 'king-addons'),
2832 'not_found_in_trash' => esc_html__('No Templates found in Trash.', 'king-addons'),
2833 ];
2834
2835 $args = [
2836 'labels' => $labels,
2837 'public' => true,
2838 'show_ui' => true,
2839 'show_in_menu' => false,
2840 'show_in_nav_menus' => false,
2841 'exclude_from_search' => true,
2842 'capability_type' => 'post',
2843 'hierarchical' => false,
2844 'menu_icon' => 'dashicons-editor-kitchensink',
2845 'supports' => ['title', 'editor', 'thumbnail', 'elementor'],
2846 'show_in_rest' => true,
2847 ];
2848
2849 register_post_type('king-addons-el-hf', $args);
2850
2851 if (false === get_option('king_addons_HFB_flushed_rewrite_rules')) {
2852 add_option('king_addons_HFB_flushed_rewrite_rules', true);
2853 flush_rewrite_rules();
2854 }
2855 }
2856
2857 function registerMetabox()
2858 {
2859 add_meta_box(
2860 'king-addons-el-hf-meta-box',
2861 esc_html__('Elementor Header & Footer Builder Options', 'king-addons'),
2862 [$this, 'renderMetabox'],
2863 'king-addons-el-hf',
2864 'normal',
2865 'high'
2866 );
2867 }
2868
2869 function renderMetabox($post)
2870 {
2871 $values = get_post_custom($post->ID);
2872 $template_type = isset($values['king_addons_el_hf_template_type']) ? esc_attr(sanitize_text_field($values['king_addons_el_hf_template_type'][0])) : '';
2873 $display_on_canvas = isset($values['king-addons-el-hf-display-on-canvas']);
2874
2875 wp_nonce_field('king_addons_el_hf_meta_nounce', 'king_addons_el_hf_meta_nounce');
2876 ?>
2877 <table class="king-addons-el-hf-options-table widefat">
2878 <tbody>
2879 <tr class="king-addons-el-hf-options-row type-of-template">
2880 <td class="king-addons-el-hf-options-row-heading">
2881 <label for="king_addons_el_hf_template_type"><strong><?php esc_html_e('Type of Template', 'king-addons'); ?></strong></label>
2882 </td>
2883 <td class="king-addons-el-hf-options-row-content">
2884 <select name="king_addons_el_hf_template_type" id="king_addons_el_hf_template_type">
2885 <option value="king_addons_el_hf_not_selected" <?php selected($template_type, ''); ?>><?php esc_html_e('Select Option', 'king-addons'); ?></option>
2886 <option value="king_addons_el_hf_type_header" <?php selected($template_type, 'king_addons_el_hf_type_header'); ?>><?php esc_html_e('Header', 'king-addons'); ?></option>
2887 <option value="king_addons_el_hf_type_footer" <?php selected($template_type, 'king_addons_el_hf_type_footer'); ?>><?php esc_html_e('Footer', 'king-addons'); ?></option>
2888 </select>
2889 </td>
2890 </tr>
2891 <?php
2892 $this->display_rules_tab();
2893
2894 ?>
2895 <tr class="king-addons-el-hf-options-row enable-for-canvas">
2896 <td class="king-addons-el-hf-options-row-heading">
2897 <label for="king-addons-el-hf-display-on-canvas">
2898 <strong><?php esc_html_e('Enable Layout for Elementor Canvas Template?', 'king-addons'); ?></strong>
2899 </label>
2900 <p><?php esc_html_e('Enabling this option will display this layout on pages using Elementor Canvas Template', 'king-addons'); ?></p>
2901 </td>
2902 <td class="king-addons-el-hf-options-row-content">
2903 <input type="checkbox" id="king-addons-el-hf-display-on-canvas"
2904 name="king-addons-el-hf-display-on-canvas"
2905 value="1" <?php checked($display_on_canvas); ?> />
2906 </td>
2907 </tr>
2908 </tbody>
2909 </table>
2910 <?php
2911 }
2912
2913
2914 public function admin_styles()
2915 {
2916 wp_enqueue_script('king-addons-el-hf-select2', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/select2.js', array('jquery'), KING_ADDONS_VERSION, true);
2917
2918 wp_register_script(
2919 'king-addons-el-hf-target-rule',
2920 KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/conditions-target.js',
2921 array(
2922 'jquery',
2923 'king-addons-el-hf-select2',
2924 ),
2925 KING_ADDONS_VERSION,
2926 true
2927 );
2928
2929 wp_enqueue_script('king-addons-el-hf-target-rule');
2930
2931 wp_register_script(
2932 'king-addons-el-hf-user-role',
2933 KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/conditions-user.js',
2934 array(
2935 'jquery',
2936 ),
2937 KING_ADDONS_VERSION,
2938 true
2939 );
2940
2941 wp_enqueue_script('king-addons-el-hf-user-role');
2942
2943 wp_register_style('king-addons-el-hf-select2', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/select2.css', '', KING_ADDONS_VERSION);
2944 wp_enqueue_style('king-addons-el-hf-select2');
2945 wp_register_style('king-addons-el-hf-target-rule', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/conditions.css', '', KING_ADDONS_VERSION);
2946 wp_enqueue_style('king-addons-el-hf-target-rule');
2947 wp_enqueue_script('king-addons-el-hf-script', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/admin.js', array('jquery'), KING_ADDONS_VERSION);
2948
2949 $localize_vars = array(
2950 'please_enter' => __('Please enter', 'king-addons'),
2951 'please_delete' => __('Please delete', 'king-addons'),
2952 'more_char' => __('or more characters', 'king-addons'),
2953 'character' => __('character', 'king-addons'),
2954 'loading' => __('Loading more results…', 'king-addons'),
2955 'only_select' => __('You can only select', 'king-addons'),
2956 'item' => __('item', 'king-addons'),
2957 'char_s' => __('s', 'king-addons'),
2958 'no_result' => __('No results found', 'king-addons'),
2959 'searching' => __('Searching…', 'king-addons'),
2960 'not_loader' => __('The results could not be loaded.', 'king-addons'),
2961 'search' => __('Search pages / post / categories', 'king-addons'),
2962 'ajax_nonce' => wp_create_nonce('king-addons-el-hf-get-posts-by-query'),
2963 );
2964 wp_localize_script('king-addons-el-hf-select2', 'kngRules', $localize_vars);
2965
2966 }
2967
2968 public function display_rules_tab()
2969 {
2970 $this->admin_styles();
2971 $include_locations = get_post_meta(get_the_id(), 'king_addons_el_hf_target_include_locations', true);
2972 $exclude_locations = get_post_meta(get_the_id(), 'king_addons_el_hf_target_exclude_locations', true);
2973 $users = get_post_meta(get_the_id(), 'king_addons_el_hf_target_user_roles', true);
2974 ?>
2975 <tr class="king-addons-el-hf-target-rules-row king-addons-el-hf-options-row">
2976 <td class="king-addons-el-hf-target-rules-row-heading king-addons-el-hf-options-row-heading">
2977 <label><strong><?php esc_html_e('Display On', 'king-addons'); ?></strong></label>
2978 <p><?php esc_html_e('Add locations for where this template should appear', 'king-addons'); ?></p>
2979 </td>
2980 <td class="king-addons-el-hf-target-rules-row-content king-addons-el-hf-options-row-content">
2981 <?php
2982 self::target_rule_settings_field(
2983 'king-addons-el-hf-target-rules-location',
2984 [
2985 'title' => __('Display Rules', 'king-addons'),
2986 'value' => '[{"type":"basic-global","specific":null}]',
2987 'tags' => 'site,enable,target,pages',
2988 'rule_type' => 'display',
2989 'add_rule_label' => __('Add Display Rule', 'king-addons'),
2990 ],
2991 $include_locations
2992 );
2993 ?>
2994 </td>
2995 </tr>
2996 <tr class="king-addons-el-hf-target-rules-row king-addons-el-hf-options-row">
2997 <td class="king-addons-el-hf-target-rules-row-heading king-addons-el-hf-options-row-heading">
2998 <label><strong><?php esc_html_e('Do Not Display On', 'king-addons'); ?></strong></label>
2999 <p><?php esc_html_e('Add locations for where this template should not appear', 'king-addons'); ?></p>
3000 </td>
3001 <td class="king-addons-el-hf-target-rules-row-content king-addons-el-hf-options-row-content">
3002 <?php
3003 self::target_rule_settings_field(
3004 'king-addons-el-hf-target-rules-exclusion',
3005 [
3006 'title' => __('Exclude On', 'king-addons'),
3007 'value' => '[]',
3008 'tags' => 'site,enable,target,pages',
3009 'add_rule_label' => __('Add Exclusion Rule', 'king-addons'),
3010 'rule_type' => 'exclude',
3011 ],
3012 $exclude_locations
3013 );
3014 ?>
3015 </td>
3016 </tr>
3017 <tr class="king-addons-el-hf-target-rules-row king-addons-el-hf-options-row">
3018 <td class="king-addons-el-hf-target-rules-row-heading king-addons-el-hf-options-row-heading">
3019 <label><strong><?php esc_html_e('User Roles', 'king-addons'); ?></strong></label>
3020 <p><?php esc_html_e('Display custom template based on user role', 'king-addons'); ?></p>
3021 </td>
3022 <td class="king-addons-el-hf-target-rules-row-content king-addons-el-hf-options-row-content">
3023 <?php
3024 self::target_user_role_settings_field(
3025 'king-addons-el-hf-target-rules-users',
3026 [
3027 'title' => __('Users', 'king-addons'),
3028 'value' => '[]',
3029 'tags' => 'site,enable,target,pages',
3030 'add_rule_label' => __('Add User Rule', 'king-addons'),
3031 ],
3032 $users
3033 );
3034 ?>
3035 </td>
3036 </tr>
3037 <?php
3038 }
3039
3040 public static function get_user_selections()
3041 {
3042 $selection_options = array(
3043 'basic' => array(
3044 'label' => __('Basic', 'king-addons'),
3045 'value' => array(
3046 'all' => __('All', 'king-addons'),
3047 'logged-in' => __('Logged In', 'king-addons'),
3048 'logged-out' => __('Logged Out', 'king-addons'),
3049 ),
3050 ),
3051
3052 'advanced' => array(
3053 'label' => __('Advanced', 'king-addons'),
3054 'value' => array(),
3055 ),
3056 );
3057
3058 /* User roles */
3059 $roles = get_editable_roles();
3060
3061 foreach ($roles as $slug => $data) {
3062 $selection_options['advanced']['value'][$slug] = $data['name'];
3063 }
3064
3065 /**
3066 * Filter options displayed in the user select field of Display conditions.
3067 *
3068 * @since 1.5.0
3069 */
3070 return apply_filters('king-addons-el-hf_user_roles_list', $selection_options);
3071 }
3072
3073 public static function target_user_role_settings_field($name, $settings, $value)
3074 {
3075 $input_name = $name;
3076 $add_rule_label = $settings['add_rule_label'] ?? __('Add Rule', 'king-addons');
3077 $saved_values = $value;
3078 $output = '';
3079
3080 if (!isset(self::$user_selection) || empty(self::$user_selection)) {
3081 self::$user_selection = self::get_user_selections();
3082 }
3083 $selection_options = self::$user_selection;
3084
3085 $output .= '<script type="text/html" id="tmpl-king-addons-el-hf-user-role-condition">';
3086 $output .= '<div class="king-addons-el-hf-user-role-condition king-addons-el-hf-user-role-{{data.id}}" data-rule="{{data.id}}" >';
3087 $output .= '<span class="user_role-condition-delete dashicons dashicons-dismiss"></span>';
3088
3089 $output .= '<div class="user_role-condition-wrap" >';
3090 $output .= '<select name="' . esc_attr($input_name) . '[{{data.id}}]" class="user_role-condition form-control king-addons-el-hf-input">';
3091 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3092
3093 foreach ($selection_options as $group_data) {
3094 $output .= '<optgroup label="' . $group_data['label'] . '">';
3095 foreach ($group_data['value'] as $opt_key => $opt_value) {
3096 $output .= '<option value="' . $opt_key . '">' . $opt_value . '</option>';
3097 }
3098 $output .= '</optgroup>';
3099 }
3100 $output .= '</select>';
3101 $output .= '</div>';
3102 $output .= '</div> <!-- king-addons-el-hf-user-role-condition -->';
3103 $output .= '</script>';
3104
3105 /** @noinspection PhpConditionAlreadyCheckedInspection */
3106 if (!is_array($saved_values) || (is_array($saved_values) && empty($saved_values))) {
3107 $saved_values = array();
3108 $saved_values[0] = '';
3109 }
3110
3111 $index = 0;
3112
3113 $output .= '<div class="king-addons-el-hf-user-role-wrapper king-addons-el-hf-user-role-display-on-wrap" data-type="display">';
3114 $output .= '<div class="king-addons-el-hf-user-role-selector-wrapper king-addons-el-hf-user-role-display-on">';
3115 $output .= '<div class="user_role-builder-wrap">';
3116 foreach ($saved_values as $index => $data) {
3117 $output .= '<div class="king-addons-el-hf-user-role-condition king-addons-el-hf-user-role-' . $index . '" data-rule="' . $index . '" >';
3118 $output .= '<span class="user_role-condition-delete dashicons dashicons-dismiss"></span>';
3119 /* Condition Selection */
3120 $output .= '<div class="user_role-condition-wrap" >';
3121 $output .= '<select name="' . esc_attr($input_name) . '[' . $index . ']" class="user_role-condition form-control king-addons-el-hf-input">';
3122 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3123
3124 foreach ($selection_options as $group_data) {
3125 $output .= '<optgroup label="' . $group_data['label'] . '">';
3126 foreach ($group_data['value'] as $opt_key => $opt_value) {
3127 $output .= '<option value="' . $opt_key . '" ' . selected($data, $opt_key, false) . '>' . $opt_value . '</option>';
3128 }
3129 $output .= '</optgroup>';
3130 }
3131 $output .= '</select>';
3132 $output .= '</div>';
3133 $output .= '</div> <!-- king-addons-el-hf-user-role-condition -->';
3134 }
3135 $output .= '</div>';
3136 /* Add new rule */
3137 $output .= '<div class="user_role-add-rule-wrap">';
3138 $output .= '<a href="#" class="button" data-rule-id="' . absint($index) . '">' . $add_rule_label . '</a>';
3139 $output .= '</div>';
3140 $output .= '</div>';
3141 $output .= '</div>';
3142
3143 echo $output;
3144 }
3145
3146 public static function target_rule_settings_field($name, $settings, $value)
3147 {
3148 $input_name = $name;
3149 $rule_type = $settings['rule_type'] ?? 'target_rule';
3150 $add_rule_label = $settings['add_rule_label'] ?? __('Add Rule', 'king-addons');
3151 $saved_values = $value;
3152 $output = '';
3153
3154 if (isset(self::$location_selection) || empty(self::$location_selection)) {
3155 self::$location_selection = self::getLocationSelections();
3156 }
3157 $selection_options = self::$location_selection;
3158
3159 $output .= '<script type="text/html" id="tmpl-king-addons-el-hf-target-rule-' . $rule_type . '-condition">';
3160
3161 $output .= '<div class="king-addons-el-hf-target-rule-condition king-addons-el-hf-target-rule-{{data.id}}" data-rule="{{data.id}}" >';
3162 $output .= '<span class="target_rule-condition-delete dashicons dashicons-dismiss"></span>';
3163 $output .= '<div class="target_rule-condition-wrap" >';
3164
3165 $output .= '<select name="' . esc_attr($input_name) . '[rule][{{data.id}}]" class="target_rule-condition form-control king-addons-el-hf-input">';
3166 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3167
3168 foreach ($selection_options as $group_data) {
3169 $output .= '<optgroup label="' . $group_data['label'] . '">';
3170 foreach ($group_data['value'] as $opt_key => $opt_value) {
3171 $output .= '<option value="' . $opt_key . '">' . $opt_value . '</option>';
3172 }
3173 $output .= '</optgroup>';
3174 }
3175 $output .= '</select>';
3176
3177 $output .= '</div>';
3178 $output .= '</div> <!-- king-addons-el-hf-target-rule-condition -->';
3179
3180 $output .= '<div class="target_rule-specific-page-wrap" style="display:none">';
3181 $output .= '<select name="' . esc_attr($input_name) . '[specific][]" class="target-rule-select2 target_rule-specific-page form-control king-addons-el-hf-input " multiple="multiple">';
3182 $output .= '</select>';
3183 $output .= '</div>';
3184
3185 $output .= '</script>';
3186
3187 $output .= '<div class="king-addons-el-hf-target-rule-wrapper king-addons-el-hf-target-rule-' . $rule_type . '-on-wrap" data-type="' . $rule_type . '">';
3188 $output .= '<div class="king-addons-el-hf-target-rule-selector-wrapper king-addons-el-hf-target-rule-' . $rule_type . '-on">';
3189 $output .= self::generate_target_rule_selector($rule_type, $selection_options, $input_name, $saved_values, $add_rule_label);
3190 $output .= '</div>';
3191 $output .= '</div>';
3192
3193 echo $output;
3194 }
3195
3196 public static function generate_target_rule_selector($type, $selection_options, $input_name, $saved_values, $add_rule_label)
3197 {
3198 $output = '<div class="target_rule-builder-wrap">';
3199
3200 /** @noinspection PhpConditionAlreadyCheckedInspection */
3201 if (!is_array($saved_values) || (is_array($saved_values) && empty($saved_values))) {
3202 $saved_values = array();
3203 $saved_values['rule'][0] = '';
3204 $saved_values['specific'][0] = '';
3205 }
3206
3207 $index = 0;
3208 if (is_array($saved_values) && is_array($saved_values['rule'])) {
3209 foreach ($saved_values['rule'] as $index => $data) {
3210 $output .= '<div class="king-addons-el-hf-target-rule-condition king-addons-el-hf-target-rule-' . $index . '" data-rule="' . $index . '" >';
3211
3212 $output .= '<span class="target_rule-condition-delete dashicons dashicons-dismiss"></span>';
3213 $output .= '<div class="target_rule-condition-wrap" >';
3214 $output .= '<select name="' . esc_attr($input_name) . '[rule][' . $index . ']" class="target_rule-condition form-control king-addons-el-hf-input">';
3215 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3216
3217 foreach ($selection_options as $group_data) {
3218 $output .= '<optgroup label="' . $group_data['label'] . '">';
3219 foreach ($group_data['value'] as $opt_key => $opt_value) {
3220
3221 $selected = '';
3222
3223 if ($data == $opt_key) {
3224 $selected = 'selected="selected"';
3225 }
3226
3227 $output .= '<option value="' . $opt_key . '" ' . $selected . '>' . $opt_value . '</option>';
3228 }
3229 $output .= '</optgroup>';
3230 }
3231 $output .= '</select>';
3232 $output .= '</div>';
3233
3234 $output .= '</div>';
3235
3236 $output .= '<div class="target_rule-specific-page-wrap" style="display:none">';
3237 $output .= '<select name="' . esc_attr($input_name) . '[specific][]" class="target-rule-select2 target_rule-specific-page form-control king-addons-el-hf-input " multiple="multiple">';
3238
3239 if ('specifics' == $data && isset($saved_values['specific']) && null != $saved_values['specific'] && is_array($saved_values['specific'])) {
3240 foreach ($saved_values['specific'] as $sel_value) {
3241
3242 if (strpos($sel_value, 'post-') !== false) {
3243 $post_id = (int)str_replace('post-', '', $sel_value);
3244 $post_title = get_the_title($post_id);
3245 $output .= '<option value="post-' . $post_id . '" selected="selected" >' . $post_title . '</option>';
3246 }
3247
3248 if (strpos($sel_value, 'tax-') !== false) {
3249 $tax_data = explode('-', $sel_value);
3250
3251 $tax_id = (int)str_replace('tax-', '', $sel_value);
3252 $term = get_term($tax_id);
3253 $term_name = '';
3254
3255 if (!is_wp_error($term)) {
3256 $term_taxonomy = ucfirst(str_replace('_', ' ', $term->taxonomy));
3257
3258 if (isset($tax_data[2]) && 'single' === $tax_data[2]) {
3259 $term_name = 'All singulars from ' . $term->name;
3260 } else {
3261 $term_name = $term->name . ' - ' . $term_taxonomy;
3262 }
3263 }
3264
3265 $output .= '<option value="' . $sel_value . '" selected="selected" >' . $term_name . '</option>';
3266 }
3267 }
3268 }
3269 $output .= '</select>';
3270 $output .= '</div>';
3271 }
3272 }
3273
3274 $output .= '</div>';
3275
3276 $output .= '<div class="target_rule-add-rule-wrap">';
3277 $output .= '<a href="#" class="button" data-rule-id="' . absint($index) . '" data-rule-type="' . $type . '">' . $add_rule_label . '</a>';
3278 $output .= '</div>';
3279
3280 if ('display' == $type) {
3281 $output .= '<div class="target_rule-add-exclusion-rule">';
3282 $output .= '<a href="#" class="button">' . __('Add Exclusion Rule', 'king-addons') . '</a>';
3283 $output .= '</div>';
3284 }
3285
3286 return $output;
3287 }
3288
3289 function saveMetaboxData($post_id)
3290 {
3291 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
3292 return;
3293 }
3294
3295 if (!isset($_POST['king_addons_el_hf_meta_nounce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['king_addons_el_hf_meta_nounce'])), 'king_addons_el_hf_meta_nounce')) {
3296 return;
3297 }
3298
3299 if (!current_user_can('edit_posts')) {
3300 return;
3301 }
3302
3303 if (!isset($_POST['king-addons-el-hf-target-rules-location'])) {
3304 $target_locations = array(
3305 'rule' => array('basic-global'),
3306 'specific' => array(),
3307 );
3308 } else {
3309 $target_locations = self::getFormatRuleValue($_POST, 'king-addons-el-hf-target-rules-location');
3310 if (empty($target_locations)) {
3311 $target_locations = array(
3312 'rule' => array('basic-global'),
3313 'specific' => array(),
3314 );
3315 }
3316 }
3317
3318 $target_exclusion = self::getFormatRuleValue($_POST, 'king-addons-el-hf-target-rules-exclusion');
3319 $target_users = [];
3320
3321 if (isset($_POST['king-addons-el-hf-target-rules-users'])) {
3322 $target_users = array_map('sanitize_text_field', wp_unslash($_POST['king-addons-el-hf-target-rules-users']));
3323 }
3324
3325 update_post_meta($post_id, 'king_addons_el_hf_target_include_locations', $target_locations);
3326 update_post_meta($post_id, 'king_addons_el_hf_target_exclude_locations', $target_exclusion);
3327 update_post_meta($post_id, 'king_addons_el_hf_target_user_roles', $target_users);
3328
3329 if (isset($_POST['king_addons_el_hf_template_type'])) {
3330 update_post_meta($post_id, 'king_addons_el_hf_template_type', sanitize_text_field(wp_unslash($_POST['king_addons_el_hf_template_type'])));
3331 }
3332
3333 if (isset($_POST['king-addons-el-hf-display-on-canvas'])) {
3334 update_post_meta($post_id, 'king-addons-el-hf-display-on-canvas', sanitize_text_field(wp_unslash($_POST['king-addons-el-hf-display-on-canvas'])));
3335 } else {
3336 delete_post_meta($post_id, 'king-addons-el-hf-display-on-canvas');
3337 }
3338 }
3339
3340 function setCompatibility()
3341 {
3342 $template = get_template();
3343 $is_elementor_callable = defined('ELEMENTOR_VERSION') && is_callable('Elementor\Plugin::instance');
3344
3345 if ($is_elementor_callable) {
3346 self::$elementor_instance = Elementor\Plugin::instance();
3347
3348 // TODO: Add popular themes
3349 switch ($template) {
3350 case 'hello-elementor':
3351 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/hello-elementor/ELHF_Hello_Elementor.php');
3352 break;
3353 default:
3354 add_action('init', [$this, 'setupSettingsPage']);
3355 add_filter('king_addons_el_hf_settings_tabs', [$this, 'setupUnsupportedTheme']);
3356 add_action('init', [$this, 'setupFallbackSupport']);
3357 break;
3358 }
3359 }
3360 }
3361
3362 public function setupUnsupportedTheme($settings_tabs = [])
3363 {
3364 if (!current_theme_supports('king-addons-elementor-header-footer')) {
3365 $settings_tabs['king_addons_el_hf_settings'] = [
3366 'name' => esc_html__('Display Settings', 'king-addons'),
3367 'url' => admin_url('admin.php?page=king-addons-el-hf&tab=settings'),
3368 ];
3369 }
3370 return $settings_tabs;
3371 }
3372
3373 public function setupFallbackSupport()
3374 {
3375 if (!current_theme_supports('king-addons-elementor-header-footer')) {
3376 // Default to Method 3 (Universal) for best compatibility
3377 $compatibility_option = get_option('king_addons_el_hf_compatibility_option', '3');
3378
3379 if ('1' === $compatibility_option) {
3380 if (!class_exists('ELHF_Default_Method_1')) {
3381 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/default/ELHF_Default_Method_1.php');
3382 }
3383 } elseif ('2' === $compatibility_option) {
3384 if (!class_exists('ELHF_Default_Method_2')) {
3385 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/default/ELHF_Default_Method_2.php');
3386 }
3387 } else {
3388 // Method 3: Universal (combines all approaches for maximum compatibility)
3389 if (!class_exists('ELHF_Default_Method_3')) {
3390 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/default/ELHF_Default_Method_3.php');
3391 }
3392 }
3393 }
3394 }
3395
3396 function setupSettingsPage()
3397 {
3398 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/ELHF_Settings_Page.php');
3399 }
3400
3401 public static function renderHeader()
3402 {
3403 /** @noinspection SpellCheckingInspection */
3404 echo '<header id="masthead" class="king-addons-el-hf-header" itemscope="itemscope" itemtype="https://schema.org/WPHeader">';
3405 ?><p class="main-title" style="display: none;" itemprop="headline"><a
3406 href="<?php echo esc_url(get_bloginfo('url')); ?>"
3407 title="<?php echo esc_attr(get_bloginfo('name', 'display')); ?>"
3408 rel="home"><?php echo esc_html(get_bloginfo('name')); ?></a></p><?php
3409 self::getHeaderContent();
3410 echo '</header>';
3411 }
3412
3413 public static function renderFooter()
3414 {
3415 /** @noinspection SpellCheckingInspection */
3416 echo '<footer id="colophon" class="king-addons-el-hf-footer" itemscope="itemscope" itemtype="https://schema.org/WPFooter" role="contentinfo">';
3417 self::getFooterContent();
3418 echo '</footer>';
3419 }
3420
3421 public static function getHeaderContent()
3422 {
3423 $header_id = self::getHeaderID();
3424 if ($header_id) {
3425 // Ensure Elementor-generated CSS is included on non-Elementor pages.
3426 $with_css = true;
3427 echo self::$elementor_instance->frontend->get_builder_content_for_display($header_id, $with_css); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3428 }
3429 }
3430
3431 public static function getFooterContent()
3432 {
3433 $footer_id = self::getFooterID();
3434 if ($footer_id) {
3435 echo '<div style="width: 100%;">';
3436 // Ensure Elementor-generated CSS is included on non-Elementor pages.
3437 $with_css = true;
3438 echo self::$elementor_instance->frontend->get_builder_content_for_display($footer_id, $with_css); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3439 echo '</div>';
3440 }
3441 }
3442
3443 public static function getHeaderID()
3444 {
3445 $header_id = self::getSettings('king_addons_el_hf_type_header');
3446
3447 if ('' === $header_id) {
3448 $header_id = false;
3449 }
3450
3451 return apply_filters('king_addons_el_hf_get_header_id', $header_id);
3452 }
3453
3454 public static function isHeaderEnabled()
3455 {
3456 $header_id = self::getSettings('king_addons_el_hf_type_header');
3457 $status = false;
3458
3459 if ('' !== $header_id) {
3460 $status = true;
3461 }
3462
3463 return apply_filters('king_addons_el_hf_header_enabled', $status);
3464 }
3465
3466 public static function isFooterEnabled()
3467 {
3468 $footer_id = self::getSettings('king_addons_el_hf_type_footer');
3469 $status = false;
3470
3471 if ('' !== $footer_id) {
3472 $status = true;
3473 }
3474
3475 return apply_filters('king_addons_el_hf_footer_enabled', $status);
3476 }
3477
3478 public static function getFooterID()
3479 {
3480 $footer_id = self::getSettings('king_addons_el_hf_type_footer');
3481
3482 if ('' === $footer_id) {
3483 $footer_id = false;
3484 }
3485
3486 return apply_filters('king_addons_el_hf_get_footer_id', $footer_id);
3487 }
3488
3489
3490 public static function getSettings($setting = '')
3491 {
3492 if ('king_addons_el_hf_type_header' == $setting || 'king_addons_el_hf_type_footer' == $setting) {
3493 $templates = self::getTemplateID($setting);
3494 $template = !is_array($templates) ? $templates : $templates[0];
3495 return apply_filters("king_addons_el_hf_get_settings_$setting", $template);
3496 }
3497
3498 return null;
3499 }
3500
3501 public static function getTemplateID($type)
3502 {
3503 $option = [
3504 'location' => 'king_addons_el_hf_target_include_locations',
3505 'exclusion' => 'king_addons_el_hf_target_exclude_locations',
3506 'users' => 'king_addons_el_hf_target_user_roles',
3507 ];
3508
3509 $templates = self::getPostsByConditions('king-addons-el-hf', $option);
3510
3511 // Prime meta cache to avoid N+1 queries when calling get_post_meta in loops below.
3512 // getPostsByConditions() may already have populated template IDs; caching is safe and cheap.
3513 if (!empty($templates)) {
3514 $template_ids = array_map(static function ($template) {
3515 return isset($template['id']) ? absint($template['id']) : 0;
3516 }, $templates);
3517 $template_ids = array_values(array_filter($template_ids));
3518 if (!empty($template_ids)) {
3519 update_meta_cache('post', $template_ids);
3520 }
3521 }
3522
3523 foreach ($templates as $template) {
3524 if (get_post_meta(absint($template['id']), 'king_addons_el_hf_template_type', true) === $type) {
3525 // Polylang check - https://polylang.pro/doc/function-reference/
3526 if (function_exists('pll_current_language')) {
3527 if (pll_current_language('slug') == pll_get_post_language($template['id'], 'slug')) {
3528 return $template['id'];
3529 }
3530 } else {
3531 return $template['id'];
3532 }
3533 }
3534 }
3535
3536 return '';
3537 }
3538
3539 public static function getPostsByConditions($post_type, $option)
3540 {
3541 global $wpdb;
3542 global $post;
3543
3544 // Security fix: Validate and sanitize post_type
3545 // Ensure $post is valid before accessing its properties
3546 if ($post_type) {
3547 $post_type = sanitize_key($post_type);
3548 } elseif ($post instanceof \WP_Post && !empty($post->post_type)) {
3549 $post_type = sanitize_key($post->post_type);
3550 } else {
3551 return [];
3552 }
3553
3554 if (empty($post_type)) {
3555 return [];
3556 }
3557
3558 if (is_array(self::$current_page_data) && isset(self::$current_page_data[$post_type])) {
3559 return apply_filters('king_addons_el_hf_get_display_posts_by_conditions', self::$current_page_data[$post_type], $post_type);
3560 }
3561
3562 $current_page_type = self::getCurrentPageType();
3563
3564 self::$current_page_data[$post_type] = array();
3565
3566 $option['current_post_id'] = self::$current_page_data['ID'];
3567 $meta_header = self::getMetaOptionPost($post_type, $option);
3568
3569 if (false === $meta_header) {
3570 $current_post_type = sanitize_key(get_post_type());
3571 $current_post_id = false;
3572 $q_obj = get_queried_object();
3573
3574 $current_id = absint(get_the_id());
3575
3576 // Check if WPML is active. Find WPML Object ID for current page.
3577 /** @noinspection SpellCheckingInspection */
3578 if (defined('ICL_SITEPRESS_VERSION')) {
3579 $default_lang = apply_filters('wpml_default_language', '');
3580 $current_lang = apply_filters('wpml_current_language', '');
3581
3582 if ($default_lang !== $current_lang) {
3583 $current_post_type = get_post_type($current_id);
3584 $current_id = apply_filters('wpml_object_id', $current_id, $current_post_type, true, $default_lang);
3585 }
3586 }
3587
3588 // Security fix: Sanitize location parameter
3589 $location = isset($option['location']) ? sanitize_key($option['location']) : '';
3590 if (empty($location)) {
3591 return [];
3592 }
3593
3594 // Security fix: Use prepared statement to prevent SQL injection
3595 $query = $wpdb->prepare(
3596 "SELECT p.ID, pm.meta_value FROM {$wpdb->postmeta} as pm
3597 INNER JOIN {$wpdb->posts} as p ON pm.post_id = p.ID
3598 WHERE pm.meta_key = %s
3599 AND p.post_type = %s
3600 AND p.post_status = 'publish'",
3601 $location,
3602 $post_type
3603 );
3604
3605 $orderby = ' ORDER BY p.post_date DESC';
3606
3607 // Security fix: Build meta_args using safe placeholders and prepared statements
3608 $meta_conditions = ["pm.meta_value LIKE %s"];
3609 $meta_values = ['%"basic-global"%'];
3610
3611 switch ($current_page_type) {
3612 case 'is_404':
3613 $meta_conditions[] = "pm.meta_value LIKE %s";
3614 $meta_values[] = '%"special-404"%';
3615 break;
3616 case 'is_search':
3617 $meta_conditions[] = "pm.meta_value LIKE %s";
3618 $meta_values[] = '%"special-search"%';
3619 break;
3620 case 'is_archive':
3621 case 'is_tax':
3622 case 'is_date':
3623 case 'is_author':
3624 $meta_conditions[] = "pm.meta_value LIKE %s";
3625 $meta_values[] = '%"basic-archives"%';
3626 $meta_conditions[] = "pm.meta_value LIKE %s";
3627 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all|archive"%';
3628
3629 if ('is_tax' == $current_page_type && (is_category() || is_tag() || is_tax())) {
3630 if (is_object($q_obj) && isset($q_obj->taxonomy) && isset($q_obj->term_id)) {
3631 $meta_conditions[] = "pm.meta_value LIKE %s";
3632 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all|taxarchive|' . sanitize_key($q_obj->taxonomy) . '"%';
3633 $meta_conditions[] = "pm.meta_value LIKE %s";
3634 $meta_values[] = '%"tax-' . absint($q_obj->term_id) . '"%';
3635 }
3636 } elseif ('is_date' == $current_page_type) {
3637 $meta_conditions[] = "pm.meta_value LIKE %s";
3638 $meta_values[] = '%"special-date"%';
3639 } elseif ('is_author' == $current_page_type) {
3640 $meta_conditions[] = "pm.meta_value LIKE %s";
3641 $meta_values[] = '%"special-author"%';
3642 }
3643 break;
3644 case 'is_home':
3645 $meta_conditions[] = "pm.meta_value LIKE %s";
3646 $meta_values[] = '%"special-blog"%';
3647 break;
3648 case 'is_front_page':
3649 $current_post_id = $current_id;
3650 $meta_conditions[] = "pm.meta_value LIKE %s";
3651 $meta_values[] = '%"special-front"%';
3652 $meta_conditions[] = "pm.meta_value LIKE %s";
3653 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all"%';
3654 $meta_conditions[] = "pm.meta_value LIKE %s";
3655 $meta_values[] = '%"post-' . absint($current_id) . '"%';
3656 break;
3657 case 'is_singular':
3658 $current_post_id = $current_id;
3659 $meta_conditions[] = "pm.meta_value LIKE %s";
3660 $meta_values[] = '%"basic-singulars"%';
3661 $meta_conditions[] = "pm.meta_value LIKE %s";
3662 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all"%';
3663 $meta_conditions[] = "pm.meta_value LIKE %s";
3664 $meta_values[] = '%"post-' . absint($current_id) . '"%';
3665
3666 if (is_object($q_obj) && isset($q_obj->post_type) && isset($q_obj->ID)) {
3667 $taxonomies = get_object_taxonomies($q_obj->post_type);
3668 $terms = wp_get_post_terms($q_obj->ID, $taxonomies);
3669 foreach ($terms as $term) {
3670 if (isset($term->term_id) && isset($term->taxonomy)) {
3671 $meta_conditions[] = "pm.meta_value LIKE %s";
3672 $meta_values[] = '%"tax-' . absint($term->term_id) . '-single-' . sanitize_key($term->taxonomy) . '"%';
3673 }
3674 }
3675 }
3676 break;
3677 case 'is_woo_shop_page':
3678 if (function_exists('is_shop')) {
3679 $meta_conditions[] = "pm.meta_value LIKE %s";
3680 $meta_values[] = '%"special-woocommerce-shop"%';
3681 }
3682 break;
3683 case '':
3684 $current_post_id = $current_id;
3685 break;
3686 }
3687
3688 // Build the final meta_args string using prepare
3689 $meta_args = '(' . implode(' OR ', $meta_conditions) . ')';
3690
3691 // Security fix: Use prepared statement for the complete query
3692 $full_query = $wpdb->prepare(
3693 $query . ' AND ' . $meta_args . $orderby,
3694 ...$meta_values
3695 );
3696
3697 $posts = $wpdb->get_results($full_query);
3698
3699 foreach ($posts as $local_post) {
3700 $unserialized_location = maybe_unserialize($local_post->meta_value);
3701 if ($unserialized_location !== false) {
3702 self::$current_page_data[$post_type][$local_post->ID] = array(
3703 'id' => $local_post->ID,
3704 'location' => $unserialized_location,
3705 );
3706 }
3707 }
3708
3709 // Prime meta cache for all candidate templates to avoid repeated get_post_meta queries
3710 // in removeExclusionRulePosts/removeUserRulePosts and getTemplateID.
3711 if (!empty(self::$current_page_data[$post_type])) {
3712 $candidate_ids = array_map('absint', array_keys(self::$current_page_data[$post_type]));
3713 $candidate_ids = array_values(array_filter($candidate_ids));
3714 if (!empty($candidate_ids)) {
3715 update_meta_cache('post', $candidate_ids);
3716 }
3717 }
3718
3719 $option['current_post_id'] = $current_post_id;
3720
3721 self::removeExclusionRulePosts($post_type, $option);
3722 self::removeUserRulePosts($post_type, $option);
3723 }
3724
3725 return apply_filters('king_addons_el_hf_get_display_posts_by_conditions', self::$current_page_data[$post_type], $post_type);
3726 }
3727
3728 public static function getCurrentPageType(): ?string
3729 {
3730 if (null === self::$current_page_type) {
3731 $page_type = '';
3732 $current_id = false;
3733
3734 if (is_404()) {
3735 $page_type = 'is_404';
3736 } elseif (is_search()) {
3737 $page_type = 'is_search';
3738 } elseif (is_archive()) {
3739 $page_type = 'is_archive';
3740 if (is_category() || is_tag() || is_tax()) {
3741 $page_type = 'is_tax';
3742 } elseif (is_date()) {
3743 $page_type = 'is_date';
3744 } elseif (is_author()) {
3745 $page_type = 'is_author';
3746 } elseif (function_exists('is_shop')) {
3747 /** @noinspection PhpUndefinedFunctionInspection */
3748 if (is_shop()) {
3749 $page_type = 'is_woo_shop_page';
3750 }
3751 }
3752 } elseif (is_home()) {
3753 $page_type = 'is_home';
3754 } elseif (is_front_page()) {
3755 $page_type = 'is_front_page';
3756 $current_id = get_the_id();
3757 } elseif (is_singular()) {
3758 $page_type = 'is_singular';
3759 $current_id = get_the_id();
3760 } else {
3761 $current_id = get_the_id();
3762 }
3763
3764 self::$current_page_data['ID'] = $current_id;
3765 self::$current_page_type = $page_type;
3766 }
3767
3768 return self::$current_page_type;
3769 }
3770
3771 public static function getMetaOptionPost($post_type, $option)
3772 {
3773 $page_meta = (isset($option['page_meta']) && '' != $option['page_meta']) ? $option['page_meta'] : false;
3774
3775 if (false !== $page_meta) {
3776 $current_post_id = $option['current_post_id'] ?? false;
3777 $meta_id = get_post_meta($current_post_id, $option['page_meta'], true);
3778
3779 if (false !== $meta_id && '' != $meta_id) {
3780 self::$current_page_data[$post_type][$meta_id] = array(
3781 'id' => $meta_id,
3782 'location' => '',
3783 );
3784
3785 return self::$current_page_data[$post_type];
3786 }
3787 }
3788
3789 return false;
3790 }
3791
3792 public static function removeExclusionRulePosts($post_type, $option)
3793 {
3794 $exclusion = $option['exclusion'] ?? '';
3795 $current_post_id = $option['current_post_id'] ?? false;
3796 foreach (self::$current_page_data[$post_type] as $c_post_id => $c_data) {
3797 $exclusion_rules = get_post_meta($c_post_id, $exclusion, true);
3798 $is_exclude = self::parseLayoutDisplayCondition($current_post_id, $exclusion_rules);
3799 if ($is_exclude) {
3800 unset(self::$current_page_data[$post_type][$c_post_id]);
3801 }
3802 }
3803 }
3804
3805 public static function removeUserRulePosts($post_type, $option)
3806 {
3807 $users = $option['users'] ?? '';
3808
3809 foreach (self::$current_page_data[$post_type] as $c_post_id => $c_data) {
3810 $user_rules = get_post_meta($c_post_id, $users, true);
3811 $is_user = self::parseUserRoleCondition($user_rules);
3812
3813 if (!$is_user) {
3814 unset(self::$current_page_data[$post_type][$c_post_id]);
3815 }
3816 }
3817 }
3818
3819 public static function parseLayoutDisplayCondition($post_id, $rules): bool
3820 {
3821 $display = false;
3822
3823 /** @noinspection PhpConditionCheckedByNextConditionInspection */
3824 if (isset($rules['rule']) && is_array($rules['rule']) && !empty($rules['rule'])) {
3825 foreach ($rules['rule'] as $rule) {
3826 if (strrpos($rule, 'all') !== false) {
3827 $rule_case = 'all';
3828 } else {
3829 $rule_case = $rule;
3830 }
3831
3832 switch ($rule_case) {
3833 case 'basic-global':
3834 $display = true;
3835 break;
3836
3837 case 'basic-singulars':
3838 if (is_singular()) {
3839 $display = true;
3840 }
3841 break;
3842
3843 case 'basic-archives':
3844 if (is_archive()) {
3845 $display = true;
3846 }
3847 break;
3848
3849 case 'special-404':
3850 if (is_404()) {
3851 $display = true;
3852 }
3853 break;
3854
3855 case 'special-search':
3856 if (is_search()) {
3857 $display = true;
3858 }
3859 break;
3860
3861 case 'special-blog':
3862 if (is_home()) {
3863 $display = true;
3864 }
3865 break;
3866
3867 case 'special-front':
3868 if (is_front_page()) {
3869 $display = true;
3870 }
3871 break;
3872
3873 case 'special-date':
3874 if (is_date()) {
3875 $display = true;
3876 }
3877 break;
3878
3879 case 'special-author':
3880 if (is_author()) {
3881 $display = true;
3882 }
3883 break;
3884
3885 case 'special-woocommerce-shop':
3886 if (function_exists('is_shop')) {
3887 if (is_shop()) {
3888 $display = true;
3889 }
3890 }
3891 break;
3892
3893 case 'all':
3894 $rule_data = explode('|', $rule);
3895
3896 $post_type = $rule_data[0] ?? false;
3897 $archive_type = $rule_data[2] ?? false;
3898 $taxonomy = $rule_data[3] ?? false;
3899 if (false === $archive_type) {
3900 $current_post_type = get_post_type($post_id);
3901 if (false !== $post_id && $current_post_type == $post_type) {
3902 $display = true;
3903 }
3904 } else {
3905 if (is_archive()) {
3906 $current_post_type = get_post_type();
3907 if ($current_post_type == $post_type) {
3908 if ('archive' == $archive_type) {
3909 $display = true;
3910 } elseif ('taxarchive' == $archive_type) {
3911 $obj = get_queried_object();
3912 $current_taxonomy = '';
3913 if ('' !== $obj && null !== $obj) {
3914 $current_taxonomy = $obj->taxonomy;
3915 }
3916
3917 if ($current_taxonomy == $taxonomy) {
3918 $display = true;
3919 }
3920 }
3921 }
3922 }
3923 }
3924 break;
3925
3926 case 'specifics':
3927 if (isset($rules['specific']) && is_array($rules['specific'])) {
3928 foreach ($rules['specific'] as $specific_page) {
3929 $specific_data = explode('-', $specific_page);
3930 $specific_post_type = $specific_data[0] ?? false;
3931 $specific_post_id = $specific_data[1] ?? false;
3932 if ('post' == $specific_post_type) {
3933 if ($specific_post_id == $post_id) {
3934 $display = true;
3935 }
3936 } elseif (isset($specific_data[2]) && ('single' == $specific_data[2]) && 'tax' == $specific_post_type) {
3937 if (is_singular()) {
3938 $term_details = get_term($specific_post_id);
3939
3940 if (isset($term_details->taxonomy)) {
3941 $has_term = has_term((int)$specific_post_id, $term_details->taxonomy, $post_id);
3942
3943 if ($has_term) {
3944 $display = true;
3945 }
3946 }
3947 }
3948 } elseif ('tax' == $specific_post_type) {
3949 $tax_id = get_queried_object_id();
3950 if ($specific_post_id == $tax_id) {
3951 $display = true;
3952 }
3953 }
3954 }
3955 }
3956 break;
3957
3958 default:
3959 break;
3960 }
3961
3962 if ($display) {
3963 break;
3964 }
3965 }
3966 }
3967
3968 return $display;
3969 }
3970
3971 public static function parseUserRoleCondition($rules): bool
3972 {
3973 $show_popup = true;
3974
3975 if (is_array($rules) && !empty($rules)) {
3976 $show_popup = false;
3977
3978 foreach ($rules as $rule) {
3979 switch ($rule) {
3980 case '':
3981 case 'all':
3982 $show_popup = true;
3983 break;
3984
3985 case 'logged-in':
3986 if (is_user_logged_in()) {
3987 $show_popup = true;
3988 }
3989 break;
3990
3991 case 'logged-out':
3992 if (!is_user_logged_in()) {
3993 $show_popup = true;
3994 }
3995 break;
3996
3997 default:
3998 if (is_user_logged_in()) {
3999 $current_user = wp_get_current_user();
4000
4001 if (isset($current_user->roles)
4002 && is_array($current_user->roles)
4003 && in_array($rule, $current_user->roles)
4004 ) {
4005 $show_popup = true;
4006 }
4007 }
4008 break;
4009 }
4010
4011 if ($show_popup) {
4012 break;
4013 }
4014 }
4015 }
4016
4017 return $show_popup;
4018 }
4019
4020 public static function checkUserCanEdit()
4021 {
4022 if (is_singular('king-addons-el-hf') && !current_user_can('edit_posts')) {
4023 wp_redirect(site_url(), 301);
4024 die;
4025 }
4026 }
4027
4028 public static function loadElementorCanvasTemplate($single_template)
4029 {
4030 global $post;
4031
4032 // Safety check: ensure $post is a valid WP_Post object before accessing properties
4033 if (!$post instanceof \WP_Post) {
4034 return $single_template;
4035 }
4036
4037 if ('king-addons-el-hf' === $post->post_type) {
4038 if (defined('ELEMENTOR_VERSION')) {
4039 $elementor_2_0_canvas = ELEMENTOR_PATH . '/modules/page-templates/templates/canvas.php';
4040
4041 if (file_exists($elementor_2_0_canvas)) {
4042 return $elementor_2_0_canvas;
4043 } else {
4044 return ELEMENTOR_PATH . '/includes/page-templates/canvas.php';
4045 }
4046 }
4047 }
4048
4049 return $single_template;
4050 }
4051
4052 public function forceElementorCanvasTemplate($template)
4053 {
4054 if (!defined('ELEMENTOR_VERSION')) {
4055 return $template;
4056 }
4057
4058 $post_id = 0;
4059
4060 if (is_singular('king-addons-el-hf')) {
4061 $post_id = (int) get_queried_object_id();
4062 } elseif (isset($_GET['elementor-preview'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4063 $post_id = (int) $_GET['elementor-preview'];
4064 }
4065
4066 if ($post_id && 'king-addons-el-hf' === get_post_type($post_id)) {
4067 $elementor_2_0_canvas = ELEMENTOR_PATH . '/modules/page-templates/templates/canvas.php';
4068 if (file_exists($elementor_2_0_canvas)) {
4069 return $elementor_2_0_canvas;
4070 }
4071 return ELEMENTOR_PATH . '/includes/page-templates/canvas.php';
4072 }
4073
4074 return $template;
4075 }
4076
4077 public function forcePreviewQuery($query): void
4078 {
4079 if (is_admin() || !$query->is_main_query()) {
4080 return;
4081 }
4082
4083 if (!isset($_GET['elementor-preview'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4084 return;
4085 }
4086
4087 $preview_id = (int) $_GET['elementor-preview']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4088 if (!$preview_id || 'king-addons-el-hf' !== get_post_type($preview_id)) {
4089 return;
4090 }
4091
4092 $query->set('p', $preview_id);
4093 $query->set('post_type', 'king-addons-el-hf');
4094 $query->set('post_status', ['publish', 'draft', 'pending', 'private']);
4095 }
4096
4097 public static function enqueueScripts(): void
4098 {
4099 $screen = get_current_screen();
4100 if ($screen->id === 'edit-king-addons-el-hf') {
4101 // todo - styles
4102 // wp_enqueue_style('king-addons-el-hf-style', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/header-footer-builder.css', '', KING_ADDONS_VERSION);
4103 wp_enqueue_style('king-addons-el-hf-style', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/admin.css', '', KING_ADDONS_VERSION);
4104 }
4105 }
4106
4107 public static function columnHeadings($columns)
4108 {
4109 unset($columns['date']);
4110 $columns['king_addons_el_hf_edit_template'] = esc_html__('Edit Template', 'king-addons');
4111 $columns['king_addons_el_hf_type_of_template'] = esc_html__('Type of Template', 'king-addons');
4112 $columns['king_addons_el_hf_display_rules'] = esc_html__('Display Rules', 'king-addons');
4113 $columns['date'] = esc_html__('Date', 'king-addons');
4114 return $columns;
4115 }
4116
4117 public static function columnContent($column, $post_id)
4118 {
4119 // Edit Template
4120 if ('king_addons_el_hf_edit_template' === $column) {
4121 echo '<a class="king-addons-el-hf-edit-template-btn" href="';
4122 echo './post.php?post=' . esc_attr($post_id) . '&action=edit';
4123 echo '">' . esc_html__('Edit Template', 'king-addons') . '</a>';
4124 }
4125
4126 // Display Rules
4127 if ('king_addons_el_hf_display_rules' === $column) {
4128
4129 $locations = get_post_meta($post_id, 'king_addons_el_hf_target_include_locations', true);
4130 if (!empty($locations)) {
4131 echo '<div style="margin-bottom: 5px;">';
4132 echo '<strong>';
4133 echo esc_html__('Display: ', 'king-addons');
4134 echo '</strong>';
4135 self::columnDisplayLocation($locations);
4136 echo '</div>';
4137 }
4138
4139 $locations = get_post_meta($post_id, 'king_addons_el_hf_target_exclude_locations', true);
4140 if (!empty($locations)) {
4141 echo '<div style="margin-bottom: 5px;">';
4142 echo '<strong>';
4143 echo esc_html__('Exclusion: ', 'king-addons');
4144 echo '</strong>';
4145 self::columnDisplayLocation($locations);
4146 echo '</div>';
4147 }
4148
4149 $users = get_post_meta($post_id, 'king_addons_el_hf_target_user_roles', true);
4150 if (isset($users) && is_array($users)) {
4151 if (!empty($users[0])) {
4152 $user_label = [];
4153 foreach ($users as $user) {
4154 $user_label[] = self::get_user_by_key($user);
4155 }
4156 echo '<div>';
4157 echo '<strong>Users: </strong>';
4158 echo esc_html(join(', ', $user_label));
4159 echo '</div>';
4160 }
4161 }
4162
4163 }
4164
4165 // Type of Template
4166 if ('king_addons_el_hf_type_of_template' === $column) {
4167 $template_type = get_post_meta($post_id, 'king_addons_el_hf_template_type', true);
4168 if (!empty($template_type)) {
4169 echo '<div style="margin-bottom: 5px;">';
4170 echo '<strong>';
4171 switch ($template_type) {
4172 case 'king_addons_el_hf_type_header':
4173 echo esc_html__('Header', 'king-addons');
4174 break;
4175 case 'king_addons_el_hf_type_footer':
4176 echo esc_html__('Footer', 'king-addons');
4177 break;
4178 default:
4179 echo esc_html__('Not selected', 'king-addons');
4180 break;
4181 }
4182 echo '</strong>';
4183 echo '</div>';
4184 }
4185 }
4186 }
4187
4188 public static function get_user_by_key($key)
4189 {
4190 if (!isset(self::$user_selection) || empty(self::$user_selection)) {
4191 self::$user_selection = self::get_user_selections();
4192 }
4193 $user_selection = self::$user_selection;
4194
4195 if (isset($user_selection['basic']['value'][$key])) {
4196 return $user_selection['basic']['value'][$key];
4197 } elseif ($user_selection['advanced']['value'][$key]) {
4198 return $user_selection['advanced']['value'][$key];
4199 }
4200 return $key;
4201 }
4202
4203 public static function columnDisplayLocation($locations)
4204 {
4205 $location_label = [];
4206 /** @noinspection PhpConditionAlreadyCheckedInspection */
4207 if (is_array($locations) && is_array($locations['rule']) && isset($locations['rule'])) {
4208 /** @noinspection PhpArraySearchInBooleanContextInspection */
4209 $index = array_search('specifics', $locations['rule']);
4210 /** @noinspection PhpConditionCheckedByNextConditionInspection */
4211 if (false !== $index && !empty($index)) {
4212 unset($locations['rule'][$index]);
4213 }
4214 }
4215
4216 if (isset($locations['rule']) && is_array($locations['rule'])) {
4217 foreach ($locations['rule'] as $location) {
4218 $location_label[] = self::getLocation($location);
4219 }
4220 }
4221
4222 if (isset($locations['specific']) && is_array($locations['specific'])) {
4223 foreach ($locations['specific'] as $location) {
4224 $location_label[] = self::getLocation($location);
4225 }
4226 }
4227
4228 echo esc_html(join(', ', $location_label));
4229 }
4230
4231 public static function getLocation($key)
4232 {
4233 if (!isset(self::$location_selection) || empty(self::$location_selection)) {
4234 self::$location_selection = self::getLocationSelections();
4235 }
4236
4237 $location_selection = self::$location_selection;
4238
4239 foreach ($location_selection as $location_grp) {
4240 if (isset($location_grp['value'][$key])) {
4241 return $location_grp['value'][$key];
4242 }
4243 }
4244
4245 if (strpos($key, 'post-') !== false) {
4246 $post_id = (int)str_replace('post-', '', $key);
4247 return get_the_title($post_id);
4248 }
4249
4250 if (strpos($key, 'tax-') !== false) {
4251 $tax_id = (int)str_replace('tax-', '', $key);
4252 $term = get_term($tax_id);
4253
4254 if (!is_wp_error($term)) {
4255 $term_taxonomy = ucfirst(str_replace('_', ' ', $term->taxonomy));
4256 return $term->name . ' - ' . $term_taxonomy;
4257 } else {
4258 return '';
4259 }
4260 }
4261
4262 return $key;
4263 }
4264
4265 public static function getLocationSelections()
4266 {
4267 $args = array(
4268 'public' => true,
4269 '_builtin' => true,
4270 );
4271
4272 $post_types = get_post_types($args, 'objects');
4273 unset($post_types['attachment']);
4274
4275 $args['_builtin'] = false;
4276 $custom_post_type = get_post_types($args, 'objects');
4277
4278 $post_types = apply_filters('king_addons_el_hf_location_rule_post_types', array_merge($post_types, $custom_post_type));
4279
4280 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
4281 $special_pages = array(
4282 'special-404-none' => esc_html__('404 Page (Available in PRO)', 'king-addons'),
4283 'special-search' => esc_html__('Search Page', 'king-addons'),
4284 'special-blog-none' => esc_html__('Blog / Posts Page (Available in PRO)', 'king-addons'),
4285 'special-front' => esc_html__('Front Page', 'king-addons'),
4286 'special-date' => esc_html__('Date Archive', 'king-addons'),
4287 'special-author' => esc_html__('Author Archive', 'king-addons'),
4288 );
4289 } else {
4290 $special_pages = array(
4291 'special-404' => esc_html__('404 Page', 'king-addons'),
4292 'special-search' => esc_html__('Search Page', 'king-addons'),
4293 'special-blog' => esc_html__('Blog / Posts Page', 'king-addons'),
4294 'special-front' => esc_html__('Front Page', 'king-addons'),
4295 'special-date' => esc_html__('Date Archive', 'king-addons'),
4296 'special-author' => esc_html__('Author Archive', 'king-addons'),
4297 );
4298 }
4299
4300 if (class_exists('WooCommerce')) {
4301 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
4302 $special_pages['special-woocommerce-shop-none'] = esc_html__('WooCommerce Shop Page (Available in PRO)', 'king-addons');
4303 } else {
4304 $special_pages['special-woocommerce-shop'] = esc_html__('WooCommerce Shop Page', 'king-addons');
4305 }
4306 }
4307
4308 $selection_options = array(
4309 'basic' => array(
4310 'label' => esc_html__('Basic', 'king-addons'),
4311 'value' => array(
4312 'basic-global' => esc_html__('Entire Website', 'king-addons'),
4313 'basic-singulars' => esc_html__('All Singulars', 'king-addons'),
4314 'basic-archives' => esc_html__('All Archives', 'king-addons'),
4315 ),
4316 ),
4317
4318 'special-pages' => array(
4319 'label' => esc_html__('Special Pages', 'king-addons'),
4320 'value' => $special_pages,
4321 ),
4322 );
4323
4324 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
4325 $selection_options['specific-target'] = array(
4326 'label' => esc_html__('Specific Target', 'king-addons'),
4327 'value' => array(
4328 'specifics-none' => esc_html__('Specific Pages / Posts / Taxonomies, etc. (Available in PRO)', 'king-addons'),
4329 ),
4330 );
4331 } else {
4332 $selection_options['specific-target'] = array(
4333 'label' => esc_html__('Specific Target', 'king-addons'),
4334 'value' => array(
4335 'specifics' => esc_html__('Specific Pages / Posts / Taxonomies, etc.', 'king-addons'),
4336 ),
4337 );
4338 }
4339
4340 $args = array(
4341 'public' => true,
4342 );
4343
4344 $taxonomies = get_taxonomies($args, 'objects');
4345
4346 if (!empty($taxonomies)) {
4347 foreach ($taxonomies as $taxonomy) {
4348
4349 if ('post_format' == $taxonomy->name) {
4350 continue;
4351 }
4352
4353 foreach ($post_types as $post_type) {
4354 $post_opt = self::getPostTargetRuleOptions($post_type, $taxonomy);
4355
4356 if (isset($selection_options[$post_opt['post_key']])) {
4357 if (!empty($post_opt['value']) && is_array($post_opt['value'])) {
4358 foreach ($post_opt['value'] as $key => $value) {
4359 if (!in_array($value, $selection_options[$post_opt['post_key']]['value'])) {
4360 $selection_options[$post_opt['post_key']]['value'][$key] = $value;
4361 }
4362 }
4363 }
4364 } else {
4365 $selection_options[$post_opt['post_key']] = array(
4366 'label' => $post_opt['label'],
4367 'value' => $post_opt['value'],
4368 );
4369 }
4370 }
4371 }
4372 }
4373
4374 return apply_filters('king_addons_el_hf_display_on_list', $selection_options);
4375 }
4376
4377 public static function getPostTargetRuleOptions($post_type, $taxonomy): array
4378 {
4379 $post_key = str_replace(' ', '-', strtolower($post_type->label));
4380 $post_label = ucwords($post_type->label);
4381 $post_name = $post_type->name;
4382 $post_option = array();
4383
4384 /* translators: %s is post label */
4385 $all_posts = sprintf(esc_html__('All %s', 'king-addons'), $post_label);
4386 $post_option[$post_name . '|all'] = $all_posts;
4387
4388 if ('pages' != $post_key) {
4389 /* translators: %s is post label */
4390 $all_archive = sprintf(esc_html__('All %s Archive', 'king-addons'), $post_label);
4391 $post_option[$post_name . '|all|archive'] = $all_archive;
4392 }
4393
4394 if (in_array($post_type->name, $taxonomy->object_type)) {
4395 $tax_label = ucwords($taxonomy->label);
4396 $tax_name = $taxonomy->name;
4397
4398 /* translators: %s is taxonomy label */
4399 $tax_archive = sprintf(esc_html__('All %s Archive', 'king-addons'), $tax_label);
4400
4401 $post_option[$post_name . '|all|taxarchive|' . $tax_name] = $tax_archive;
4402 }
4403
4404 $post_output['post_key'] = $post_key;
4405 $post_output['label'] = $post_label;
4406 $post_output['value'] = $post_option;
4407
4408 return $post_output;
4409 }
4410
4411 public static function getFormatRuleValue($save_data, $key): array
4412 {
4413 $meta_value = array();
4414
4415 if (isset($save_data[$key]['rule'])) {
4416 $save_data[$key]['rule'] = array_unique($save_data[$key]['rule']);
4417 if (isset($save_data[$key]['specific'])) {
4418 $save_data[$key]['specific'] = array_unique($save_data[$key]['specific']);
4419 }
4420
4421 $index = array_search('', $save_data[$key]['rule']);
4422 if (false !== $index) {
4423 unset($save_data[$key]['rule'][$index]);
4424 }
4425 $index = array_search('specifics', $save_data[$key]['rule']);
4426 if (false !== $index) {
4427 unset($save_data[$key]['rule'][$index]);
4428
4429 if (isset($save_data[$key]['specific']) && is_array($save_data[$key]['specific'])) {
4430 $save_data[$key]['rule'][] = 'specifics';
4431 }
4432 }
4433
4434 foreach ($save_data[$key] as $meta_key => $value) {
4435 if (!empty($value)) {
4436 $meta_value[$meta_key] = array_map('esc_attr', $value);
4437 }
4438 }
4439 if (!isset($meta_value['rule']) || !in_array('specifics', $meta_value['rule'])) {
4440 $meta_value['specific'] = array();
4441 }
4442
4443 if (empty($meta_value['rule'])) {
4444 $meta_value = array();
4445 }
4446 }
4447
4448 return $meta_value;
4449 }
4450 }