PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.78
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.78
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 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / extensions / Header_Footer_Builder / Header_Footer_Builder.php

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

4,447 lines 187.6 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 }
1902
1903 /* Navigation Tabs */
1904 .ka-hf-nav-tabs {
1905 display: inline-flex;
1906 align-items: center;
1907 gap: 4px;
1908 padding: 4px;
1909 background: var(--ka-hf-surface);
1910 border: 1px solid var(--ka-hf-border);
1911 border-radius: 14px;
1912 margin-bottom: 32px;
1913 }
1914 .ka-hf-nav-tab {
1915 display: inline-flex;
1916 align-items: center;
1917 gap: 8px;
1918 padding: 12px 20px;
1919 font-size: 14px;
1920 font-weight: 500;
1921 color: var(--ka-hf-text-secondary);
1922 text-decoration: none;
1923 border-radius: 10px;
1924 transition: var(--ka-hf-transition);
1925 }
1926 .ka-hf-nav-tab svg {
1927 opacity: 0.7;
1928 transition: var(--ka-hf-transition);
1929 }
1930 .ka-hf-nav-tab:hover {
1931 color: var(--ka-hf-text);
1932 background: rgba(0, 0, 0, 0.03);
1933 }
1934 .ka-hf-nav-tab:hover svg {
1935 opacity: 1;
1936 }
1937 .ka-hf-nav-tab.is-active {
1938 background: var(--ka-hf-accent);
1939 color: #fff;
1940 }
1941 .ka-hf-nav-tab.is-active svg {
1942 opacity: 1;
1943 }
1944 body.ka-v3-dark .ka-hf-nav-tab:hover {
1945 background: rgba(255, 255, 255, 0.05);
1946 }
1947
1948 /* Settings Tab Styles */
1949 .ka-hf-settings-section {
1950 max-width: 700px;
1951 }
1952 .ka-hf-settings-form {
1953 margin: 0;
1954 }
1955 .ka-hf-settings-card {
1956 background: var(--ka-hf-surface);
1957 border: 1px solid var(--ka-hf-border);
1958 border-radius: var(--ka-hf-radius);
1959 padding: 32px;
1960 }
1961 .ka-hf-settings-card-title {
1962 font-size: 20px;
1963 font-weight: 600;
1964 margin: 0 0 8px 0;
1965 color: var(--ka-hf-text);
1966 }
1967 .ka-hf-settings-card-desc {
1968 font-size: 14px;
1969 color: var(--ka-hf-text-secondary);
1970 margin: 0 0 24px 0;
1971 line-height: 1.5;
1972 }
1973 .ka-hf-settings-options {
1974 display: flex;
1975 flex-direction: column;
1976 gap: 16px;
1977 margin-bottom: 28px;
1978 }
1979 .ka-hf-settings-option {
1980 display: flex;
1981 align-items: flex-start;
1982 gap: 14px;
1983 padding: 20px;
1984 background: rgba(0, 0, 0, 0.02);
1985 border: 1px solid var(--ka-hf-border);
1986 border-radius: var(--ka-hf-radius-sm);
1987 cursor: pointer;
1988 transition: var(--ka-hf-transition);
1989 }
1990 .ka-hf-settings-option:hover {
1991 border-color: var(--ka-hf-accent);
1992 background: rgba(0, 113, 227, 0.02);
1993 }
1994 .ka-hf-settings-option:has(input:checked) {
1995 border-color: var(--ka-hf-accent);
1996 background: rgba(0, 113, 227, 0.05);
1997 }
1998 .ka-hf-settings-option input[type="radio"] {
1999 margin: 3px 0 0 0;
2000 flex-shrink: 0;
2001 accent-color: var(--ka-hf-accent);
2002 width: 18px;
2003 height: 18px;
2004 }
2005 .ka-hf-settings-option-content {
2006 display: flex;
2007 flex-direction: column;
2008 gap: 4px;
2009 }
2010 .ka-hf-settings-option-title {
2011 font-size: 15px;
2012 font-weight: 600;
2013 color: var(--ka-hf-text);
2014 }
2015 .ka-hf-settings-option-desc {
2016 font-size: 13px;
2017 color: var(--ka-hf-text-secondary);
2018 line-height: 1.5;
2019 }
2020 .ka-hf-settings-actions {
2021 padding-top: 4px;
2022 }
2023 body.ka-v3-dark .ka-hf-settings-option {
2024 background: rgba(255, 255, 255, 0.02);
2025 }
2026 body.ka-v3-dark .ka-hf-settings-option:hover {
2027 background: rgba(0, 113, 227, 0.08);
2028 }
2029 body.ka-v3-dark .ka-hf-settings-option:has(input:checked) {
2030 background: rgba(0, 113, 227, 0.12);
2031 }
2032 </style>
2033
2034 <script>
2035 document.addEventListener('DOMContentLoaded', function() {
2036 var addNewButton = document.getElementById('ka-hf-add-new');
2037 var addNewEmptyButton = document.getElementById('ka-hf-add-new-empty');
2038 var addNewFilterEmptyButton = document.getElementById('ka-hf-add-new-filter-empty');
2039 var modal = document.getElementById('ka-hf-modal');
2040 var closeButtons = document.querySelectorAll('.ka-hf-modal-close');
2041 var typeSelect = document.getElementById('ka-hf-type');
2042 var createUserRolesContainer = document.getElementById('ka-hf-create-user-roles');
2043 var createAddUserRoleBtn = document.getElementById('ka-hf-create-add-user-role');
2044
2045 var renameModal = document.getElementById('ka-hf-rename-modal');
2046 var renameCloseButtons = document.querySelectorAll('.ka-hf-rename-close');
2047 var renameSaveBtn = document.getElementById('ka-hf-rename-save');
2048 var renameIdInput = document.getElementById('ka-hf-rename-id');
2049 var renameTitleInput = document.getElementById('ka-hf-rename-title-input');
2050
2051 var openModal = function() {
2052 if (modal) {
2053 modal.classList.add('is-open');
2054 modal.setAttribute('aria-hidden', 'false');
2055 }
2056
2057 // Ensure at least one user role row exists in create modal
2058 if (createUserRolesContainer && typeof addUserRoleRow === 'function') {
2059 if (createUserRolesContainer.children.length === 0) {
2060 addUserRoleRow(createUserRolesContainer, 'all', true);
2061 }
2062 }
2063 };
2064
2065 var closeModal = function() {
2066 if (modal) {
2067 modal.classList.remove('is-open');
2068 modal.setAttribute('aria-hidden', 'true');
2069 }
2070 };
2071
2072 if (addNewButton) {
2073 addNewButton.addEventListener('click', function(e) {
2074 e.preventDefault();
2075 openModal();
2076 });
2077 }
2078
2079 if (addNewEmptyButton) {
2080 addNewEmptyButton.addEventListener('click', function(e) {
2081 e.preventDefault();
2082 openModal();
2083 });
2084 }
2085
2086 if (addNewFilterEmptyButton) {
2087 addNewFilterEmptyButton.addEventListener('click', function(e) {
2088 e.preventDefault();
2089 openModal();
2090 });
2091 }
2092
2093 // Type cards: preselect type and open create modal
2094 var typeButtons = document.querySelectorAll('.ka-hf-type');
2095 typeButtons.forEach(function(btn) {
2096 btn.addEventListener('click', function(e) {
2097 e.preventDefault();
2098 var type = btn.getAttribute('data-ka-hf-type');
2099 if (typeSelect && type) {
2100 typeSelect.value = type;
2101 }
2102 openModal();
2103 });
2104 });
2105
2106 closeButtons.forEach(function(button) {
2107 button.addEventListener('click', closeModal);
2108 });
2109
2110 // Backdrop click closes
2111 if (modal) {
2112 modal.addEventListener('click', function(e) {
2113 if (e.target === modal) {
2114 closeModal();
2115 }
2116 });
2117 }
2118
2119 // ESC closes
2120 document.addEventListener('keydown', function(e) {
2121 if (e.key === 'Escape') {
2122 closeModal();
2123 }
2124 });
2125
2126 // Dropdown toggles
2127 var dropdowns = document.querySelectorAll('[data-ka-dropdown]');
2128 dropdowns.forEach(function(dropdown) {
2129 var trigger = dropdown.querySelector('.ka-hf-dropdown-trigger');
2130 if (trigger) {
2131 trigger.addEventListener('click', function(e) {
2132 e.stopPropagation();
2133 dropdown.classList.toggle('is-open');
2134 });
2135 }
2136 });
2137
2138 document.addEventListener('click', function() {
2139 dropdowns.forEach(function(dropdown) {
2140 dropdown.classList.remove('is-open');
2141 });
2142 });
2143
2144 // ==========================================
2145 // JS Filtering (All/Headers/Footers)
2146 // ==========================================
2147 var filterButtons = document.querySelectorAll('.ka-hf-filter[data-filter]');
2148 var templateItems = document.querySelectorAll('.ka-hf-template[data-template-type]');
2149 var templateCount = document.querySelector('.ka-hf-section-count');
2150 var filterEmptyState = document.querySelector('.ka-hf-filter-empty');
2151 var templatesList = document.querySelector('.ka-hf-templates');
2152
2153 var updateCount = function(count) {
2154 if (templateCount) {
2155 templateCount.textContent = count + ' ' + (count === 1 ? '<?php echo esc_js(__('item', 'king-addons')); ?>' : '<?php echo esc_js(__('items', 'king-addons')); ?>');
2156 }
2157 };
2158
2159 filterButtons.forEach(function(btn) {
2160 btn.addEventListener('click', function(e) {
2161 e.preventDefault();
2162 var filter = btn.getAttribute('data-filter');
2163
2164 // Update active state
2165 filterButtons.forEach(function(b) { b.classList.remove('is-active'); });
2166 btn.classList.add('is-active');
2167
2168 // Filter templates
2169 var visibleCount = 0;
2170 templateItems.forEach(function(item) {
2171 var type = item.getAttribute('data-template-type');
2172 if (filter === 'all' || type === filter) {
2173 item.style.display = '';
2174 visibleCount++;
2175 } else {
2176 item.style.display = 'none';
2177 }
2178 });
2179
2180 updateCount(visibleCount);
2181
2182 if (filterEmptyState && templatesList) {
2183 if (visibleCount === 0) {
2184 filterEmptyState.style.display = 'block';
2185 templatesList.style.display = 'none';
2186 } else {
2187 filterEmptyState.style.display = 'none';
2188 templatesList.style.display = '';
2189 }
2190 }
2191 });
2192 });
2193
2194 // ==========================================
2195 // Rename Template
2196 // ==========================================
2197 var renameBtns = document.querySelectorAll('.ka-hf-rename-btn');
2198 renameBtns.forEach(function(btn) {
2199 btn.addEventListener('click', function(e) {
2200 e.preventDefault();
2201 e.stopPropagation();
2202 var templateId = btn.getAttribute('data-id');
2203 var currentTitle = btn.getAttribute('data-title');
2204 if (renameModal && renameIdInput && renameTitleInput) {
2205 renameIdInput.value = templateId || '';
2206 renameTitleInput.value = currentTitle || '';
2207 renameModal.classList.add('is-open');
2208 renameModal.setAttribute('aria-hidden', 'false');
2209 renameTitleInput.focus();
2210 renameTitleInput.select();
2211 }
2212 });
2213 });
2214
2215 if (renameCloseButtons.length) {
2216 renameCloseButtons.forEach(function(btn) {
2217 btn.addEventListener('click', function() {
2218 if (renameModal) {
2219 renameModal.classList.remove('is-open');
2220 renameModal.setAttribute('aria-hidden', 'true');
2221 }
2222 });
2223 });
2224 }
2225
2226 if (renameModal) {
2227 renameModal.addEventListener('click', function(e) {
2228 if (e.target === renameModal) {
2229 renameModal.classList.remove('is-open');
2230 renameModal.setAttribute('aria-hidden', 'true');
2231 }
2232 });
2233 }
2234
2235 if (renameSaveBtn) {
2236 renameSaveBtn.addEventListener('click', function() {
2237 if (!renameIdInput || !renameTitleInput) return;
2238 var templateId = renameIdInput.value;
2239 var newTitle = (renameTitleInput.value || '').trim();
2240 if (!templateId || !newTitle) return;
2241
2242 var formData = new FormData();
2243 formData.append('action', 'ka_hf_rename_template');
2244 formData.append('nonce', kaHfNonce);
2245 formData.append('template_id', templateId);
2246 formData.append('new_title', newTitle);
2247
2248 fetch(kaHfAjaxUrl, {
2249 method: 'POST',
2250 body: formData
2251 })
2252 .then(function(response) { return response.json(); })
2253 .then(function(data) {
2254 if (data.success) {
2255 var templateEl = document.querySelector('.ka-hf-template[data-template-id="' + templateId + '"]');
2256 if (templateEl) {
2257 var titleText = templateEl.querySelector('.ka-hf-template-title-text');
2258 if (titleText) titleText.textContent = newTitle;
2259 var renameBtn = templateEl.querySelector('.ka-hf-rename-btn');
2260 if (renameBtn) renameBtn.setAttribute('data-title', newTitle);
2261 }
2262 if (renameModal) {
2263 renameModal.classList.remove('is-open');
2264 renameModal.setAttribute('aria-hidden', 'true');
2265 }
2266 } else {
2267 alert(data.data && data.data.message ? data.data.message : '<?php echo esc_js(__('Error renaming template', 'king-addons')); ?>');
2268 }
2269 })
2270 .catch(function(err) {
2271 alert('<?php echo esc_js(__('Error renaming template', 'king-addons')); ?>');
2272 // console.error(err);
2273 });
2274 });
2275 }
2276
2277 // ==========================================
2278 // Enable/Disable Template
2279 // ==========================================
2280 var toggleStatusBtns = document.querySelectorAll('.ka-hf-toggle-status-btn');
2281 toggleStatusBtns.forEach(function(btn) {
2282 btn.addEventListener('click', function(e) {
2283 e.preventDefault();
2284 e.stopPropagation();
2285 var templateId = btn.getAttribute('data-id');
2286 var currentStatus = btn.getAttribute('data-status');
2287 var newStatus = (currentStatus === 'publish') ? 'draft' : 'publish';
2288
2289 var formData = new FormData();
2290 formData.append('action', 'ka_hf_toggle_template_status');
2291 formData.append('nonce', kaHfNonce);
2292 formData.append('template_id', templateId);
2293 formData.append('new_status', newStatus);
2294
2295 fetch(kaHfAjaxUrl, {
2296 method: 'POST',
2297 body: formData
2298 })
2299 .then(function(response) { return response.json(); })
2300 .then(function(data) {
2301 if (data.success) {
2302 // Update button text and data attribute
2303 btn.setAttribute('data-status', newStatus);
2304 btn.textContent = (newStatus === 'publish') ? '<?php echo esc_js(__('Disable', 'king-addons')); ?>' : '<?php echo esc_js(__('Enable', 'king-addons')); ?>';
2305
2306 // Update status badge
2307 var templateEl = document.querySelector('.ka-hf-template[data-template-id="' + templateId + '"]');
2308 if (templateEl) {
2309 templateEl.setAttribute('data-status', newStatus);
2310 var statusBadge = templateEl.querySelector('.ka-hf-template-status');
2311 if (statusBadge) {
2312 if (newStatus === 'publish') {
2313 statusBadge.classList.remove('is-disabled');
2314 statusBadge.classList.add('is-enabled');
2315 var typeLabel = templateEl.getAttribute('data-type-label') || '';
2316 statusBadge.textContent = typeLabel || '<?php echo esc_js(__('Enabled', 'king-addons')); ?>';
2317 } else {
2318 statusBadge.classList.remove('is-enabled');
2319 statusBadge.classList.add('is-disabled');
2320 statusBadge.textContent = '<?php echo esc_js(__('Disabled', 'king-addons')); ?>';
2321 }
2322 }
2323 }
2324 } else {
2325 alert(data.data && data.data.message ? data.data.message : '<?php echo esc_js(__('Error updating template status', 'king-addons')); ?>');
2326 }
2327 })
2328 .catch(function(err) {
2329 alert('<?php echo esc_js(__('Error updating template status', 'king-addons')); ?>');
2330 // console.error(err);
2331 });
2332 });
2333 });
2334
2335 // ==========================================
2336 // Create Modal: Show/hide specific input
2337 // ==========================================
2338 var displayRuleSelect = document.getElementById('ka-hf-display-rule');
2339 var displaySpecificInput = document.getElementById('ka-hf-display-specific');
2340
2341 if (displayRuleSelect && displaySpecificInput) {
2342 displayRuleSelect.addEventListener('change', function() {
2343 if (this.value === 'specifics') {
2344 displaySpecificInput.style.display = 'block';
2345 } else {
2346 displaySpecificInput.style.display = 'none';
2347 displaySpecificInput.value = '';
2348 }
2349 });
2350 }
2351
2352 if (createAddUserRoleBtn) {
2353 createAddUserRoleBtn.addEventListener('click', function() {
2354 if (createUserRolesContainer && typeof addUserRoleRow === 'function') {
2355 addUserRoleRow(createUserRolesContainer, 'all', true);
2356 }
2357 });
2358 }
2359
2360 // ==========================================
2361 // Conditions Popup Functionality
2362 // ==========================================
2363 var conditionsModal = document.getElementById('ka-hf-conditions-modal');
2364 var conditionsCloseButtons = document.querySelectorAll('.ka-hf-conditions-close');
2365 var conditionButtons = document.querySelectorAll('.ka-hf-open-conditions');
2366 var saveConditionsBtn = document.getElementById('ka-hf-save-conditions');
2367 var ruleTemplate = document.getElementById('ka-hf-rule-template');
2368 var userRoleTemplate = document.getElementById('ka-hf-user-role-template');
2369 var userRolesContainer = document.getElementById('ka-hf-user-roles');
2370 var addUserRoleBtn = document.getElementById('ka-hf-add-user-role');
2371
2372 var openConditionsModal = function() {
2373 if (conditionsModal) {
2374 conditionsModal.classList.add('is-open');
2375 conditionsModal.setAttribute('aria-hidden', 'false');
2376 }
2377 };
2378
2379 var closeConditionsModal = function() {
2380 if (conditionsModal) {
2381 conditionsModal.classList.remove('is-open');
2382 conditionsModal.setAttribute('aria-hidden', 'true');
2383 }
2384 };
2385
2386 // Add rule row
2387 var addRuleRow = function(container, ruleValue, specificValue) {
2388 var templateContent = ruleTemplate.content.cloneNode(true);
2389 var row = templateContent.querySelector('.ka-hf-rule-row');
2390 var select = row.querySelector('.ka-hf-rule-select');
2391 var specificInput = row.querySelector('.ka-hf-specific-input');
2392 var removeBtn = row.querySelector('.ka-hf-remove-rule-btn');
2393
2394 if (ruleValue) {
2395 select.value = ruleValue;
2396 }
2397
2398 // Show specific input if needed
2399 if (ruleValue === 'specifics') {
2400 specificInput.style.display = 'block';
2401 if (specificValue) {
2402 specificInput.value = specificValue;
2403 }
2404 }
2405
2406 // Handle select change
2407 select.addEventListener('change', function() {
2408 if (this.value === 'specifics') {
2409 specificInput.style.display = 'block';
2410 } else {
2411 specificInput.style.display = 'none';
2412 specificInput.value = '';
2413 }
2414 });
2415
2416 // Handle remove
2417 removeBtn.addEventListener('click', function() {
2418 row.remove();
2419 });
2420
2421 container.appendChild(row);
2422 };
2423
2424 // Add user role row
2425 var addUserRoleRow = function(container, roleValue, includeNameAttr) {
2426 if (!container || !userRoleTemplate) return;
2427 var templateContent = userRoleTemplate.content.cloneNode(true);
2428 var row = templateContent.querySelector('.ka-hf-user-role-row');
2429 var select = row.querySelector('.ka-hf-user-role-select');
2430 var removeBtn = row.querySelector('.ka-hf-remove-rule-btn');
2431
2432 if (includeNameAttr) {
2433 select.setAttribute('name', 'ka_hf_user_role[]');
2434 }
2435 if (roleValue) {
2436 select.value = roleValue;
2437 }
2438
2439 removeBtn.addEventListener('click', function() {
2440 row.remove();
2441 });
2442
2443 container.appendChild(row);
2444 };
2445
2446 // Create modal default row (after helper is defined)
2447 if (createUserRolesContainer && createUserRolesContainer.children.length === 0) {
2448 addUserRoleRow(createUserRolesContainer, 'all', true);
2449 }
2450
2451 // Load template data into popup
2452 var loadConditionsData = function(templateData) {
2453 var includeContainer = document.getElementById('ka-hf-include-rules');
2454 var excludeContainer = document.getElementById('ka-hf-exclude-rules');
2455 var templateIdInput = document.getElementById('ka-hf-cond-template-id');
2456 var templateTypeSelect = document.getElementById('ka-hf-cond-template-type');
2457
2458 // Clear existing rules
2459 includeContainer.innerHTML = '';
2460 excludeContainer.innerHTML = '';
2461 if (userRolesContainer) {
2462 userRolesContainer.innerHTML = '';
2463 }
2464
2465 // Set template ID
2466 templateIdInput.value = templateData.id;
2467
2468 // Set template type
2469 if (templateTypeSelect && templateData.type) {
2470 templateTypeSelect.value = templateData.type;
2471 }
2472
2473 // Load include rules
2474 var includeRules = templateData.include && templateData.include.rule ? templateData.include.rule : [];
2475 var includeSpecific = templateData.include && templateData.include.specific ? templateData.include.specific : [];
2476
2477 if (includeRules.length === 0) {
2478 addRuleRow(includeContainer, 'basic-global', '');
2479 } else {
2480 includeRules.forEach(function(rule, idx) {
2481 var specificVal = (rule === 'specifics' && includeSpecific.length) ? includeSpecific.join(',') : '';
2482 addRuleRow(includeContainer, rule, specificVal);
2483 });
2484 }
2485
2486 // Load exclude rules
2487 var excludeRules = templateData.exclude && templateData.exclude.rule ? templateData.exclude.rule : [];
2488 var excludeSpecific = templateData.exclude && templateData.exclude.specific ? templateData.exclude.specific : [];
2489
2490 excludeRules.forEach(function(rule, idx) {
2491 var specificVal = (rule === 'specifics' && excludeSpecific.length) ? excludeSpecific.join(',') : '';
2492 addRuleRow(excludeContainer, rule, specificVal);
2493 });
2494
2495 // Load user roles (rows)
2496 var userRoles = templateData.userRoles || [];
2497 if (!Array.isArray(userRoles) || userRoles.length === 0) {
2498 userRoles = ['all'];
2499 }
2500 userRoles.forEach(function(role) {
2501 addUserRoleRow(userRolesContainer, role, false);
2502 });
2503 };
2504
2505 // Condition button click handlers
2506 conditionButtons.forEach(function(btn) {
2507 btn.addEventListener('click', function(e) {
2508 e.preventDefault();
2509 e.stopPropagation();
2510 var templateData = JSON.parse(btn.getAttribute('data-template'));
2511 loadConditionsData(templateData);
2512 openConditionsModal();
2513 });
2514 });
2515
2516 // Add rule buttons
2517 document.querySelectorAll('.ka-hf-add-rule-btn').forEach(function(btn) {
2518 btn.addEventListener('click', function() {
2519 var ruleType = btn.getAttribute('data-rule-type');
2520 var container = document.getElementById('ka-hf-' + ruleType + '-rules');
2521 addRuleRow(container, 'basic-global', '');
2522 });
2523 });
2524
2525 if (addUserRoleBtn) {
2526 addUserRoleBtn.addEventListener('click', function() {
2527 addUserRoleRow(userRolesContainer, 'all', false);
2528 });
2529 }
2530
2531 // Close buttons
2532 conditionsCloseButtons.forEach(function(btn) {
2533 btn.addEventListener('click', closeConditionsModal);
2534 });
2535
2536 // Backdrop click
2537 if (conditionsModal) {
2538 conditionsModal.addEventListener('click', function(e) {
2539 if (e.target === conditionsModal) {
2540 closeConditionsModal();
2541 }
2542 });
2543 }
2544
2545 // Save conditions via AJAX
2546 if (saveConditionsBtn) {
2547 saveConditionsBtn.addEventListener('click', function() {
2548 var savingOverlay = document.getElementById('ka-hf-conditions-saving');
2549 savingOverlay.style.display = 'flex';
2550
2551 var templateId = document.getElementById('ka-hf-cond-template-id').value;
2552 var templateType = document.getElementById('ka-hf-cond-template-type').value;
2553
2554 // Collect user roles
2555 var selectedRoles = [];
2556 document.querySelectorAll('#ka-hf-user-roles .ka-hf-user-role-select').forEach(function(sel) {
2557 if (sel && sel.value) {
2558 selectedRoles.push(sel.value);
2559 }
2560 });
2561 // De-dupe and default
2562 selectedRoles = Array.from(new Set(selectedRoles));
2563 if (selectedRoles.length === 0) {
2564 selectedRoles = ['all'];
2565 }
2566 if (selectedRoles.indexOf('all') !== -1) {
2567 selectedRoles = ['all'];
2568 }
2569
2570 // Collect include rules
2571 var includeRules = [];
2572 document.querySelectorAll('#ka-hf-include-rules .ka-hf-rule-row').forEach(function(row) {
2573 var rule = row.querySelector('.ka-hf-rule-select').value;
2574 var specific = row.querySelector('.ka-hf-specific-input').value;
2575 includeRules.push({ rule: rule, specific: specific });
2576 });
2577
2578 // Collect exclude rules
2579 var excludeRules = [];
2580 document.querySelectorAll('#ka-hf-exclude-rules .ka-hf-rule-row').forEach(function(row) {
2581 var rule = row.querySelector('.ka-hf-rule-select').value;
2582 var specific = row.querySelector('.ka-hf-specific-input').value;
2583 excludeRules.push({ rule: rule, specific: specific });
2584 });
2585
2586 // Build form data
2587 var formData = new FormData();
2588 formData.append('action', 'ka_hf_save_conditions');
2589 formData.append('nonce', kaHfNonce);
2590 formData.append('template_id', templateId);
2591 formData.append('template_type', templateType);
2592
2593 // Append multiple user roles
2594 selectedRoles.forEach(function(role, idx) {
2595 formData.append('user_roles[' + idx + ']', role);
2596 });
2597
2598 includeRules.forEach(function(rule, idx) {
2599 formData.append('include_rules[' + idx + '][rule]', rule.rule);
2600 formData.append('include_rules[' + idx + '][specific]', rule.specific);
2601 });
2602
2603 excludeRules.forEach(function(rule, idx) {
2604 formData.append('exclude_rules[' + idx + '][rule]', rule.rule);
2605 formData.append('exclude_rules[' + idx + '][specific]', rule.specific);
2606 });
2607
2608 fetch(kaHfAjaxUrl, {
2609 method: 'POST',
2610 body: formData
2611 })
2612 .then(function(response) { return response.json(); })
2613 .then(function(data) {
2614 savingOverlay.style.display = 'none';
2615 if (data.success) {
2616 closeConditionsModal();
2617 // Reload page to show updated conditions
2618 window.location.reload();
2619 } else {
2620 alert(data.data && data.data.message ? data.data.message : 'Error saving conditions');
2621 }
2622 })
2623 .catch(function(err) {
2624 savingOverlay.style.display = 'none';
2625 alert('Error saving conditions');
2626 // console.error(err);
2627 });
2628 });
2629 }
2630 });
2631 </script>
2632 <?php
2633 }
2634
2635 public function disableScreenOptions($show_screen, $screen)
2636 {
2637 if ($screen->id === 'edit-king-addons-el-hf') {
2638 return false;
2639 }
2640 return $show_screen;
2641 }
2642
2643 function king_addons_el_hf_get_posts_by_query()
2644 {
2645 // Security fix: Add authorization check
2646 if (!current_user_can('edit_posts')) {
2647 wp_send_json_error('Insufficient permissions');
2648 return;
2649 }
2650
2651 check_ajax_referer('king-addons-el-hf-get-posts-by-query', 'nonce');
2652
2653 $search_string = isset($_POST['q']) ? sanitize_text_field($_POST['q']) : '';
2654 $result = array();
2655
2656 $args = array(
2657 'public' => true,
2658 '_builtin' => false,
2659 );
2660
2661 $output = 'names';
2662 $operator = 'and';
2663 $post_types = get_post_types($args, $output, $operator);
2664
2665 unset($post_types['elementor-hf']);
2666
2667 $post_types['Posts'] = 'post';
2668 $post_types['Pages'] = 'page';
2669
2670 foreach ($post_types as $key => $post_type) {
2671 $data = array();
2672
2673 add_filter('posts_search', array($this, 'search_only_titles'), 10, 2);
2674
2675 $query = new WP_Query(
2676 array(
2677 's' => $search_string,
2678 'post_type' => $post_type,
2679 'posts_per_page' => -1,
2680 )
2681 );
2682
2683 if ($query->have_posts()) {
2684 while ($query->have_posts()) {
2685 $query->the_post();
2686 $title = get_the_title();
2687 $title .= (0 != $query->post->post_parent) ? ' (' . get_the_title($query->post->post_parent) . ')' : '';
2688 $id = get_the_id();
2689 $data[] = array(
2690 'id' => 'post-' . $id,
2691 'text' => $title,
2692 );
2693 }
2694 }
2695
2696 if (is_array($data) && !empty($data)) {
2697 $result[] = array(
2698 'text' => $key,
2699 'children' => $data,
2700 );
2701 }
2702 }
2703
2704 wp_reset_postdata();
2705
2706 $args = array(
2707 'public' => true,
2708 );
2709
2710 $output = 'objects';
2711 $taxonomies = get_taxonomies($args, $output, $operator);
2712
2713 foreach ($taxonomies as $taxonomy) {
2714 $terms = get_terms(
2715 $taxonomy->name,
2716 array(
2717 'orderby' => 'count',
2718 'hide_empty' => 0,
2719 'name__like' => $search_string,
2720 )
2721 );
2722
2723 $data = array();
2724
2725 $label = ucwords($taxonomy->label);
2726
2727 if (!empty($terms)) {
2728 foreach ($terms as $term) {
2729
2730 $data[] = array(
2731 'id' => 'tax-' . $term->term_id,
2732 'text' => $term->name . ' archive page',
2733 );
2734
2735 $data[] = array(
2736 'id' => 'tax-' . $term->term_id . '-single-' . $taxonomy->name,
2737 'text' => 'All singulars from ' . $term->name,
2738 );
2739 }
2740 }
2741
2742 if (is_array($data) && !empty($data)) {
2743 $result[] = array(
2744 'text' => $label,
2745 'children' => $data,
2746 );
2747 }
2748 }
2749
2750 wp_send_json($result);
2751 }
2752
2753 public function initialize_options()
2754 {
2755 self::$user_selection = self::get_user_selections();
2756 self::$location_selection = self::getLocationSelections();
2757 }
2758
2759 public function renderAdminCustomHeader()
2760 {
2761 $current_screen = get_current_screen()->id;
2762 if ($current_screen !== 'edit-king-addons-el-hf'
2763 && $current_screen !== 'header-footer_page_king-addons-el-hf-settings') {
2764 return;
2765 }
2766
2767 ?>
2768 <div class="king-addons-pb-settings-page-header">
2769 <h1><?php esc_html_e('Elementor Header & Footer Builder', 'king-addons'); ?></h1>
2770 <p>
2771 <?php esc_html_e('Create fully customizable headers and footers with display conditions to control where they appear', 'king-addons'); ?>
2772 </p>
2773 <div class="king-addons-pb-preview-buttons">
2774 <a href="<?php echo admin_url('post-new.php?post_type=king-addons-el-hf'); ?>">
2775 <div class="king-addons-pb-user-template">
2776 <span><?php esc_html_e('Create New', 'king-addons'); ?></span>
2777 <span class="plus-icon">+</span>
2778 </div>
2779 </a>
2780 <?php if (!king_addons_freemius()->can_use_premium_code__premium_only()): ?>
2781 <div class="kng-promo-btn-wrap">
2782 <a href="https://kingaddons.com/pricing/?utm_source=king-addons-hf-builder" target="_blank">
2783 <div class="kng-promo-btn-txt">
2784 <?php esc_html_e('Unlock Premium Features & 650+ Templates Today!', 'king-addons'); ?>
2785 </div>
2786 <img width="16px"
2787 src="<?php echo esc_url(KING_ADDONS_URL) . 'includes/admin/img/share-v2.svg'; ?>"
2788 alt="<?php echo esc_html__('Open link in the new tab', 'king-addons'); ?>">
2789 </a>
2790 </div>
2791 <?php endif; ?>
2792 </div>
2793 </div>
2794 <?php
2795
2796 $counts = wp_count_posts('king-addons-el-hf');
2797 $total = (int)$counts->publish + (int)$counts->draft;
2798
2799 if (0 === $total) {
2800 echo '<div class="notice notice-info">';
2801 echo '<p>';
2802 echo esc_html__("Create the first header or footer by clicking the 'Create New' button above.", 'king addons');
2803 echo '</p>';
2804 echo '</div>';
2805 }
2806
2807 }
2808
2809 function addPostType(): void
2810 {
2811 if (!current_user_can('manage_options')) {
2812 return;
2813 }
2814
2815 $labels = [
2816 'name' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2817 'singular_name' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2818 'menu_name' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2819 'name_admin_bar' => esc_html__('Elementor Header & Footer Builder', 'king-addons'),
2820 'add_new' => esc_html__('Add New', 'king-addons'),
2821 'add_new_item' => esc_html__('Add New', 'king-addons'),
2822 'new_item' => esc_html__('New Template', 'king-addons'),
2823 'edit_item' => esc_html__('Edit Template', 'king-addons'),
2824 'view_item' => esc_html__('View Template', 'king-addons'),
2825 'all_items' => esc_html__('All Templates', 'king-addons'),
2826 'search_items' => esc_html__('Search Templates', 'king-addons'),
2827 'parent_item_colon' => esc_html__('Parent Templates:', 'king-addons'),
2828 'not_found' => esc_html__('No Templates found.', 'king-addons'),
2829 'not_found_in_trash' => esc_html__('No Templates found in Trash.', 'king-addons'),
2830 ];
2831
2832 $args = [
2833 'labels' => $labels,
2834 'public' => true,
2835 'show_ui' => true,
2836 'show_in_menu' => false,
2837 'show_in_nav_menus' => false,
2838 'exclude_from_search' => true,
2839 'capability_type' => 'post',
2840 'hierarchical' => false,
2841 'menu_icon' => 'dashicons-editor-kitchensink',
2842 'supports' => ['title', 'editor', 'thumbnail', 'elementor'],
2843 'show_in_rest' => true,
2844 ];
2845
2846 register_post_type('king-addons-el-hf', $args);
2847
2848 if (false === get_option('king_addons_HFB_flushed_rewrite_rules')) {
2849 add_option('king_addons_HFB_flushed_rewrite_rules', true);
2850 flush_rewrite_rules();
2851 }
2852 }
2853
2854 function registerMetabox()
2855 {
2856 add_meta_box(
2857 'king-addons-el-hf-meta-box',
2858 esc_html__('Elementor Header & Footer Builder Options', 'king-addons'),
2859 [$this, 'renderMetabox'],
2860 'king-addons-el-hf',
2861 'normal',
2862 'high'
2863 );
2864 }
2865
2866 function renderMetabox($post)
2867 {
2868 $values = get_post_custom($post->ID);
2869 $template_type = isset($values['king_addons_el_hf_template_type']) ? esc_attr(sanitize_text_field($values['king_addons_el_hf_template_type'][0])) : '';
2870 $display_on_canvas = isset($values['king-addons-el-hf-display-on-canvas']);
2871
2872 wp_nonce_field('king_addons_el_hf_meta_nounce', 'king_addons_el_hf_meta_nounce');
2873 ?>
2874 <table class="king-addons-el-hf-options-table widefat">
2875 <tbody>
2876 <tr class="king-addons-el-hf-options-row type-of-template">
2877 <td class="king-addons-el-hf-options-row-heading">
2878 <label for="king_addons_el_hf_template_type"><strong><?php esc_html_e('Type of Template', 'king-addons'); ?></strong></label>
2879 </td>
2880 <td class="king-addons-el-hf-options-row-content">
2881 <select name="king_addons_el_hf_template_type" id="king_addons_el_hf_template_type">
2882 <option value="king_addons_el_hf_not_selected" <?php selected($template_type, ''); ?>><?php esc_html_e('Select Option', 'king-addons'); ?></option>
2883 <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>
2884 <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>
2885 </select>
2886 </td>
2887 </tr>
2888 <?php
2889 $this->display_rules_tab();
2890
2891 ?>
2892 <tr class="king-addons-el-hf-options-row enable-for-canvas">
2893 <td class="king-addons-el-hf-options-row-heading">
2894 <label for="king-addons-el-hf-display-on-canvas">
2895 <strong><?php esc_html_e('Enable Layout for Elementor Canvas Template?', 'king-addons'); ?></strong>
2896 </label>
2897 <p><?php esc_html_e('Enabling this option will display this layout on pages using Elementor Canvas Template', 'king-addons'); ?></p>
2898 </td>
2899 <td class="king-addons-el-hf-options-row-content">
2900 <input type="checkbox" id="king-addons-el-hf-display-on-canvas"
2901 name="king-addons-el-hf-display-on-canvas"
2902 value="1" <?php checked($display_on_canvas); ?> />
2903 </td>
2904 </tr>
2905 </tbody>
2906 </table>
2907 <?php
2908 }
2909
2910
2911 public function admin_styles()
2912 {
2913 wp_enqueue_script('king-addons-el-hf-select2', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/select2.js', array('jquery'), KING_ADDONS_VERSION, true);
2914
2915 wp_register_script(
2916 'king-addons-el-hf-target-rule',
2917 KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/conditions-target.js',
2918 array(
2919 'jquery',
2920 'king-addons-el-hf-select2',
2921 ),
2922 KING_ADDONS_VERSION,
2923 true
2924 );
2925
2926 wp_enqueue_script('king-addons-el-hf-target-rule');
2927
2928 wp_register_script(
2929 'king-addons-el-hf-user-role',
2930 KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/conditions-user.js',
2931 array(
2932 'jquery',
2933 ),
2934 KING_ADDONS_VERSION,
2935 true
2936 );
2937
2938 wp_enqueue_script('king-addons-el-hf-user-role');
2939
2940 wp_register_style('king-addons-el-hf-select2', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/select2.css', '', KING_ADDONS_VERSION);
2941 wp_enqueue_style('king-addons-el-hf-select2');
2942 wp_register_style('king-addons-el-hf-target-rule', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/conditions.css', '', KING_ADDONS_VERSION);
2943 wp_enqueue_style('king-addons-el-hf-target-rule');
2944 wp_enqueue_script('king-addons-el-hf-script', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/admin.js', array('jquery'), KING_ADDONS_VERSION);
2945
2946 $localize_vars = array(
2947 'please_enter' => __('Please enter', 'king-addons'),
2948 'please_delete' => __('Please delete', 'king-addons'),
2949 'more_char' => __('or more characters', 'king-addons'),
2950 'character' => __('character', 'king-addons'),
2951 'loading' => __('Loading more results…', 'king-addons'),
2952 'only_select' => __('You can only select', 'king-addons'),
2953 'item' => __('item', 'king-addons'),
2954 'char_s' => __('s', 'king-addons'),
2955 'no_result' => __('No results found', 'king-addons'),
2956 'searching' => __('Searching…', 'king-addons'),
2957 'not_loader' => __('The results could not be loaded.', 'king-addons'),
2958 'search' => __('Search pages / post / categories', 'king-addons'),
2959 'ajax_nonce' => wp_create_nonce('king-addons-el-hf-get-posts-by-query'),
2960 );
2961 wp_localize_script('king-addons-el-hf-select2', 'kngRules', $localize_vars);
2962
2963 }
2964
2965 public function display_rules_tab()
2966 {
2967 $this->admin_styles();
2968 $include_locations = get_post_meta(get_the_id(), 'king_addons_el_hf_target_include_locations', true);
2969 $exclude_locations = get_post_meta(get_the_id(), 'king_addons_el_hf_target_exclude_locations', true);
2970 $users = get_post_meta(get_the_id(), 'king_addons_el_hf_target_user_roles', true);
2971 ?>
2972 <tr class="king-addons-el-hf-target-rules-row king-addons-el-hf-options-row">
2973 <td class="king-addons-el-hf-target-rules-row-heading king-addons-el-hf-options-row-heading">
2974 <label><strong><?php esc_html_e('Display On', 'king-addons'); ?></strong></label>
2975 <p><?php esc_html_e('Add locations for where this template should appear', 'king-addons'); ?></p>
2976 </td>
2977 <td class="king-addons-el-hf-target-rules-row-content king-addons-el-hf-options-row-content">
2978 <?php
2979 self::target_rule_settings_field(
2980 'king-addons-el-hf-target-rules-location',
2981 [
2982 'title' => __('Display Rules', 'king-addons'),
2983 'value' => '[{"type":"basic-global","specific":null}]',
2984 'tags' => 'site,enable,target,pages',
2985 'rule_type' => 'display',
2986 'add_rule_label' => __('Add Display Rule', 'king-addons'),
2987 ],
2988 $include_locations
2989 );
2990 ?>
2991 </td>
2992 </tr>
2993 <tr class="king-addons-el-hf-target-rules-row king-addons-el-hf-options-row">
2994 <td class="king-addons-el-hf-target-rules-row-heading king-addons-el-hf-options-row-heading">
2995 <label><strong><?php esc_html_e('Do Not Display On', 'king-addons'); ?></strong></label>
2996 <p><?php esc_html_e('Add locations for where this template should not appear', 'king-addons'); ?></p>
2997 </td>
2998 <td class="king-addons-el-hf-target-rules-row-content king-addons-el-hf-options-row-content">
2999 <?php
3000 self::target_rule_settings_field(
3001 'king-addons-el-hf-target-rules-exclusion',
3002 [
3003 'title' => __('Exclude On', 'king-addons'),
3004 'value' => '[]',
3005 'tags' => 'site,enable,target,pages',
3006 'add_rule_label' => __('Add Exclusion Rule', 'king-addons'),
3007 'rule_type' => 'exclude',
3008 ],
3009 $exclude_locations
3010 );
3011 ?>
3012 </td>
3013 </tr>
3014 <tr class="king-addons-el-hf-target-rules-row king-addons-el-hf-options-row">
3015 <td class="king-addons-el-hf-target-rules-row-heading king-addons-el-hf-options-row-heading">
3016 <label><strong><?php esc_html_e('User Roles', 'king-addons'); ?></strong></label>
3017 <p><?php esc_html_e('Display custom template based on user role', 'king-addons'); ?></p>
3018 </td>
3019 <td class="king-addons-el-hf-target-rules-row-content king-addons-el-hf-options-row-content">
3020 <?php
3021 self::target_user_role_settings_field(
3022 'king-addons-el-hf-target-rules-users',
3023 [
3024 'title' => __('Users', 'king-addons'),
3025 'value' => '[]',
3026 'tags' => 'site,enable,target,pages',
3027 'add_rule_label' => __('Add User Rule', 'king-addons'),
3028 ],
3029 $users
3030 );
3031 ?>
3032 </td>
3033 </tr>
3034 <?php
3035 }
3036
3037 public static function get_user_selections()
3038 {
3039 $selection_options = array(
3040 'basic' => array(
3041 'label' => __('Basic', 'king-addons'),
3042 'value' => array(
3043 'all' => __('All', 'king-addons'),
3044 'logged-in' => __('Logged In', 'king-addons'),
3045 'logged-out' => __('Logged Out', 'king-addons'),
3046 ),
3047 ),
3048
3049 'advanced' => array(
3050 'label' => __('Advanced', 'king-addons'),
3051 'value' => array(),
3052 ),
3053 );
3054
3055 /* User roles */
3056 $roles = get_editable_roles();
3057
3058 foreach ($roles as $slug => $data) {
3059 $selection_options['advanced']['value'][$slug] = $data['name'];
3060 }
3061
3062 /**
3063 * Filter options displayed in the user select field of Display conditions.
3064 *
3065 * @since 1.5.0
3066 */
3067 return apply_filters('king-addons-el-hf_user_roles_list', $selection_options);
3068 }
3069
3070 public static function target_user_role_settings_field($name, $settings, $value)
3071 {
3072 $input_name = $name;
3073 $add_rule_label = $settings['add_rule_label'] ?? __('Add Rule', 'king-addons');
3074 $saved_values = $value;
3075 $output = '';
3076
3077 if (!isset(self::$user_selection) || empty(self::$user_selection)) {
3078 self::$user_selection = self::get_user_selections();
3079 }
3080 $selection_options = self::$user_selection;
3081
3082 $output .= '<script type="text/html" id="tmpl-king-addons-el-hf-user-role-condition">';
3083 $output .= '<div class="king-addons-el-hf-user-role-condition king-addons-el-hf-user-role-{{data.id}}" data-rule="{{data.id}}" >';
3084 $output .= '<span class="user_role-condition-delete dashicons dashicons-dismiss"></span>';
3085
3086 $output .= '<div class="user_role-condition-wrap" >';
3087 $output .= '<select name="' . esc_attr($input_name) . '[{{data.id}}]" class="user_role-condition form-control king-addons-el-hf-input">';
3088 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3089
3090 foreach ($selection_options as $group_data) {
3091 $output .= '<optgroup label="' . $group_data['label'] . '">';
3092 foreach ($group_data['value'] as $opt_key => $opt_value) {
3093 $output .= '<option value="' . $opt_key . '">' . $opt_value . '</option>';
3094 }
3095 $output .= '</optgroup>';
3096 }
3097 $output .= '</select>';
3098 $output .= '</div>';
3099 $output .= '</div> <!-- king-addons-el-hf-user-role-condition -->';
3100 $output .= '</script>';
3101
3102 /** @noinspection PhpConditionAlreadyCheckedInspection */
3103 if (!is_array($saved_values) || (is_array($saved_values) && empty($saved_values))) {
3104 $saved_values = array();
3105 $saved_values[0] = '';
3106 }
3107
3108 $index = 0;
3109
3110 $output .= '<div class="king-addons-el-hf-user-role-wrapper king-addons-el-hf-user-role-display-on-wrap" data-type="display">';
3111 $output .= '<div class="king-addons-el-hf-user-role-selector-wrapper king-addons-el-hf-user-role-display-on">';
3112 $output .= '<div class="user_role-builder-wrap">';
3113 foreach ($saved_values as $index => $data) {
3114 $output .= '<div class="king-addons-el-hf-user-role-condition king-addons-el-hf-user-role-' . $index . '" data-rule="' . $index . '" >';
3115 $output .= '<span class="user_role-condition-delete dashicons dashicons-dismiss"></span>';
3116 /* Condition Selection */
3117 $output .= '<div class="user_role-condition-wrap" >';
3118 $output .= '<select name="' . esc_attr($input_name) . '[' . $index . ']" class="user_role-condition form-control king-addons-el-hf-input">';
3119 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3120
3121 foreach ($selection_options as $group_data) {
3122 $output .= '<optgroup label="' . $group_data['label'] . '">';
3123 foreach ($group_data['value'] as $opt_key => $opt_value) {
3124 $output .= '<option value="' . $opt_key . '" ' . selected($data, $opt_key, false) . '>' . $opt_value . '</option>';
3125 }
3126 $output .= '</optgroup>';
3127 }
3128 $output .= '</select>';
3129 $output .= '</div>';
3130 $output .= '</div> <!-- king-addons-el-hf-user-role-condition -->';
3131 }
3132 $output .= '</div>';
3133 /* Add new rule */
3134 $output .= '<div class="user_role-add-rule-wrap">';
3135 $output .= '<a href="#" class="button" data-rule-id="' . absint($index) . '">' . $add_rule_label . '</a>';
3136 $output .= '</div>';
3137 $output .= '</div>';
3138 $output .= '</div>';
3139
3140 echo $output;
3141 }
3142
3143 public static function target_rule_settings_field($name, $settings, $value)
3144 {
3145 $input_name = $name;
3146 $rule_type = $settings['rule_type'] ?? 'target_rule';
3147 $add_rule_label = $settings['add_rule_label'] ?? __('Add Rule', 'king-addons');
3148 $saved_values = $value;
3149 $output = '';
3150
3151 if (isset(self::$location_selection) || empty(self::$location_selection)) {
3152 self::$location_selection = self::getLocationSelections();
3153 }
3154 $selection_options = self::$location_selection;
3155
3156 $output .= '<script type="text/html" id="tmpl-king-addons-el-hf-target-rule-' . $rule_type . '-condition">';
3157
3158 $output .= '<div class="king-addons-el-hf-target-rule-condition king-addons-el-hf-target-rule-{{data.id}}" data-rule="{{data.id}}" >';
3159 $output .= '<span class="target_rule-condition-delete dashicons dashicons-dismiss"></span>';
3160 $output .= '<div class="target_rule-condition-wrap" >';
3161
3162 $output .= '<select name="' . esc_attr($input_name) . '[rule][{{data.id}}]" class="target_rule-condition form-control king-addons-el-hf-input">';
3163 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3164
3165 foreach ($selection_options as $group_data) {
3166 $output .= '<optgroup label="' . $group_data['label'] . '">';
3167 foreach ($group_data['value'] as $opt_key => $opt_value) {
3168 $output .= '<option value="' . $opt_key . '">' . $opt_value . '</option>';
3169 }
3170 $output .= '</optgroup>';
3171 }
3172 $output .= '</select>';
3173
3174 $output .= '</div>';
3175 $output .= '</div> <!-- king-addons-el-hf-target-rule-condition -->';
3176
3177 $output .= '<div class="target_rule-specific-page-wrap" style="display:none">';
3178 $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">';
3179 $output .= '</select>';
3180 $output .= '</div>';
3181
3182 $output .= '</script>';
3183
3184 $output .= '<div class="king-addons-el-hf-target-rule-wrapper king-addons-el-hf-target-rule-' . $rule_type . '-on-wrap" data-type="' . $rule_type . '">';
3185 $output .= '<div class="king-addons-el-hf-target-rule-selector-wrapper king-addons-el-hf-target-rule-' . $rule_type . '-on">';
3186 $output .= self::generate_target_rule_selector($rule_type, $selection_options, $input_name, $saved_values, $add_rule_label);
3187 $output .= '</div>';
3188 $output .= '</div>';
3189
3190 echo $output;
3191 }
3192
3193 public static function generate_target_rule_selector($type, $selection_options, $input_name, $saved_values, $add_rule_label)
3194 {
3195 $output = '<div class="target_rule-builder-wrap">';
3196
3197 /** @noinspection PhpConditionAlreadyCheckedInspection */
3198 if (!is_array($saved_values) || (is_array($saved_values) && empty($saved_values))) {
3199 $saved_values = array();
3200 $saved_values['rule'][0] = '';
3201 $saved_values['specific'][0] = '';
3202 }
3203
3204 $index = 0;
3205 if (is_array($saved_values) && is_array($saved_values['rule'])) {
3206 foreach ($saved_values['rule'] as $index => $data) {
3207 $output .= '<div class="king-addons-el-hf-target-rule-condition king-addons-el-hf-target-rule-' . $index . '" data-rule="' . $index . '" >';
3208
3209 $output .= '<span class="target_rule-condition-delete dashicons dashicons-dismiss"></span>';
3210 $output .= '<div class="target_rule-condition-wrap" >';
3211 $output .= '<select name="' . esc_attr($input_name) . '[rule][' . $index . ']" class="target_rule-condition form-control king-addons-el-hf-input">';
3212 $output .= '<option value="">' . __('Select', 'king-addons') . '</option>';
3213
3214 foreach ($selection_options as $group_data) {
3215 $output .= '<optgroup label="' . $group_data['label'] . '">';
3216 foreach ($group_data['value'] as $opt_key => $opt_value) {
3217
3218 $selected = '';
3219
3220 if ($data == $opt_key) {
3221 $selected = 'selected="selected"';
3222 }
3223
3224 $output .= '<option value="' . $opt_key . '" ' . $selected . '>' . $opt_value . '</option>';
3225 }
3226 $output .= '</optgroup>';
3227 }
3228 $output .= '</select>';
3229 $output .= '</div>';
3230
3231 $output .= '</div>';
3232
3233 $output .= '<div class="target_rule-specific-page-wrap" style="display:none">';
3234 $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">';
3235
3236 if ('specifics' == $data && isset($saved_values['specific']) && null != $saved_values['specific'] && is_array($saved_values['specific'])) {
3237 foreach ($saved_values['specific'] as $sel_value) {
3238
3239 if (strpos($sel_value, 'post-') !== false) {
3240 $post_id = (int)str_replace('post-', '', $sel_value);
3241 $post_title = get_the_title($post_id);
3242 $output .= '<option value="post-' . $post_id . '" selected="selected" >' . $post_title . '</option>';
3243 }
3244
3245 if (strpos($sel_value, 'tax-') !== false) {
3246 $tax_data = explode('-', $sel_value);
3247
3248 $tax_id = (int)str_replace('tax-', '', $sel_value);
3249 $term = get_term($tax_id);
3250 $term_name = '';
3251
3252 if (!is_wp_error($term)) {
3253 $term_taxonomy = ucfirst(str_replace('_', ' ', $term->taxonomy));
3254
3255 if (isset($tax_data[2]) && 'single' === $tax_data[2]) {
3256 $term_name = 'All singulars from ' . $term->name;
3257 } else {
3258 $term_name = $term->name . ' - ' . $term_taxonomy;
3259 }
3260 }
3261
3262 $output .= '<option value="' . $sel_value . '" selected="selected" >' . $term_name . '</option>';
3263 }
3264 }
3265 }
3266 $output .= '</select>';
3267 $output .= '</div>';
3268 }
3269 }
3270
3271 $output .= '</div>';
3272
3273 $output .= '<div class="target_rule-add-rule-wrap">';
3274 $output .= '<a href="#" class="button" data-rule-id="' . absint($index) . '" data-rule-type="' . $type . '">' . $add_rule_label . '</a>';
3275 $output .= '</div>';
3276
3277 if ('display' == $type) {
3278 $output .= '<div class="target_rule-add-exclusion-rule">';
3279 $output .= '<a href="#" class="button">' . __('Add Exclusion Rule', 'king-addons') . '</a>';
3280 $output .= '</div>';
3281 }
3282
3283 return $output;
3284 }
3285
3286 function saveMetaboxData($post_id)
3287 {
3288 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
3289 return;
3290 }
3291
3292 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')) {
3293 return;
3294 }
3295
3296 if (!current_user_can('edit_posts')) {
3297 return;
3298 }
3299
3300 if (!isset($_POST['king-addons-el-hf-target-rules-location'])) {
3301 $target_locations = array(
3302 'rule' => array('basic-global'),
3303 'specific' => array(),
3304 );
3305 } else {
3306 $target_locations = self::getFormatRuleValue($_POST, 'king-addons-el-hf-target-rules-location');
3307 if (empty($target_locations)) {
3308 $target_locations = array(
3309 'rule' => array('basic-global'),
3310 'specific' => array(),
3311 );
3312 }
3313 }
3314
3315 $target_exclusion = self::getFormatRuleValue($_POST, 'king-addons-el-hf-target-rules-exclusion');
3316 $target_users = [];
3317
3318 if (isset($_POST['king-addons-el-hf-target-rules-users'])) {
3319 $target_users = array_map('sanitize_text_field', wp_unslash($_POST['king-addons-el-hf-target-rules-users']));
3320 }
3321
3322 update_post_meta($post_id, 'king_addons_el_hf_target_include_locations', $target_locations);
3323 update_post_meta($post_id, 'king_addons_el_hf_target_exclude_locations', $target_exclusion);
3324 update_post_meta($post_id, 'king_addons_el_hf_target_user_roles', $target_users);
3325
3326 if (isset($_POST['king_addons_el_hf_template_type'])) {
3327 update_post_meta($post_id, 'king_addons_el_hf_template_type', sanitize_text_field(wp_unslash($_POST['king_addons_el_hf_template_type'])));
3328 }
3329
3330 if (isset($_POST['king-addons-el-hf-display-on-canvas'])) {
3331 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'])));
3332 } else {
3333 delete_post_meta($post_id, 'king-addons-el-hf-display-on-canvas');
3334 }
3335 }
3336
3337 function setCompatibility()
3338 {
3339 $template = get_template();
3340 $is_elementor_callable = defined('ELEMENTOR_VERSION') && is_callable('Elementor\Plugin::instance');
3341
3342 if ($is_elementor_callable) {
3343 self::$elementor_instance = Elementor\Plugin::instance();
3344
3345 // TODO: Add popular themes
3346 switch ($template) {
3347 case 'hello-elementor':
3348 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/hello-elementor/ELHF_Hello_Elementor.php');
3349 break;
3350 default:
3351 add_action('init', [$this, 'setupSettingsPage']);
3352 add_filter('king_addons_el_hf_settings_tabs', [$this, 'setupUnsupportedTheme']);
3353 add_action('init', [$this, 'setupFallbackSupport']);
3354 break;
3355 }
3356 }
3357 }
3358
3359 public function setupUnsupportedTheme($settings_tabs = [])
3360 {
3361 if (!current_theme_supports('king-addons-elementor-header-footer')) {
3362 $settings_tabs['king_addons_el_hf_settings'] = [
3363 'name' => esc_html__('Display Settings', 'king-addons'),
3364 'url' => admin_url('edit.php?post_type=king-addons-el-hf&page=king-addons-el-hf-settings'),
3365 ];
3366 }
3367 return $settings_tabs;
3368 }
3369
3370 public function setupFallbackSupport()
3371 {
3372 if (!current_theme_supports('king-addons-elementor-header-footer')) {
3373 // Default to Method 3 (Universal) for best compatibility
3374 $compatibility_option = get_option('king_addons_el_hf_compatibility_option', '3');
3375
3376 if ('1' === $compatibility_option) {
3377 if (!class_exists('ELHF_Default_Method_1')) {
3378 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/default/ELHF_Default_Method_1.php');
3379 }
3380 } elseif ('2' === $compatibility_option) {
3381 if (!class_exists('ELHF_Default_Method_2')) {
3382 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/default/ELHF_Default_Method_2.php');
3383 }
3384 } else {
3385 // Method 3: Universal (combines all approaches for maximum compatibility)
3386 if (!class_exists('ELHF_Default_Method_3')) {
3387 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/themes/default/ELHF_Default_Method_3.php');
3388 }
3389 }
3390 }
3391 }
3392
3393 function setupSettingsPage()
3394 {
3395 require_once(KING_ADDONS_PATH . 'includes/extensions/Header_Footer_Builder/ELHF_Settings_Page.php');
3396 }
3397
3398 public static function renderHeader()
3399 {
3400 /** @noinspection SpellCheckingInspection */
3401 echo '<header id="masthead" class="king-addons-el-hf-header" itemscope="itemscope" itemtype="https://schema.org/WPHeader">';
3402 ?><p class="main-title" style="display: none;" itemprop="headline"><a
3403 href="<?php echo esc_url(get_bloginfo('url')); ?>"
3404 title="<?php echo esc_attr(get_bloginfo('name', 'display')); ?>"
3405 rel="home"><?php echo esc_html(get_bloginfo('name')); ?></a></p><?php
3406 self::getHeaderContent();
3407 echo '</header>';
3408 }
3409
3410 public static function renderFooter()
3411 {
3412 /** @noinspection SpellCheckingInspection */
3413 echo '<footer id="colophon" class="king-addons-el-hf-footer" itemscope="itemscope" itemtype="https://schema.org/WPFooter" role="contentinfo">';
3414 self::getFooterContent();
3415 echo '</footer>';
3416 }
3417
3418 public static function getHeaderContent()
3419 {
3420 $header_id = self::getHeaderID();
3421 if ($header_id) {
3422 // Ensure Elementor-generated CSS is included on non-Elementor pages.
3423 $with_css = true;
3424 echo self::$elementor_instance->frontend->get_builder_content_for_display($header_id, $with_css); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3425 }
3426 }
3427
3428 public static function getFooterContent()
3429 {
3430 $footer_id = self::getFooterID();
3431 if ($footer_id) {
3432 echo '<div style="width: 100%;">';
3433 // Ensure Elementor-generated CSS is included on non-Elementor pages.
3434 $with_css = true;
3435 echo self::$elementor_instance->frontend->get_builder_content_for_display($footer_id, $with_css); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3436 echo '</div>';
3437 }
3438 }
3439
3440 public static function getHeaderID()
3441 {
3442 $header_id = self::getSettings('king_addons_el_hf_type_header');
3443
3444 if ('' === $header_id) {
3445 $header_id = false;
3446 }
3447
3448 return apply_filters('king_addons_el_hf_get_header_id', $header_id);
3449 }
3450
3451 public static function isHeaderEnabled()
3452 {
3453 $header_id = self::getSettings('king_addons_el_hf_type_header');
3454 $status = false;
3455
3456 if ('' !== $header_id) {
3457 $status = true;
3458 }
3459
3460 return apply_filters('king_addons_el_hf_header_enabled', $status);
3461 }
3462
3463 public static function isFooterEnabled()
3464 {
3465 $footer_id = self::getSettings('king_addons_el_hf_type_footer');
3466 $status = false;
3467
3468 if ('' !== $footer_id) {
3469 $status = true;
3470 }
3471
3472 return apply_filters('king_addons_el_hf_footer_enabled', $status);
3473 }
3474
3475 public static function getFooterID()
3476 {
3477 $footer_id = self::getSettings('king_addons_el_hf_type_footer');
3478
3479 if ('' === $footer_id) {
3480 $footer_id = false;
3481 }
3482
3483 return apply_filters('king_addons_el_hf_get_footer_id', $footer_id);
3484 }
3485
3486
3487 public static function getSettings($setting = '')
3488 {
3489 if ('king_addons_el_hf_type_header' == $setting || 'king_addons_el_hf_type_footer' == $setting) {
3490 $templates = self::getTemplateID($setting);
3491 $template = !is_array($templates) ? $templates : $templates[0];
3492 return apply_filters("king_addons_el_hf_get_settings_$setting", $template);
3493 }
3494
3495 return null;
3496 }
3497
3498 public static function getTemplateID($type)
3499 {
3500 $option = [
3501 'location' => 'king_addons_el_hf_target_include_locations',
3502 'exclusion' => 'king_addons_el_hf_target_exclude_locations',
3503 'users' => 'king_addons_el_hf_target_user_roles',
3504 ];
3505
3506 $templates = self::getPostsByConditions('king-addons-el-hf', $option);
3507
3508 // Prime meta cache to avoid N+1 queries when calling get_post_meta in loops below.
3509 // getPostsByConditions() may already have populated template IDs; caching is safe and cheap.
3510 if (!empty($templates)) {
3511 $template_ids = array_map(static function ($template) {
3512 return isset($template['id']) ? absint($template['id']) : 0;
3513 }, $templates);
3514 $template_ids = array_values(array_filter($template_ids));
3515 if (!empty($template_ids)) {
3516 update_meta_cache('post', $template_ids);
3517 }
3518 }
3519
3520 foreach ($templates as $template) {
3521 if (get_post_meta(absint($template['id']), 'king_addons_el_hf_template_type', true) === $type) {
3522 // Polylang check - https://polylang.pro/doc/function-reference/
3523 if (function_exists('pll_current_language')) {
3524 if (pll_current_language('slug') == pll_get_post_language($template['id'], 'slug')) {
3525 return $template['id'];
3526 }
3527 } else {
3528 return $template['id'];
3529 }
3530 }
3531 }
3532
3533 return '';
3534 }
3535
3536 public static function getPostsByConditions($post_type, $option)
3537 {
3538 global $wpdb;
3539 global $post;
3540
3541 // Security fix: Validate and sanitize post_type
3542 // Ensure $post is valid before accessing its properties
3543 if ($post_type) {
3544 $post_type = sanitize_key($post_type);
3545 } elseif ($post instanceof \WP_Post && !empty($post->post_type)) {
3546 $post_type = sanitize_key($post->post_type);
3547 } else {
3548 return [];
3549 }
3550
3551 if (empty($post_type)) {
3552 return [];
3553 }
3554
3555 if (is_array(self::$current_page_data) && isset(self::$current_page_data[$post_type])) {
3556 return apply_filters('king_addons_el_hf_get_display_posts_by_conditions', self::$current_page_data[$post_type], $post_type);
3557 }
3558
3559 $current_page_type = self::getCurrentPageType();
3560
3561 self::$current_page_data[$post_type] = array();
3562
3563 $option['current_post_id'] = self::$current_page_data['ID'];
3564 $meta_header = self::getMetaOptionPost($post_type, $option);
3565
3566 if (false === $meta_header) {
3567 $current_post_type = sanitize_key(get_post_type());
3568 $current_post_id = false;
3569 $q_obj = get_queried_object();
3570
3571 $current_id = absint(get_the_id());
3572
3573 // Check if WPML is active. Find WPML Object ID for current page.
3574 /** @noinspection SpellCheckingInspection */
3575 if (defined('ICL_SITEPRESS_VERSION')) {
3576 $default_lang = apply_filters('wpml_default_language', '');
3577 $current_lang = apply_filters('wpml_current_language', '');
3578
3579 if ($default_lang !== $current_lang) {
3580 $current_post_type = get_post_type($current_id);
3581 $current_id = apply_filters('wpml_object_id', $current_id, $current_post_type, true, $default_lang);
3582 }
3583 }
3584
3585 // Security fix: Sanitize location parameter
3586 $location = isset($option['location']) ? sanitize_key($option['location']) : '';
3587 if (empty($location)) {
3588 return [];
3589 }
3590
3591 // Security fix: Use prepared statement to prevent SQL injection
3592 $query = $wpdb->prepare(
3593 "SELECT p.ID, pm.meta_value FROM {$wpdb->postmeta} as pm
3594 INNER JOIN {$wpdb->posts} as p ON pm.post_id = p.ID
3595 WHERE pm.meta_key = %s
3596 AND p.post_type = %s
3597 AND p.post_status = 'publish'",
3598 $location,
3599 $post_type
3600 );
3601
3602 $orderby = ' ORDER BY p.post_date DESC';
3603
3604 // Security fix: Build meta_args using safe placeholders and prepared statements
3605 $meta_conditions = ["pm.meta_value LIKE %s"];
3606 $meta_values = ['%"basic-global"%'];
3607
3608 switch ($current_page_type) {
3609 case 'is_404':
3610 $meta_conditions[] = "pm.meta_value LIKE %s";
3611 $meta_values[] = '%"special-404"%';
3612 break;
3613 case 'is_search':
3614 $meta_conditions[] = "pm.meta_value LIKE %s";
3615 $meta_values[] = '%"special-search"%';
3616 break;
3617 case 'is_archive':
3618 case 'is_tax':
3619 case 'is_date':
3620 case 'is_author':
3621 $meta_conditions[] = "pm.meta_value LIKE %s";
3622 $meta_values[] = '%"basic-archives"%';
3623 $meta_conditions[] = "pm.meta_value LIKE %s";
3624 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all|archive"%';
3625
3626 if ('is_tax' == $current_page_type && (is_category() || is_tag() || is_tax())) {
3627 if (is_object($q_obj) && isset($q_obj->taxonomy) && isset($q_obj->term_id)) {
3628 $meta_conditions[] = "pm.meta_value LIKE %s";
3629 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all|taxarchive|' . sanitize_key($q_obj->taxonomy) . '"%';
3630 $meta_conditions[] = "pm.meta_value LIKE %s";
3631 $meta_values[] = '%"tax-' . absint($q_obj->term_id) . '"%';
3632 }
3633 } elseif ('is_date' == $current_page_type) {
3634 $meta_conditions[] = "pm.meta_value LIKE %s";
3635 $meta_values[] = '%"special-date"%';
3636 } elseif ('is_author' == $current_page_type) {
3637 $meta_conditions[] = "pm.meta_value LIKE %s";
3638 $meta_values[] = '%"special-author"%';
3639 }
3640 break;
3641 case 'is_home':
3642 $meta_conditions[] = "pm.meta_value LIKE %s";
3643 $meta_values[] = '%"special-blog"%';
3644 break;
3645 case 'is_front_page':
3646 $current_post_id = $current_id;
3647 $meta_conditions[] = "pm.meta_value LIKE %s";
3648 $meta_values[] = '%"special-front"%';
3649 $meta_conditions[] = "pm.meta_value LIKE %s";
3650 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all"%';
3651 $meta_conditions[] = "pm.meta_value LIKE %s";
3652 $meta_values[] = '%"post-' . absint($current_id) . '"%';
3653 break;
3654 case 'is_singular':
3655 $current_post_id = $current_id;
3656 $meta_conditions[] = "pm.meta_value LIKE %s";
3657 $meta_values[] = '%"basic-singulars"%';
3658 $meta_conditions[] = "pm.meta_value LIKE %s";
3659 $meta_values[] = '%"' . sanitize_key($current_post_type) . '|all"%';
3660 $meta_conditions[] = "pm.meta_value LIKE %s";
3661 $meta_values[] = '%"post-' . absint($current_id) . '"%';
3662
3663 if (is_object($q_obj) && isset($q_obj->post_type) && isset($q_obj->ID)) {
3664 $taxonomies = get_object_taxonomies($q_obj->post_type);
3665 $terms = wp_get_post_terms($q_obj->ID, $taxonomies);
3666 foreach ($terms as $term) {
3667 if (isset($term->term_id) && isset($term->taxonomy)) {
3668 $meta_conditions[] = "pm.meta_value LIKE %s";
3669 $meta_values[] = '%"tax-' . absint($term->term_id) . '-single-' . sanitize_key($term->taxonomy) . '"%';
3670 }
3671 }
3672 }
3673 break;
3674 case 'is_woo_shop_page':
3675 if (function_exists('is_shop')) {
3676 $meta_conditions[] = "pm.meta_value LIKE %s";
3677 $meta_values[] = '%"special-woocommerce-shop"%';
3678 }
3679 break;
3680 case '':
3681 $current_post_id = $current_id;
3682 break;
3683 }
3684
3685 // Build the final meta_args string using prepare
3686 $meta_args = '(' . implode(' OR ', $meta_conditions) . ')';
3687
3688 // Security fix: Use prepared statement for the complete query
3689 $full_query = $wpdb->prepare(
3690 $query . ' AND ' . $meta_args . $orderby,
3691 ...$meta_values
3692 );
3693
3694 $posts = $wpdb->get_results($full_query);
3695
3696 foreach ($posts as $local_post) {
3697 $unserialized_location = maybe_unserialize($local_post->meta_value);
3698 if ($unserialized_location !== false) {
3699 self::$current_page_data[$post_type][$local_post->ID] = array(
3700 'id' => $local_post->ID,
3701 'location' => $unserialized_location,
3702 );
3703 }
3704 }
3705
3706 // Prime meta cache for all candidate templates to avoid repeated get_post_meta queries
3707 // in removeExclusionRulePosts/removeUserRulePosts and getTemplateID.
3708 if (!empty(self::$current_page_data[$post_type])) {
3709 $candidate_ids = array_map('absint', array_keys(self::$current_page_data[$post_type]));
3710 $candidate_ids = array_values(array_filter($candidate_ids));
3711 if (!empty($candidate_ids)) {
3712 update_meta_cache('post', $candidate_ids);
3713 }
3714 }
3715
3716 $option['current_post_id'] = $current_post_id;
3717
3718 self::removeExclusionRulePosts($post_type, $option);
3719 self::removeUserRulePosts($post_type, $option);
3720 }
3721
3722 return apply_filters('king_addons_el_hf_get_display_posts_by_conditions', self::$current_page_data[$post_type], $post_type);
3723 }
3724
3725 public static function getCurrentPageType(): ?string
3726 {
3727 if (null === self::$current_page_type) {
3728 $page_type = '';
3729 $current_id = false;
3730
3731 if (is_404()) {
3732 $page_type = 'is_404';
3733 } elseif (is_search()) {
3734 $page_type = 'is_search';
3735 } elseif (is_archive()) {
3736 $page_type = 'is_archive';
3737 if (is_category() || is_tag() || is_tax()) {
3738 $page_type = 'is_tax';
3739 } elseif (is_date()) {
3740 $page_type = 'is_date';
3741 } elseif (is_author()) {
3742 $page_type = 'is_author';
3743 } elseif (function_exists('is_shop')) {
3744 /** @noinspection PhpUndefinedFunctionInspection */
3745 if (is_shop()) {
3746 $page_type = 'is_woo_shop_page';
3747 }
3748 }
3749 } elseif (is_home()) {
3750 $page_type = 'is_home';
3751 } elseif (is_front_page()) {
3752 $page_type = 'is_front_page';
3753 $current_id = get_the_id();
3754 } elseif (is_singular()) {
3755 $page_type = 'is_singular';
3756 $current_id = get_the_id();
3757 } else {
3758 $current_id = get_the_id();
3759 }
3760
3761 self::$current_page_data['ID'] = $current_id;
3762 self::$current_page_type = $page_type;
3763 }
3764
3765 return self::$current_page_type;
3766 }
3767
3768 public static function getMetaOptionPost($post_type, $option)
3769 {
3770 $page_meta = (isset($option['page_meta']) && '' != $option['page_meta']) ? $option['page_meta'] : false;
3771
3772 if (false !== $page_meta) {
3773 $current_post_id = $option['current_post_id'] ?? false;
3774 $meta_id = get_post_meta($current_post_id, $option['page_meta'], true);
3775
3776 if (false !== $meta_id && '' != $meta_id) {
3777 self::$current_page_data[$post_type][$meta_id] = array(
3778 'id' => $meta_id,
3779 'location' => '',
3780 );
3781
3782 return self::$current_page_data[$post_type];
3783 }
3784 }
3785
3786 return false;
3787 }
3788
3789 public static function removeExclusionRulePosts($post_type, $option)
3790 {
3791 $exclusion = $option['exclusion'] ?? '';
3792 $current_post_id = $option['current_post_id'] ?? false;
3793 foreach (self::$current_page_data[$post_type] as $c_post_id => $c_data) {
3794 $exclusion_rules = get_post_meta($c_post_id, $exclusion, true);
3795 $is_exclude = self::parseLayoutDisplayCondition($current_post_id, $exclusion_rules);
3796 if ($is_exclude) {
3797 unset(self::$current_page_data[$post_type][$c_post_id]);
3798 }
3799 }
3800 }
3801
3802 public static function removeUserRulePosts($post_type, $option)
3803 {
3804 $users = $option['users'] ?? '';
3805
3806 foreach (self::$current_page_data[$post_type] as $c_post_id => $c_data) {
3807 $user_rules = get_post_meta($c_post_id, $users, true);
3808 $is_user = self::parseUserRoleCondition($user_rules);
3809
3810 if (!$is_user) {
3811 unset(self::$current_page_data[$post_type][$c_post_id]);
3812 }
3813 }
3814 }
3815
3816 public static function parseLayoutDisplayCondition($post_id, $rules): bool
3817 {
3818 $display = false;
3819
3820 /** @noinspection PhpConditionCheckedByNextConditionInspection */
3821 if (isset($rules['rule']) && is_array($rules['rule']) && !empty($rules['rule'])) {
3822 foreach ($rules['rule'] as $rule) {
3823 if (strrpos($rule, 'all') !== false) {
3824 $rule_case = 'all';
3825 } else {
3826 $rule_case = $rule;
3827 }
3828
3829 switch ($rule_case) {
3830 case 'basic-global':
3831 $display = true;
3832 break;
3833
3834 case 'basic-singulars':
3835 if (is_singular()) {
3836 $display = true;
3837 }
3838 break;
3839
3840 case 'basic-archives':
3841 if (is_archive()) {
3842 $display = true;
3843 }
3844 break;
3845
3846 case 'special-404':
3847 if (is_404()) {
3848 $display = true;
3849 }
3850 break;
3851
3852 case 'special-search':
3853 if (is_search()) {
3854 $display = true;
3855 }
3856 break;
3857
3858 case 'special-blog':
3859 if (is_home()) {
3860 $display = true;
3861 }
3862 break;
3863
3864 case 'special-front':
3865 if (is_front_page()) {
3866 $display = true;
3867 }
3868 break;
3869
3870 case 'special-date':
3871 if (is_date()) {
3872 $display = true;
3873 }
3874 break;
3875
3876 case 'special-author':
3877 if (is_author()) {
3878 $display = true;
3879 }
3880 break;
3881
3882 case 'special-woocommerce-shop':
3883 if (function_exists('is_shop')) {
3884 if (is_shop()) {
3885 $display = true;
3886 }
3887 }
3888 break;
3889
3890 case 'all':
3891 $rule_data = explode('|', $rule);
3892
3893 $post_type = $rule_data[0] ?? false;
3894 $archive_type = $rule_data[2] ?? false;
3895 $taxonomy = $rule_data[3] ?? false;
3896 if (false === $archive_type) {
3897 $current_post_type = get_post_type($post_id);
3898 if (false !== $post_id && $current_post_type == $post_type) {
3899 $display = true;
3900 }
3901 } else {
3902 if (is_archive()) {
3903 $current_post_type = get_post_type();
3904 if ($current_post_type == $post_type) {
3905 if ('archive' == $archive_type) {
3906 $display = true;
3907 } elseif ('taxarchive' == $archive_type) {
3908 $obj = get_queried_object();
3909 $current_taxonomy = '';
3910 if ('' !== $obj && null !== $obj) {
3911 $current_taxonomy = $obj->taxonomy;
3912 }
3913
3914 if ($current_taxonomy == $taxonomy) {
3915 $display = true;
3916 }
3917 }
3918 }
3919 }
3920 }
3921 break;
3922
3923 case 'specifics':
3924 if (isset($rules['specific']) && is_array($rules['specific'])) {
3925 foreach ($rules['specific'] as $specific_page) {
3926 $specific_data = explode('-', $specific_page);
3927 $specific_post_type = $specific_data[0] ?? false;
3928 $specific_post_id = $specific_data[1] ?? false;
3929 if ('post' == $specific_post_type) {
3930 if ($specific_post_id == $post_id) {
3931 $display = true;
3932 }
3933 } elseif (isset($specific_data[2]) && ('single' == $specific_data[2]) && 'tax' == $specific_post_type) {
3934 if (is_singular()) {
3935 $term_details = get_term($specific_post_id);
3936
3937 if (isset($term_details->taxonomy)) {
3938 $has_term = has_term((int)$specific_post_id, $term_details->taxonomy, $post_id);
3939
3940 if ($has_term) {
3941 $display = true;
3942 }
3943 }
3944 }
3945 } elseif ('tax' == $specific_post_type) {
3946 $tax_id = get_queried_object_id();
3947 if ($specific_post_id == $tax_id) {
3948 $display = true;
3949 }
3950 }
3951 }
3952 }
3953 break;
3954
3955 default:
3956 break;
3957 }
3958
3959 if ($display) {
3960 break;
3961 }
3962 }
3963 }
3964
3965 return $display;
3966 }
3967
3968 public static function parseUserRoleCondition($rules): bool
3969 {
3970 $show_popup = true;
3971
3972 if (is_array($rules) && !empty($rules)) {
3973 $show_popup = false;
3974
3975 foreach ($rules as $rule) {
3976 switch ($rule) {
3977 case '':
3978 case 'all':
3979 $show_popup = true;
3980 break;
3981
3982 case 'logged-in':
3983 if (is_user_logged_in()) {
3984 $show_popup = true;
3985 }
3986 break;
3987
3988 case 'logged-out':
3989 if (!is_user_logged_in()) {
3990 $show_popup = true;
3991 }
3992 break;
3993
3994 default:
3995 if (is_user_logged_in()) {
3996 $current_user = wp_get_current_user();
3997
3998 if (isset($current_user->roles)
3999 && is_array($current_user->roles)
4000 && in_array($rule, $current_user->roles)
4001 ) {
4002 $show_popup = true;
4003 }
4004 }
4005 break;
4006 }
4007
4008 if ($show_popup) {
4009 break;
4010 }
4011 }
4012 }
4013
4014 return $show_popup;
4015 }
4016
4017 public static function checkUserCanEdit()
4018 {
4019 if (is_singular('king-addons-el-hf') && !current_user_can('edit_posts')) {
4020 wp_redirect(site_url(), 301);
4021 die;
4022 }
4023 }
4024
4025 public static function loadElementorCanvasTemplate($single_template)
4026 {
4027 global $post;
4028
4029 // Safety check: ensure $post is a valid WP_Post object before accessing properties
4030 if (!$post instanceof \WP_Post) {
4031 return $single_template;
4032 }
4033
4034 if ('king-addons-el-hf' === $post->post_type) {
4035 if (defined('ELEMENTOR_VERSION')) {
4036 $elementor_2_0_canvas = ELEMENTOR_PATH . '/modules/page-templates/templates/canvas.php';
4037
4038 if (file_exists($elementor_2_0_canvas)) {
4039 return $elementor_2_0_canvas;
4040 } else {
4041 return ELEMENTOR_PATH . '/includes/page-templates/canvas.php';
4042 }
4043 }
4044 }
4045
4046 return $single_template;
4047 }
4048
4049 public function forceElementorCanvasTemplate($template)
4050 {
4051 if (!defined('ELEMENTOR_VERSION')) {
4052 return $template;
4053 }
4054
4055 $post_id = 0;
4056
4057 if (is_singular('king-addons-el-hf')) {
4058 $post_id = (int) get_queried_object_id();
4059 } elseif (isset($_GET['elementor-preview'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4060 $post_id = (int) $_GET['elementor-preview'];
4061 }
4062
4063 if ($post_id && 'king-addons-el-hf' === get_post_type($post_id)) {
4064 $elementor_2_0_canvas = ELEMENTOR_PATH . '/modules/page-templates/templates/canvas.php';
4065 if (file_exists($elementor_2_0_canvas)) {
4066 return $elementor_2_0_canvas;
4067 }
4068 return ELEMENTOR_PATH . '/includes/page-templates/canvas.php';
4069 }
4070
4071 return $template;
4072 }
4073
4074 public function forcePreviewQuery($query): void
4075 {
4076 if (is_admin() || !$query->is_main_query()) {
4077 return;
4078 }
4079
4080 if (!isset($_GET['elementor-preview'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4081 return;
4082 }
4083
4084 $preview_id = (int) $_GET['elementor-preview']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
4085 if (!$preview_id || 'king-addons-el-hf' !== get_post_type($preview_id)) {
4086 return;
4087 }
4088
4089 $query->set('p', $preview_id);
4090 $query->set('post_type', 'king-addons-el-hf');
4091 $query->set('post_status', ['publish', 'draft', 'pending', 'private']);
4092 }
4093
4094 public static function enqueueScripts(): void
4095 {
4096 $screen = get_current_screen();
4097 if ($screen->id === 'edit-king-addons-el-hf') {
4098 // todo - styles
4099 // wp_enqueue_style('king-addons-el-hf-style', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/header-footer-builder.css', '', KING_ADDONS_VERSION);
4100 wp_enqueue_style('king-addons-el-hf-style', KING_ADDONS_URL . 'includes/extensions/Header_Footer_Builder/admin.css', '', KING_ADDONS_VERSION);
4101 }
4102 }
4103
4104 public static function columnHeadings($columns)
4105 {
4106 unset($columns['date']);
4107 $columns['king_addons_el_hf_edit_template'] = esc_html__('Edit Template', 'king-addons');
4108 $columns['king_addons_el_hf_type_of_template'] = esc_html__('Type of Template', 'king-addons');
4109 $columns['king_addons_el_hf_display_rules'] = esc_html__('Display Rules', 'king-addons');
4110 $columns['date'] = esc_html__('Date', 'king-addons');
4111 return $columns;
4112 }
4113
4114 public static function columnContent($column, $post_id)
4115 {
4116 // Edit Template
4117 if ('king_addons_el_hf_edit_template' === $column) {
4118 echo '<a class="king-addons-el-hf-edit-template-btn" href="';
4119 echo './post.php?post=' . esc_attr($post_id) . '&action=edit';
4120 echo '">' . esc_html__('Edit Template', 'king-addons') . '</a>';
4121 }
4122
4123 // Display Rules
4124 if ('king_addons_el_hf_display_rules' === $column) {
4125
4126 $locations = get_post_meta($post_id, 'king_addons_el_hf_target_include_locations', true);
4127 if (!empty($locations)) {
4128 echo '<div style="margin-bottom: 5px;">';
4129 echo '<strong>';
4130 echo esc_html__('Display: ', 'king-addons');
4131 echo '</strong>';
4132 self::columnDisplayLocation($locations);
4133 echo '</div>';
4134 }
4135
4136 $locations = get_post_meta($post_id, 'king_addons_el_hf_target_exclude_locations', true);
4137 if (!empty($locations)) {
4138 echo '<div style="margin-bottom: 5px;">';
4139 echo '<strong>';
4140 echo esc_html__('Exclusion: ', 'king-addons');
4141 echo '</strong>';
4142 self::columnDisplayLocation($locations);
4143 echo '</div>';
4144 }
4145
4146 $users = get_post_meta($post_id, 'king_addons_el_hf_target_user_roles', true);
4147 if (isset($users) && is_array($users)) {
4148 if (!empty($users[0])) {
4149 $user_label = [];
4150 foreach ($users as $user) {
4151 $user_label[] = self::get_user_by_key($user);
4152 }
4153 echo '<div>';
4154 echo '<strong>Users: </strong>';
4155 echo esc_html(join(', ', $user_label));
4156 echo '</div>';
4157 }
4158 }
4159
4160 }
4161
4162 // Type of Template
4163 if ('king_addons_el_hf_type_of_template' === $column) {
4164 $template_type = get_post_meta($post_id, 'king_addons_el_hf_template_type', true);
4165 if (!empty($template_type)) {
4166 echo '<div style="margin-bottom: 5px;">';
4167 echo '<strong>';
4168 switch ($template_type) {
4169 case 'king_addons_el_hf_type_header':
4170 echo esc_html__('Header', 'king-addons');
4171 break;
4172 case 'king_addons_el_hf_type_footer':
4173 echo esc_html__('Footer', 'king-addons');
4174 break;
4175 default:
4176 echo esc_html__('Not selected', 'king-addons');
4177 break;
4178 }
4179 echo '</strong>';
4180 echo '</div>';
4181 }
4182 }
4183 }
4184
4185 public static function get_user_by_key($key)
4186 {
4187 if (!isset(self::$user_selection) || empty(self::$user_selection)) {
4188 self::$user_selection = self::get_user_selections();
4189 }
4190 $user_selection = self::$user_selection;
4191
4192 if (isset($user_selection['basic']['value'][$key])) {
4193 return $user_selection['basic']['value'][$key];
4194 } elseif ($user_selection['advanced']['value'][$key]) {
4195 return $user_selection['advanced']['value'][$key];
4196 }
4197 return $key;
4198 }
4199
4200 public static function columnDisplayLocation($locations)
4201 {
4202 $location_label = [];
4203 /** @noinspection PhpConditionAlreadyCheckedInspection */
4204 if (is_array($locations) && is_array($locations['rule']) && isset($locations['rule'])) {
4205 /** @noinspection PhpArraySearchInBooleanContextInspection */
4206 $index = array_search('specifics', $locations['rule']);
4207 /** @noinspection PhpConditionCheckedByNextConditionInspection */
4208 if (false !== $index && !empty($index)) {
4209 unset($locations['rule'][$index]);
4210 }
4211 }
4212
4213 if (isset($locations['rule']) && is_array($locations['rule'])) {
4214 foreach ($locations['rule'] as $location) {
4215 $location_label[] = self::getLocation($location);
4216 }
4217 }
4218
4219 if (isset($locations['specific']) && is_array($locations['specific'])) {
4220 foreach ($locations['specific'] as $location) {
4221 $location_label[] = self::getLocation($location);
4222 }
4223 }
4224
4225 echo esc_html(join(', ', $location_label));
4226 }
4227
4228 public static function getLocation($key)
4229 {
4230 if (!isset(self::$location_selection) || empty(self::$location_selection)) {
4231 self::$location_selection = self::getLocationSelections();
4232 }
4233
4234 $location_selection = self::$location_selection;
4235
4236 foreach ($location_selection as $location_grp) {
4237 if (isset($location_grp['value'][$key])) {
4238 return $location_grp['value'][$key];
4239 }
4240 }
4241
4242 if (strpos($key, 'post-') !== false) {
4243 $post_id = (int)str_replace('post-', '', $key);
4244 return get_the_title($post_id);
4245 }
4246
4247 if (strpos($key, 'tax-') !== false) {
4248 $tax_id = (int)str_replace('tax-', '', $key);
4249 $term = get_term($tax_id);
4250
4251 if (!is_wp_error($term)) {
4252 $term_taxonomy = ucfirst(str_replace('_', ' ', $term->taxonomy));
4253 return $term->name . ' - ' . $term_taxonomy;
4254 } else {
4255 return '';
4256 }
4257 }
4258
4259 return $key;
4260 }
4261
4262 public static function getLocationSelections()
4263 {
4264 $args = array(
4265 'public' => true,
4266 '_builtin' => true,
4267 );
4268
4269 $post_types = get_post_types($args, 'objects');
4270 unset($post_types['attachment']);
4271
4272 $args['_builtin'] = false;
4273 $custom_post_type = get_post_types($args, 'objects');
4274
4275 $post_types = apply_filters('king_addons_el_hf_location_rule_post_types', array_merge($post_types, $custom_post_type));
4276
4277 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
4278 $special_pages = array(
4279 'special-404-none' => esc_html__('404 Page (Available in PRO)', 'king-addons'),
4280 'special-search' => esc_html__('Search Page', 'king-addons'),
4281 'special-blog-none' => esc_html__('Blog / Posts Page (Available in PRO)', 'king-addons'),
4282 'special-front' => esc_html__('Front Page', 'king-addons'),
4283 'special-date' => esc_html__('Date Archive', 'king-addons'),
4284 'special-author' => esc_html__('Author Archive', 'king-addons'),
4285 );
4286 } else {
4287 $special_pages = array(
4288 'special-404' => esc_html__('404 Page', 'king-addons'),
4289 'special-search' => esc_html__('Search Page', 'king-addons'),
4290 'special-blog' => esc_html__('Blog / Posts Page', 'king-addons'),
4291 'special-front' => esc_html__('Front Page', 'king-addons'),
4292 'special-date' => esc_html__('Date Archive', 'king-addons'),
4293 'special-author' => esc_html__('Author Archive', 'king-addons'),
4294 );
4295 }
4296
4297 if (class_exists('WooCommerce')) {
4298 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
4299 $special_pages['special-woocommerce-shop-none'] = esc_html__('WooCommerce Shop Page (Available in PRO)', 'king-addons');
4300 } else {
4301 $special_pages['special-woocommerce-shop'] = esc_html__('WooCommerce Shop Page', 'king-addons');
4302 }
4303 }
4304
4305 $selection_options = array(
4306 'basic' => array(
4307 'label' => esc_html__('Basic', 'king-addons'),
4308 'value' => array(
4309 'basic-global' => esc_html__('Entire Website', 'king-addons'),
4310 'basic-singulars' => esc_html__('All Singulars', 'king-addons'),
4311 'basic-archives' => esc_html__('All Archives', 'king-addons'),
4312 ),
4313 ),
4314
4315 'special-pages' => array(
4316 'label' => esc_html__('Special Pages', 'king-addons'),
4317 'value' => $special_pages,
4318 ),
4319 );
4320
4321 if (!king_addons_freemius()->can_use_premium_code__premium_only()) {
4322 $selection_options['specific-target'] = array(
4323 'label' => esc_html__('Specific Target', 'king-addons'),
4324 'value' => array(
4325 'specifics-none' => esc_html__('Specific Pages / Posts / Taxonomies, etc. (Available in PRO)', 'king-addons'),
4326 ),
4327 );
4328 } else {
4329 $selection_options['specific-target'] = array(
4330 'label' => esc_html__('Specific Target', 'king-addons'),
4331 'value' => array(
4332 'specifics' => esc_html__('Specific Pages / Posts / Taxonomies, etc.', 'king-addons'),
4333 ),
4334 );
4335 }
4336
4337 $args = array(
4338 'public' => true,
4339 );
4340
4341 $taxonomies = get_taxonomies($args, 'objects');
4342
4343 if (!empty($taxonomies)) {
4344 foreach ($taxonomies as $taxonomy) {
4345
4346 if ('post_format' == $taxonomy->name) {
4347 continue;
4348 }
4349
4350 foreach ($post_types as $post_type) {
4351 $post_opt = self::getPostTargetRuleOptions($post_type, $taxonomy);
4352
4353 if (isset($selection_options[$post_opt['post_key']])) {
4354 if (!empty($post_opt['value']) && is_array($post_opt['value'])) {
4355 foreach ($post_opt['value'] as $key => $value) {
4356 if (!in_array($value, $selection_options[$post_opt['post_key']]['value'])) {
4357 $selection_options[$post_opt['post_key']]['value'][$key] = $value;
4358 }
4359 }
4360 }
4361 } else {
4362 $selection_options[$post_opt['post_key']] = array(
4363 'label' => $post_opt['label'],
4364 'value' => $post_opt['value'],
4365 );
4366 }
4367 }
4368 }
4369 }
4370
4371 return apply_filters('king_addons_el_hf_display_on_list', $selection_options);
4372 }
4373
4374 public static function getPostTargetRuleOptions($post_type, $taxonomy): array
4375 {
4376 $post_key = str_replace(' ', '-', strtolower($post_type->label));
4377 $post_label = ucwords($post_type->label);
4378 $post_name = $post_type->name;
4379 $post_option = array();
4380
4381 /* translators: %s is post label */
4382 $all_posts = sprintf(esc_html__('All %s', 'king-addons'), $post_label);
4383 $post_option[$post_name . '|all'] = $all_posts;
4384
4385 if ('pages' != $post_key) {
4386 /* translators: %s is post label */
4387 $all_archive = sprintf(esc_html__('All %s Archive', 'king-addons'), $post_label);
4388 $post_option[$post_name . '|all|archive'] = $all_archive;
4389 }
4390
4391 if (in_array($post_type->name, $taxonomy->object_type)) {
4392 $tax_label = ucwords($taxonomy->label);
4393 $tax_name = $taxonomy->name;
4394
4395 /* translators: %s is taxonomy label */
4396 $tax_archive = sprintf(esc_html__('All %s Archive', 'king-addons'), $tax_label);
4397
4398 $post_option[$post_name . '|all|taxarchive|' . $tax_name] = $tax_archive;
4399 }
4400
4401 $post_output['post_key'] = $post_key;
4402 $post_output['label'] = $post_label;
4403 $post_output['value'] = $post_option;
4404
4405 return $post_output;
4406 }
4407
4408 public static function getFormatRuleValue($save_data, $key): array
4409 {
4410 $meta_value = array();
4411
4412 if (isset($save_data[$key]['rule'])) {
4413 $save_data[$key]['rule'] = array_unique($save_data[$key]['rule']);
4414 if (isset($save_data[$key]['specific'])) {
4415 $save_data[$key]['specific'] = array_unique($save_data[$key]['specific']);
4416 }
4417
4418 $index = array_search('', $save_data[$key]['rule']);
4419 if (false !== $index) {
4420 unset($save_data[$key]['rule'][$index]);
4421 }
4422 $index = array_search('specifics', $save_data[$key]['rule']);
4423 if (false !== $index) {
4424 unset($save_data[$key]['rule'][$index]);
4425
4426 if (isset($save_data[$key]['specific']) && is_array($save_data[$key]['specific'])) {
4427 $save_data[$key]['rule'][] = 'specifics';
4428 }
4429 }
4430
4431 foreach ($save_data[$key] as $meta_key => $value) {
4432 if (!empty($value)) {
4433 $meta_value[$meta_key] = array_map('esc_attr', $value);
4434 }
4435 }
4436 if (!isset($meta_value['rule']) || !in_array('specifics', $meta_value['rule'])) {
4437 $meta_value['specific'] = array();
4438 }
4439
4440 if (empty($meta_value['rule'])) {
4441 $meta_value = array();
4442 }
4443 }
4444
4445 return $meta_value;
4446 }
4447 }