PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.0
6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / includes / HelperFunctions.php
kirki / includes Last commit date
API 4 days ago Admin 1 month ago Ajax 4 days ago ExportImport 2 weeks ago FormValidator 3 months ago Frontend 4 days ago Manager 2 weeks ago API.php 2 weeks ago Admin.php 3 months ago Ajax.php 4 days ago Apps.php 1 month ago ContentManager.php 3 months ago DbQueryUtils.php 2 months ago ElementVisibilityConditions.php 3 months ago Frontend.php 3 months ago HelperFunctions.php 4 days ago KirkiBase.php 1 month ago PostsQueryUtils.php 3 months ago Staging.php 4 days ago View.php 1 month ago
HelperFunctions.php
4705 lines
1 <?php
2 /**
3 * Helper class for kirki project
4 *
5 * @package kirki
6 */
7
8 namespace Kirki;
9
10 if (!defined('ABSPATH')) {
11 exit; // Exit if accessed directly.
12 }
13
14 use DateTime;
15 use Kirki\Ajax\Collaboration\Collaboration;
16 use Kirki\Ajax\Page;
17 use Kirki\Ajax\RBAC;
18 use Kirki\Staging;
19 use Kirki\Ajax\Symbol;
20 use Kirki\Ajax\UserData;
21 use Kirki\Ajax\Users;
22 use Kirki\Ajax\WpAdmin;
23 use Kirki\API\ContentManager\ContentManagerHelper;
24 use Kirki\App\Supports\Facades\Page as FacadesPage;
25 use Kirki\App\Supports\FileHandler;
26 use Kirki\Frontend\Preview\DataHelper;
27 use Kirki\Frontend\Preview\Preview;
28 use WP_Post;
29 use WP_Query;
30 use WP_Term;
31 use WP_User;
32
33 /**
34 * HelperFunctions Class
35 */
36 class HelperFunctions
37 {
38
39 public static $custom_sections = [];
40 public static $global_session_id = false;
41 private static $printed_font_family_tracker = array();
42
43 private static $posts_where_filter_params = array();
44 /**
45 * Load assets for Editor
46 *
47 * @param string $for TheFrontend | null.
48 * @return false || array
49 */
50 public static function is_kirki_type_data($post_id = false, $staging_version = false)
51 {
52 if (!$post_id) {
53 $post_id = self::get_post_id_if_possible_from_url();
54 if (!$post_id)
55 return false;
56 }
57
58 if (!self::is_editor_mode_is_kirki($post_id)) {
59 return false;
60 }
61 if ($staging_version) {
62 return Staging::get_page_staging_data($post_id, $staging_version);
63 }
64
65 $kirki_data = get_post_meta($post_id, 'kirki', true);
66 if (!$kirki_data) {
67 $kirki_data = array();
68 $kirki_data['blocks'] = null;
69 }
70
71 $styles = self::get_page_styleblocks($post_id, $staging_version);
72
73 $kirki_data['styles'] = isset($styles) ? $styles : '';
74
75 return $kirki_data;
76 }
77
78 /**
79 * @deprecated
80 * @see \Kirki\App\Utils\PostUtil::get_post_id_from_url()
81 */
82 public static function get_post_id_if_possible_from_url()
83 {
84 if (isset($GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_child_post'])) {
85 return $GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_child_post'];
86 } else if (isset($GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_parent_post']) && false) {//disable content manager archive page logic
87 return $GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_parent_post'];
88 }
89
90 $post_id = get_the_ID();
91 $post_id = self::sanitize_text(isset($_GET['post_id']) ? $_GET['post_id'] : $post_id);
92 $post_id = self::sanitize_text(isset($_POST['post_id']) ? $_POST['post_id'] : $post_id);
93 $post_id = self::sanitize_text(isset($_GET['p']) ? $_GET['p'] : $post_id);
94
95 return (int) $post_id;
96 }
97
98 /**
99 * Get author/user id if possible from url
100 */
101 public static function get_user_id_if_possible_from_url()
102 {
103 if (isset($GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_user'])) {
104 return $GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_user'];
105 }
106
107 $user_id = '';
108 if (isset($GLOBALS['wp']->query_vars['author_name'])) {
109 $user = get_user_by('slug', $GLOBALS['wp']->query_vars['author_name']);
110 if ($user instanceof WP_User) {
111 $user_id = $user->ID;
112 }
113 } else {
114 $user_id = get_current_user_id();
115 }
116
117 return (int) $user_id;
118 }
119
120
121 /**
122 * Get all user roles by access levels
123 * @param array $access_levels access levels => array(KIRKI_ACCESS_LEVELS['FULL_ACCESS'], KIRKI_ACCESS_LEVELS['CONTENT_ACCESS']);
124 *
125 * @return array roles => array ('editor', 'author');
126 */
127 public static function get_all_user_roles_by_access_levels($access_levels = array())
128 {
129 $all_roles = RBAC::get_all_roles();
130 $roles_with_access = RBAC::get_access_level($all_roles);
131
132 $filtered_roles = [];
133
134 $filtered_roles = array_filter($roles_with_access, function ($level) use ($access_levels) {
135 return in_array($level, $access_levels);
136 });
137
138 return array_keys($filtered_roles);
139 }
140
141 /**
142 * Get term or tag id if possible from url
143 */
144 public static function get_term_id_if_possible_from_url()
145 {
146 if (isset($GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_term'])) {
147 return $GLOBALS['wp']->query_vars[KIRKI_CONTENT_MANAGER_PREFIX . '_term'];
148 }
149
150 // http://kirki.test/tag/wp-tag-2/
151 $term_id = get_queried_object_id();
152 $term = null;
153
154 if ($term_id) {
155 $term = get_term($term_id);
156 } else if (isset($GLOBALS['wp']->query_vars['tag'])) {
157 $term = get_term_by('slug', $GLOBALS['wp']->query_vars['tag'], 'post_tag');
158 }
159
160 if ($term instanceof WP_Term) {
161 $term_id = $term->term_id;
162 }
163
164 if (!$term_id) {
165 // get all terms and return the first one
166 $term = get_terms(array(
167 'taxonomy' => 'category',
168 'hide_empty' => false,
169 'number' => 1,
170 ));
171
172 $term_id = isset($term[0], $term[0]->term_id) ? $term[0]->term_id : 1;
173 }
174
175 return (int) $term_id;
176 }
177
178 /**
179 * @deprecated
180 * @see Kirki\App\Services\PageService::save_page_data()
181 * @see \Kirki\App\Services\GlobalDataService::save_styles()
182 */
183 public static function save_kirki_data_to_db($post_id, $page_data, $is_staging = false)
184 {
185 $version_where_saved = false;
186 if ($is_staging) {
187 $data = Staging::save_page_staging_data_to_db($post_id, $page_data);
188 $page_data = $data['page_data'];
189 $version_where_saved = $data['version'];
190 }
191
192 if (isset($page_data['styles'])) {
193 $global_style_blocks_for_collaboration = [];
194 foreach ($page_data['styles'] as $key => $sb) {
195 if ((isset($sb['isDefault']) && $sb['isDefault'] === true) || (isset($sb['isGlobal']) && $sb['isGlobal'] === true)) {
196 if (isset($sb['fromStage'])) {
197 unset($page_data['styles'][$key]['fromStage']);
198 $global_style_blocks_for_collaboration[$key] = $page_data['styles'][$key];
199 } else
200 unset($page_data['styles'][$key]);
201 }
202 }
203 if (count($global_style_blocks_for_collaboration) > 0) {
204 $session_id = self::sanitize_text(isset($_REQUEST['session_id']) ? $_REQUEST['session_id'] : '');
205 if ($session_id && $is_staging === false) {
206 // cause template import also called this method without session_id
207 $data = array(
208 'type' => 'COLLABORATION_UPDATE_GLOBAL_STYLE',
209 'payload' => array('styleBlock' => $global_style_blocks_for_collaboration),
210 );
211 Collaboration::save_action_to_db('global', 0, $data, 1, $session_id);
212 }
213 }
214
215 self::update_page_styleblocks($post_id, $page_data['styles']);
216 unset($page_data['styles']);
217 }
218
219 if (isset($page_data['usedStyles'])) {
220 update_post_meta($post_id, KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS, $page_data['usedStyles']);
221 unset($page_data['usedStyles']);
222 }
223
224 if (isset($page_data['usedStyleIdsRandom'])) {
225 update_post_meta($post_id, KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random', $page_data['usedStyleIdsRandom']);
226 unset($page_data['usedStyleIdsRandom']);
227 }
228
229 if (isset($page_data['usedFonts'])) {
230 update_post_meta($post_id, KIRKI_META_NAME_FOR_USED_FONT_LIST, $page_data['usedFonts']);
231 unset($page_data['usedFonts']);
232 }
233
234 if (isset($page_data['customFonts'])) {
235 //save others data if isset. this is for template import
236 $custom_fonts = self::get_global_data_using_key(KIRKI_USER_CUSTOM_FONTS_META_KEY);
237 foreach ($page_data['customFonts'] as $key => $cf) {
238 $custom_fonts[$key] = $cf;
239 }
240 self::update_global_data_using_key(KIRKI_USER_CUSTOM_FONTS_META_KEY, $custom_fonts);
241 unset($page_data['customFonts']);
242 }
243
244 if (isset($page_data['viewportList'])) {
245 //save others data if isset. this is for template import
246 $controller_data = self::get_global_data_using_key(KIRKI_USER_CONTROLLER_META_KEY);
247 if (!$controller_data) {
248 $init = array(
249 'active' => 'md',
250 'defaults' => ["md", "tablet", "mobileLandscape", "mobile"],
251 'list' => $page_data['viewportList'],
252 'mdWidth' => 1200,
253 "scale" => 1,
254 "width" => 2484,
255 "zoom" => 1
256 );
257 $controller_data = array('viewport' => $init);
258 } else if (isset($controller_data['viewport'], $controller_data['viewport']['list'])) {
259 $controller_data['viewport']['list'] = $page_data['viewportList'];
260 }
261
262 HelperFunctions::update_global_data_using_key(KIRKI_USER_CONTROLLER_META_KEY, $controller_data);
263 unset($page_data['viewportList']);
264 }
265
266 if (isset($page_data['blocks'])) {
267 update_post_meta($post_id, 'kirki', array('blocks' => $page_data['blocks']));
268 // $data = array(
269 // 'type' => 'COLLABORATION_PAGE_DATA',
270 // 'payload' => array( 'data' => $page_data['blocks'] ),
271 // );
272 //Collaboration::save_action_to_db( 'post', $post_id, $data, 1 );
273 }
274
275
276
277 update_post_meta($post_id, KIRKI_META_NAME_FOR_POST_EDITOR_MODE, 'kirki');
278 return $version_where_saved;
279 }
280
281 /**
282 *
283 */
284 function abc()
285 {
286
287 }
288
289 /**
290 * This function will return page style blocks from option meta and post meta
291 * post meta for migration and option meta for global style block
292 *
293 * @param int $post_id post id.
294 * @return object
295 *
296 * @todo:
297 *
298 * @deprecated
299 * @see Kirki\App\Managers\PageManager::get_page_styleblocks()
300 */
301 public static function get_page_styleblocks($post_id, $stage_version = false)
302 {
303 return FacadesPage::get_page_styleblocks($post_id, $stage_version);
304
305 // $random_style_blocks = get_post_meta($post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', true);
306 // $global_style_blocks = self::get_global_data_using_key(KIRKI_GLOBAL_STYLE_BLOCK_META_KEY);
307
308 // $random_style_blocks = self::fix_duplicate_class_name_from_random_sbs($random_style_blocks, $global_style_blocks);
309
310 // $merged_style_blocks = array();
311 // if ($random_style_blocks) {
312 // $merged_style_blocks = array_merge($merged_style_blocks, $random_style_blocks);
313 // }
314 // if ($global_style_blocks) {
315 // $merged_style_blocks = array_merge($merged_style_blocks, $global_style_blocks);
316 // }
317
318 // $published_version = Staging::get_published_stage_version($post_id);
319 // if ($published_version && $stage_version !== $published_version) {
320 // $staging_style_blocks = array();
321 // $meta_key = Staging::get_staged_meta_name(KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $post_id, $stage_version);
322 // $stage_style = get_post_meta($post_id, $meta_key, true);
323 // if ($stage_style)
324 // $staging_style_blocks = array_merge($staging_style_blocks, $stage_style);
325
326 // $meta_key = $meta_key . '_random';
327 // $stage_style = get_post_meta($post_id, $meta_key, true);
328 // if ($stage_style)
329 // $staging_style_blocks = array_merge($staging_style_blocks, $stage_style);
330 // if ($stage_version)
331 // $merged_style_blocks = self::merge_style_blocks($merged_style_blocks, $staging_style_blocks);
332 // else
333 // $merged_style_blocks = self::merge_style_blocks($staging_style_blocks, $merged_style_blocks);
334 // }
335
336 // return $merged_style_blocks;
337 }
338
339 /**
340 * @deprecated
341 * @see Kirki\App\Managers\PageManager::merge_style_blocks()
342 */
343 public static function merge_style_blocks($a, $b)
344 {
345 $names_in_a = [];
346 foreach ($a as $val) {
347 if (!empty($val['name']) && is_string($val['name'])) {
348 $names_in_a[strtolower($val['name'])] = true;
349 }
350 }
351
352 $names_in_b = [];
353 foreach ($b as $val) {
354 if (!empty($val['name']) && is_string($val['name'])) {
355 $names_in_b[strtolower($val['name'])] = true;
356 }
357 }
358
359 foreach ($b as $id_b => &$value_b) {
360 if (empty($value_b['name']) || !is_string($value_b['name'])) {
361 continue;
362 }
363
364 $b_name = strtolower($value_b['name']);
365
366 // If same ID exists in A, remove it first (old behavior)
367 if (isset($a[$id_b])) {
368 unset($a[$id_b]);
369 if (isset($names_in_a[$b_name])) {
370 unset($names_in_a[$b_name]);
371 }
372 }
373
374 // If name already exists in A, make it unique
375 if (isset($names_in_a[$b_name])) {
376 $i = 1;
377 while (isset($names_in_a[$b_name . '_' . $i]) || isset($names_in_b[$b_name . '_' . $i])) {
378 $i++;
379 }
380 $new_name = $value_b['name'] . '_' . $i;
381
382 foreach ($b as &$v) {
383 if (isset($v['name']) && is_array($v['name'])) {
384 $v['name'] = array_map(fn($item) => $item === $value_b['name'] ? $new_name : $item, $v['name']);
385 }
386 }
387
388 $value_b['name'] = $new_name;
389 unset($names_in_b[$b_name]);
390 $names_in_b[strtolower($new_name)] = true;
391 }
392 }
393 unset($value_b);
394
395 // Use array_merge to keep old semantics
396 return array_merge($a, $b);
397 }
398
399
400 /**
401 * @deprecated
402 * @see Kirki\App\Managers\PageManager::resolve_duplicate_current_style_block_names()
403 */
404 private static function fix_duplicate_class_name_from_random_sbs($random_style_blocks, $global_style_blocks)
405 {
406 $global_class_names = [];
407 $random_class_names = [];
408 if ($global_style_blocks) {
409 foreach ($global_style_blocks as $key => $value) {
410 if (isset($value['name']) && is_string($value['name'])) {
411 $global_class_names[self::get_class_name_from_string($value['name'])] = true;
412 }
413 }
414 }
415 if ($random_style_blocks) {
416 foreach ($random_style_blocks as $key => $value) {
417 if (isset($value['name']) && is_string($value['name'])) {
418 $random_class_names[self::get_class_name_from_string($value['name'])] = true;
419 }
420 }
421 }
422
423 $class_match = [];
424 foreach ($random_class_names as $key => $value) {
425 if (isset($global_class_names[$key])) {
426 $class_match[$key] = true;
427 }
428 }
429
430 $class_match = self::check_or_generate_new_class_names($class_match, $global_class_names, $random_class_names);
431
432 if (count($class_match) > 0) {
433 foreach ($random_style_blocks as $key => $value) {
434 if (isset($value['name']) && is_string($value['name'])) {
435 $class_name = self::get_class_name_from_string($value['name']);
436 if (isset($class_match[$class_name])) {
437 $random_style_blocks[$key]['name'] = $class_match[$class_name];
438 }
439 } else if (isset($value['name']) && is_array($value['name'])) {
440 //array
441 foreach ($value['name'] as $key2 => $v2) {
442 $class_name = self::get_class_name_from_string($v2);
443 if (isset($class_match[$class_name])) {
444 $random_style_blocks[$key]['name'][$key2] = $class_match[$class_name];
445 }
446 }
447 }
448 }
449 }
450 return $random_style_blocks;
451 }
452
453 /**
454 * @deprecated
455 * @see Kirki\App\Managers\PageManager::make_duplicate_classes_to_unique()
456 */
457 private static function check_or_generate_new_class_names($class_match, $global_class_names, $random_class_names)
458 {
459 foreach ($class_match as $key => $value) {
460 $temp_class = $key;
461 $found = true;
462 while ($found) {
463 if (isset($global_class_names[$temp_class]) || isset($random_class_names[$temp_class])) {
464 $temp_class = $temp_class . '-copy';
465 } else {
466 $found = false;
467 }
468 }
469 $class_match[$key] = $temp_class;
470 }
471 return $class_match;
472 }
473
474 /**
475 * @deprecated
476 * @see Kirki\App\Managers\PageManager::normalize_style_block_name()
477 */
478 public static function get_class_name_from_string($s)
479 {
480 $s = strtolower(str_replace(' ', '-', $s));
481 return $s;
482 }
483
484 public static function get_selector_from_sb_name($name)
485 {
486 if (!$name)
487 return '';
488 $class_name = '';
489 if (is_string($name)) {
490 $class_name = '.' . self::get_class_name_from_string($name);
491 } else {
492 foreach ($name as $key2 => $cn) {
493 $class_name .= '.' . self::get_class_name_from_string($cn);
494 }
495 }
496 return $class_name;
497 }
498
499 /**
500 * This function will update page style blocks into option meta and post meta
501 * post meta for migration and option meta for global style block
502 *
503 * @param int $post_id post id.
504 * @param object $style_blocks styleblocks.
505 *
506 * @deprecated the method is not used anymore. handle separately in GlobalDataManager and PageManager
507 */
508 public static function update_page_styleblocks($post_id, $style_blocks)
509 {
510 $prev_style_blocks = self::get_page_styleblocks($post_id);
511 $style_blocks = array_merge($prev_style_blocks, $style_blocks);
512
513 $global_style_blocks = array();
514 foreach ($style_blocks as $key => $sb) {
515 if ((isset($sb['isDefault']) && $sb['isDefault'] === true) || (isset($sb['isGlobal']) && $sb['isGlobal'] === true)) {
516 $global_style_blocks[$sb['id']] = $sb;
517 unset($style_blocks[$key]);
518 }
519 }
520
521 self::save_global_style_blocks($global_style_blocks);
522 self::save_random_style_blocks($post_id, $style_blocks);
523 }
524
525 /**
526 * Save global style blocks in option table. Also save collaboration data.
527 *
528 * @param array $style //take styleblocs if isDefault and isGlobal key is true.
529 * @return void
530 * @deprecated
531 * @see Kirki\App\Managers\GlobalDataManager::update_deprecated_global_style_blocks()
532 */
533 public static function save_global_style_blocks($style)
534 {
535 // $session_id = self::sanitize_text(isset($_REQUEST['session_id']) ? $_REQUEST['session_id'] : '');
536 self::update_global_data_using_key(KIRKI_GLOBAL_STYLE_BLOCK_META_KEY, $style);
537
538 // if($session_id){
539 // // cause template import also called this method without session_id
540 // $data = array(
541 // 'type' => 'COLLABORATION_UPDATE_GLOBAL_STYLE',
542 // 'payload' => array( 'styleBlock' => $style ),
543 // );
544 // Collaboration::save_action_to_db( 'global', 0, $data, 1, $session_id);
545 // }
546 }
547
548 /**
549 * Save post related random style blocks in option table. Also save collaboration data.
550 *
551 * @param array $post_id //current post id.
552 * @param array $style //take styleblocs if not isDefault and isGlobal key is true.
553 * @return void
554 * @deprecated
555 * @see Kirki\App\Managers\PageManager::save_style_blocks()
556 */
557 public static function save_random_style_blocks($post_id, $style)
558 {
559 update_post_meta($post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random', $style);
560 // $data = array(
561 // 'type' => 'COLLABORATION_UPDATE_GLOBAL_STYLE',
562 // 'payload' => array( 'styleBlock' => $style ),
563 // );
564 //Collaboration::save_action_to_db( 'post', $post_id, $data, 1 );
565 }
566
567 /**
568 * Save staged style blocks for a specific post and staged meta key.
569 *
570 * @param int $post_id Post ID.
571 * @param string $meta_key Staged meta key (can be normal or random).
572 * @param array $styles Styles array to save.
573 *
574 * @deprecated
575 * @see \Kirki\App\Managers\PageManager::save_deprecated_global_style_blocks()
576 * @see \Kirki\App\Managers\PageManager::save_style_blocks()
577 */
578 public static function save_staged_style_blocks($post_id, $meta_key, $styles)
579 {
580 update_post_meta($post_id, $meta_key, $styles);
581 // $data = array(
582 // 'type' => 'COLLABORATION_UPDATE_GLOBAL_STYLE',
583 // 'payload' => array('styleBlock' => $styles),
584 // );
585 // Collaboration::save_action_to_db('post', $post_id, $data, 1);
586 }
587
588 /**
589 * No module import this method right now
590 *
591 * @return array
592 */
593 public static function get_custom_code_block_element()
594 {
595 return array(
596 'name' => 'custom-code',
597 'title' => 'Code',
598 'visibility' => true,
599 'properties' => array(
600 'tag' => 'div',
601 'content' => '',
602 'data-type' => 'code',
603 ),
604 'styleIds' => array(),
605 'className' => '',
606 'id' => 'kirki' . uniqid(),
607 'parentId' => 'body',
608 );
609 }
610 /**
611 * Get current editor mode is kirki or others
612 *
613 * @param int $post_id post id.
614 * @return bool true if kirki.
615 * @deprecated
616 * @see Kirki\App\Managers\PageManager::is_kirki_editor_mode()
617 */
618 public static function is_editor_mode_is_kirki($post_id)
619 {
620 $editor_mode = get_post_meta($post_id, KIRKI_META_NAME_FOR_POST_EDITOR_MODE, true);
621 if ('kirki' === $editor_mode) {
622 return true;
623 }
624 return false;
625 }
626
627 /**
628 * Get post url arr from post id
629 * preview_url, iframe_url, post_url
630 *
631 * @param int $post_id post id.
632 * @return array
633 *
634 * @deprecated
635 * @see \Kirki\App\Supports\PageUrl::class
636 */
637 public static function get_post_url_arr_from_post_id($post_id, $options = array())
638 {
639 $post_perma_link = self::get_page_perma_url($post_id);
640
641 $obj = ['post_url' => $post_perma_link, 'post_id' => $post_id];
642 if (isset($options['ajax_url']) && $options['ajax_url']) {
643 $protocol = strpos(home_url(), 'https://') !== false ? 'https' : 'http';
644 $obj['ajax_url'] = admin_url('admin-ajax.php', $protocol);
645 }
646
647 if (isset($options['preview_url']) && $options['preview_url']) {
648 $preview_url = self::get_page_preview_url($post_id);
649 $obj['preview_url'] = $preview_url;
650 }
651
652 if (isset($options['editor_url']) && $options['editor_url']) {
653 $obj['editor_url'] = add_query_arg(
654 array(
655 'action' => KIRKI_EDITOR_ACTION,
656 ),
657 $post_perma_link
658 );
659
660 if (HelperFunctions::is_api_call_from_editor_preview() && HelperFunctions::is_api_header_post_editor_preview_token_valid()) {
661 $headers = self::getallheaders();
662 $obj['editor_url'] = add_query_arg(
663 array(
664 'editor-preview-token' => isset($headers['Editor-Preview-Token']) ? $headers['Editor-Preview-Token'] : null,
665 ),
666 $obj['editor_url']
667 );
668 }
669 }
670 if (isset($options['iframe_url']) && $options['iframe_url']) {
671 $iframe_url_params = array(
672 'action' => KIRKI_EDITOR_ACTION,
673 'load_for' => 'kirki-iframe',
674 'post_id' => $post_id,
675 );
676 if (isset($_GET['editor-preview-token'])) {
677 $iframe_url_params['editor-preview-token'] = self::sanitize_text($_GET['editor-preview-token']);
678 }
679 $obj['iframe_url'] = add_query_arg(
680 $iframe_url_params,
681 $post_perma_link
682 );
683 }
684
685 if (isset($options['nonce']) && $options['nonce']) {
686 $obj['nonce'] = wp_create_nonce('wp_rest');
687 }
688
689 if (isset($options['editor_preview_token']) && $options['editor_preview_token']) {
690 $obj['editor_preview_token'] = isset($_GET['editor-preview-token']) ? self::sanitize_text($_GET['editor-preview-token']) : false;
691 }
692
693 if (isset($options['site_url']) && $options['site_url']) {
694 $obj['site_url'] = get_site_url();
695 }
696
697 if (isset($options['admin_url']) && $options['admin_url']) {
698 $obj['admin_url'] = get_admin_url();
699 }
700
701 if (isset($options['core_plugin_url']) && $options['core_plugin_url']) {
702 $obj['core_plugin_url'] = KIRKI_CORE_PLUGIN_URL;
703 }
704
705 if (isset($options['rest_url']) && $options['rest_url']) {
706 try {
707 $rest_url = rest_url();
708 $obj['rest_url'] = $rest_url;
709 } catch (\Throwable $th) {
710 $obj['rest_url'] = '';
711 }
712 }
713
714 return $obj;
715 }
716
717 /**
718 * @deprecated
719 * @see \Kirki\App\Supports\PageUrl::get_preview_url()
720 */
721 private static function get_page_preview_url($post_id)
722 {
723 $post = get_post($post_id);
724 if ($post && $post->post_type === 'kirki_template') {
725 $conditions = get_post_meta($post->ID, 'kirki_template_conditions', true);
726
727 $d = self::get_collection_items_from_conditions($conditions);
728 if ($d['type'] === 'post' && count($d['data']) > 0) {
729 return self::get_page_perma_url($d['data'][0]['ID']);
730 }
731 if ($d['type'] === 'user' && count($d['data']) > 0) {
732 return get_author_posts_url($d['data'][0]['ID']);
733 }
734 if ($d['type'] === 'term' && count($d['data']) > 0) {
735 return get_term_link($d['data'][0]['ID']);
736 }
737 }
738 return self::get_page_perma_url($post_id);
739 }
740
741 /**
742 * @deprecated
743 * @see \Kirki\App\Supports\PageUrl::get_page_permalink()
744 */
745 private static function get_page_perma_url($post_id)
746 {
747 $post_perma_link = get_permalink($post_id);
748 $protocol = strpos(home_url(), 'https://') !== false ? 'https' : 'http';
749 if ($protocol === 'https') {
750 $post_perma_link = str_replace('http://', 'https://', $post_perma_link);
751 } else {
752 $post_perma_link = str_replace('https://', 'http://', $post_perma_link);
753 }
754
755 return $post_perma_link;
756 }
757
758 /**
759 * @deprecated
760 * @see \Kirki\App\Supports\CollectionItem::get_items_from_condition()
761 */
762 public static function get_collection_items_from_conditions($conditions, $query = '')
763 {
764 $data = [];
765 $type = 'post';
766 $post_type = 'post';
767 $role = '';
768
769 if (is_array($conditions) && count($conditions) > 0) {
770 if (isset($conditions[0]['type'])) {
771 $type = $conditions[0]['type'];
772
773 if ($type === 'post') {
774 $post_type = $conditions[0]['post_type'];
775
776 // if the conditions has from key then it is term. then it will be term type.
777 if (isset($conditions[0]['from']) && $conditions[0]['from'] === 'term') {
778 $type = 'term';
779 }
780 }
781 if ($type === 'user') {
782 $role = $conditions[0]['to'];
783 }
784 } else {
785 //legacy support
786 $post_type = $conditions[0]['category'];
787 }
788 }
789
790
791 $numberposts = 20;
792 $post_status = array('publish', 'draft', 'future');
793
794 if ($type === 'post' && HelperFunctions::user_has_post_edit_access()) { // type will be wordpress post type
795 $arg = array(
796 'post_type' => $post_type,
797 'post_status' => $post_status,
798 'numberposts' => $numberposts,
799 'orderby' => 'ID',
800 'order' => 'DESC',
801 );
802 if ($query) {
803 $arg['s'] = $query;
804 }
805 $posts = get_posts($arg);
806 foreach ($posts as $key => $post) {
807 $data[] = array(
808 'ID' => $post->ID,
809 'title' => $post->post_title,
810 );
811 }
812 }
813
814 if ($type === 'user' && HelperFunctions::user_has_post_edit_access()) {
815 // get all users
816 $arg = array(
817 'role' => $role === '*' ? '' : $role,
818 'number' => $numberposts,
819 'orderby' => 'ID',
820 'order' => 'DESC',
821 );
822
823 if ($query) {
824 $arg['search'] = '*' . $query . '*';
825 }
826
827 $users = get_users($arg);
828 foreach ($users as $key => $user) {
829 $data[] = array(
830 'ID' => $user->ID,
831 'title' => $user->display_name,
832 );
833 }
834 }
835
836 if ($type === 'term' && HelperFunctions::user_has_post_edit_access()) {
837 // conditions
838 $taxonomy = [];
839
840 foreach ($conditions as $key => $condition) {
841 if ($condition['from'] === 'term') {
842 $taxonomy[] = $condition['where'];
843 }
844 }
845
846 $arg = array(
847 'taxonomy' => $taxonomy,
848 'number' => $numberposts,
849 'orderby' => 'ID',
850 'order' => 'DESC',
851 );
852
853 if ($query) {
854 $arg['search'] = $query;
855 }
856
857 $terms = get_terms($arg);
858
859 if (!is_wp_error($terms) && is_array($terms)) {
860 foreach ($terms as $term) {
861 // Handle both object and array cases safely
862 $term_id = is_object($term) ? $term->term_id : ($term['term_id'] ?? null);
863 $term_name = is_object($term) ? $term->name : ($term['name'] ?? null);
864
865 $data[] = [
866 'ID' => $term_id,
867 'title' => $term_name,
868 ];
869 }
870 }
871 }
872
873 return ['data' => $data, 'type' => $type];
874 }
875 /**
876 * Get post content from content value. like => id, title, description, content
877 *
878 * @param string $content_value for post dynamic content.
879 * @param object $post post object.
880 *
881 * @return string
882 */
883 public static function get_post_dynamic_content($content_value, $post = null, $meta_name = '', $dynamic_options = [])
884 {
885 if (isset($post) && !empty($post)) {
886 $post_id = $post->ID;
887 }
888
889 if (empty($post_id)) {
890 $post_id = self::get_post_id_if_possible_from_url();
891 }
892
893 $content = null;
894
895 switch ($content_value) {
896 case 'post_id': {
897 $content = $post_id;
898 break;
899 }
900
901 case 'post_title': {
902 $content = get_the_title($post_id);
903 break;
904 }
905
906 case 'post_excerpt': {
907
908 // Excerpt length global variable is only for kirki editor but not for the frontend.
909 if (isset($GLOBALS['kirki_post_excerpt_length']) && $GLOBALS['kirki_post_excerpt_length'] > 0) {
910 $content = wp_trim_words(get_the_excerpt($post_id), $GLOBALS['kirki_post_excerpt_length'], '[...]');
911 } else {
912 $content = get_the_excerpt($post_id);
913 }
914 break;
915 }
916
917 case 'post_author': {
918 $post = get_post($post_id);
919 if (!$post)
920 return "";
921 $content = get_the_author_meta('display_name', $post->post_author);
922 break;
923 }
924
925 case 'post_date': {
926 $content = get_the_date('', $post_id);
927 if (isset($dynamic_options['format'])) {
928 $content = HelperFunctions::format_date($content, $dynamic_options['format']);
929 }
930 break;
931 }
932
933 case 'post_time': {
934 $content = get_the_time('', $post_id);
935 if (isset($dynamic_options['timeFormat'])) {
936 $content = HelperFunctions::convert_time_format($content, $dynamic_options['timeFormat']);
937 }
938 break;
939 }
940
941 case 'post_content': {
942 $content = self::retrieve_post_content($post_id);
943 break;
944 }
945
946 case 'post_status': {
947 $content = get_post_status($post_id);
948 break;
949 }
950
951 case 'featured_image': {
952 $content = array(
953 'wp_attachment_id' => get_post_thumbnail_id($post_id),
954 'src' => get_the_post_thumbnail_url($post_id)
955 );
956 break;
957 }
958
959 case 'site_name': {
960 $content = get_bloginfo('name');
961 break;
962 }
963 case 'site_description': {
964 $content = get_bloginfo('description');
965 break;
966 }
967 case 'site_url': {
968 $content = get_site_url();
969 break;
970 }
971
972 case 'site_logo': {
973 $content = '';
974
975 // Try site icon first
976 $site_icon_id = get_option('site_icon');
977 if ($site_icon_id) {
978 $content = wp_get_attachment_url($site_icon_id);
979 }
980
981 // If no site icon, try get_site_icon_url()
982 if (!$content) {
983 $content = get_site_icon_url(512);
984 }
985
986 // If still nothing, try custom logo
987 if (!$content) {
988 $custom_logo_id = get_theme_mod('custom_logo');
989 if ($custom_logo_id) {
990 $imgs = wp_get_attachment_image_src($custom_logo_id, 'full');
991 if ($imgs && isset($imgs[0])) {
992 $content = $imgs[0];
993 }
994 }
995 }
996
997 break;
998 }
999
1000 case 'author_profile_picture': {
1001 $post = get_post($post_id);
1002
1003 if (!empty($post)) {
1004 $post_author = $post->post_author;
1005 $content = get_avatar_url($post_author);
1006 } else {
1007 $content = 'Something wrong!';
1008 }
1009
1010 break;
1011 }
1012
1013 case 'user_profile_picture': {
1014 $content = get_avatar_url(get_current_user_id());
1015 break;
1016 }
1017
1018 case 'post_page_link': {
1019 $url = \get_permalink($post_id);
1020 $content = !empty($url) ? $url : '';
1021 break;
1022 }
1023
1024 case 'author_posts_page_link': {
1025 $post = \get_post($post_id);
1026 $url = \get_author_posts_url($post->post_author);
1027 $content = !empty($url) ? $url : '';
1028 break;
1029 }
1030
1031 case 'post_meta': {
1032 if (!empty($meta_name)) {
1033 $meta = get_post_meta($post_id, $meta_name, true);
1034
1035 // For now, only string is supported!
1036 if (is_string($meta)) {
1037 $content = $meta;
1038 }
1039 }
1040
1041 break;
1042 }
1043
1044 default: {
1045 $post = get_post($post_id);
1046 if (isset($post) && 0 !== strpos(KIRKI_CONTENT_MANAGER_PREFIX, $post->post_type)) {
1047 $meta_key = ContentManagerHelper::get_child_post_meta_key_using_field_id($post->post_parent, $content_value);
1048 $content = get_post_meta($post->ID, $meta_key, true);
1049 $fields = ContentManagerHelper::get_post_type_custom_field_keys($post->post_parent);
1050
1051 if (!$content && isset($fields[$content_value], $fields[$content_value]['default_value'])) {
1052 $content = $fields[$content_value]['default_value'];
1053 }
1054
1055 if (isset($fields[$content_value]) && $fields[$content_value]['type'] === 'date') {
1056 $content = self::format_date($content, $fields[$content_value]['default_format']); //TODO: need to check with editor format
1057 }
1058 if (isset($fields[$content_value]['type']) && $fields[$content_value]['type'] === 'image') {
1059 $content = array(
1060 'wp_attachment_id' => $content['id'] ?? '',
1061 'src' => $content['url'] ?? '',
1062 );
1063 }
1064 if (isset($fields[$content_value]['type']) && $fields[$content_value]['type'] === 'time') {
1065 $time = "";
1066
1067 if (isset($fields[$content_value], $fields[$content_value]['default_value']) && $fields[$content_value]['default_value']) {
1068 $default_time = $fields[$content_value]['default_value'];
1069
1070 $value = isset($default_time['value']) ? $default_time['value'] : '00:00';
1071 $unit = isset($default_time['unit']) ? strtolower($default_time['unit']) : 'am';
1072
1073 $time = $value . ' ' . $unit;
1074 }
1075
1076 $content = $content ? $content['value'] . ' ' . $content['unit'] : $time;
1077
1078 if (isset($dynamic_options['timeFormat']) && $dynamic_options['timeFormat']) {
1079 $content = HelperFunctions::convert_time_format($content, $dynamic_options['timeFormat']);
1080 }
1081 }
1082
1083 } else {
1084 $content = 'Not Implemented';
1085 }
1086
1087 break;
1088 }
1089 }
1090
1091 return $content ? $content : '';
1092 }
1093
1094 /**
1095 * The function `get_user_dynamic_content` retrieves specific dynamic content related to a user based
1096 * on the provided content value.
1097 */
1098 public static function get_user_dynamic_content($content_value, $user_id = null, $meta_name = '', $dynamic_options = [])
1099 {
1100 $content = '';
1101
1102 // Get the user by ID
1103 $user = get_user_by('id', $user_id);
1104
1105 // fall back to the current user
1106 if (empty($user)) {
1107 $user = get_user_by('id', get_current_user_id());
1108 }
1109
1110
1111 if (empty($user)) {
1112 return $content;
1113 }
1114
1115 // Switch statement to handle dynamic content based on the content_value
1116 switch ($content_value) {
1117 case 'display_name':
1118 return $user->display_name;
1119
1120 case 'user_email':
1121 return $user->user_email;
1122
1123 case 'user_nicename':
1124 return $user->user_nicename;
1125
1126 case 'registered_date': {
1127 $date = $user->user_registered;
1128
1129 if (isset($dynamic_options['format'])) {
1130 return HelperFunctions::format_date($date, $dynamic_options['format']);
1131 }
1132
1133 $date = new DateTime($date);
1134
1135 return $date->format('F j, Y');
1136 }
1137
1138 case 'registered_time': {
1139 $date = new DateTime($user->user_registered);
1140 return $date->format('H:i:a');
1141 }
1142
1143 case 'user_url':
1144 return get_author_posts_url($user->ID);
1145
1146 case 'profile_image':
1147 return get_avatar_url($user->ID);
1148
1149 case 'user_meta': {
1150 if (!empty($meta_name)) {
1151 $meta = get_user_meta($user->ID, $meta_name, true);
1152
1153 if (is_string($meta)) {
1154 return $meta;
1155 }
1156 return '';
1157 }
1158 }
1159
1160 case 'initials': {
1161 $first_name = isset($user->first_name) ? $user->first_name : '';
1162 $last_name = isset($user->last_name) ? $user->last_name : '';
1163
1164 $initials = '';
1165 if ($first_name) {
1166 $initials .= strtoupper(substr($first_name, 0, 1));
1167 }
1168
1169 if ($last_name) {
1170 $initials .= strtoupper(substr($last_name, 0, 1));
1171 }
1172
1173 // if initials are lenght 1 or empty, then the first two letters of the username
1174 if (empty($initials) || strlen($initials) < 2) {
1175 $username = isset($user->user_login) ? $user->user_login : '';
1176 $initials = strtoupper(substr($username, 0, 2));
1177 }
1178
1179 return $initials;
1180 }
1181
1182 default:
1183 return '';
1184 }
1185 }
1186
1187 private static function get_value($data, $key)
1188 {
1189 if (is_array($data)) {
1190 return $data[$key] ?? '';
1191 }
1192
1193 if (is_object($data)) {
1194 return $data->$key ?? '';
1195 }
1196
1197 return '';
1198 }
1199
1200 public static function get_term_dynamic_content($content_value, $term_id = null, $meta_name = '')
1201 {
1202 $term = get_term($term_id);
1203
1204 if (empty($term)) {
1205 return '';
1206 }
1207
1208 switch ($content_value) {
1209 case 'name':
1210 return self::get_value($term, 'name');
1211
1212 case 'description':
1213 return self::get_value($term, 'description');
1214
1215 case 'slug':
1216 return self::get_value($term, 'slug');
1217
1218 case 'count':
1219 return self::get_value($term, 'count');
1220
1221 case 'meta':
1222 if (!empty($meta_name)) {
1223 $term_id = self::get_value($term, 'term_id');
1224 $meta = get_term_meta($term_id, $meta_name, true);
1225
1226 return is_scalar($meta) ? (string) $meta : '';
1227 }
1228 return '';
1229
1230 default:
1231 return '';
1232 }
1233 }
1234
1235 public static function retrieve_post_content($post_id)
1236 {
1237 $content = apply_filters('the_content', get_the_content(null, false, $post_id));
1238
1239 $load_for = HelperFunctions::sanitize_text(isset($_GET['load_for']) ? $_GET['load_for'] : '');
1240
1241 if ($load_for !== 'kirki-iframe' && $kirki_data = HelperFunctions::is_kirki_type_data($post_id)) {
1242 $params = array(
1243 'blocks' => $kirki_data['blocks'],
1244 'style_blocks' => $kirki_data['styles'],
1245 'root' => 'root',
1246 'post_id' => $post_id,
1247 );
1248 $content = apply_filters('the_content', HelperFunctions::get_html_using_preview_script($params));
1249 }
1250
1251 return $content;
1252 }
1253
1254 /**
1255 * This methos is for if Theme enque some style and css then it will remove those styel and css codes.
1256 *
1257 * @return void
1258 */
1259 public static function remove_theme_style()
1260 {
1261 $theme = wp_get_theme();
1262 $parent_style = $theme->stylesheet . '-style';
1263 wp_dequeue_style($parent_style);
1264 wp_deregister_style($parent_style);
1265 wp_dequeue_style($parent_style . '-css');
1266 wp_deregister_style($parent_style . '-css');
1267 }
1268
1269 public static function dequeue_all_except_my_plugin()
1270 {
1271
1272 global $wp_scripts, $wp_styles, $kirki_editor_assets;
1273
1274 foreach ($wp_scripts->queue as $handle) {
1275
1276 // Keep WordPress media scripts
1277 if (
1278 str_starts_with($handle, 'media') ||
1279 str_starts_with($handle, 'wp-media') ||
1280 in_array($handle, ['underscore', 'backbone', 'jquery', 'wp-util'])
1281 ) {
1282 continue;
1283 }
1284
1285 wp_dequeue_script($handle);
1286 }
1287
1288 foreach ($wp_styles->queue as $handle) {
1289
1290 // Keep media related styles
1291 if (
1292 str_starts_with($handle, 'media') ||
1293 in_array($handle, ['buttons', 'dashicons', 'imgareaselect'])
1294 ) {
1295 continue;
1296 }
1297
1298 wp_dequeue_style($handle);
1299 }
1300
1301 if (!empty($kirki_editor_assets['scripts'])) {
1302 foreach ($kirki_editor_assets['scripts'] as $script_handle) {
1303 wp_enqueue_script($script_handle);
1304 }
1305 }
1306
1307 if (!empty($kirki_editor_assets['styles'])) {
1308 foreach ($kirki_editor_assets['styles'] as $style_handle) {
1309 wp_enqueue_style($style_handle);
1310 }
1311 }
1312 }
1313
1314 /**
1315 * Generate html using Preview script.
1316 *
1317 * @param array $data blocks.
1318 * @param array $style_blocks style_blocks.
1319 * @param string $root data root.
1320 * @param int $id if need prefix like symbol or popup post_id.
1321 * @return string the html string.
1322 */
1323 public static function get_html_using_preview_script(array $params = [])
1324 {
1325
1326 $blocks = $params['blocks'] ?? null;
1327 $style_blocks = $params['style_blocks'] ?? null;
1328 $root = $params['root'] ?? null;
1329 $options = $params['options'] ?? [];
1330 $post_id = $params['post_id'] ?? null;
1331 $get_style = $params['get_style'] ?? true;
1332 $get_variable = $params['get_variable'] ?? true;
1333 $get_fonts = $params['get_fonts'] ?? true;
1334 $should_take_app_script = $params['should_take_app_script'] ?? true;
1335 $prefix = $params['prefix'] ?? false;
1336 $get_all_style_forcefully_if_get_style_true = $params['get_all_style_forcefully_if_get_style_true'] ?? false;
1337
1338 if ($blocks) {
1339 $options['search_related_collection_ids'] = isset($params['search_related_collection_ids']) ? $params['search_related_collection_ids'] : self::collect_search_related_collection_ids($blocks);
1340 }
1341
1342 //set initial context data start
1343 if (!isset($options['post'])) {
1344 $post = get_post(get_the_ID());
1345 if ($post) {
1346 $options['post'] = $post;
1347 $options['itemType'] = 'post';
1348 }
1349 }
1350 if (!isset($options['user'])) {
1351 $user = get_user_by('id', get_current_user_id());
1352 if ($user) {
1353 $options['user'] = Users::get_format_single_user_data($user);
1354 }
1355 }
1356 //set initial context data end
1357
1358 $preview = new Preview($blocks, $style_blocks, $root, $post_id, $prefix);
1359 $html = $preview->getHtml($options);// this method need to call first cause after that only used style block array construct.
1360 $only_used_style_blocks = $get_all_style_forcefully_if_get_style_true ? $style_blocks : $preview->get_only_used_style_blocks();
1361 $s = '';
1362 if ($get_fonts) {
1363 $s .= $preview->getCustomFontsLinks();
1364 }
1365
1366 if ($get_style) {
1367 //style will be false when it calls from collection single item. only first item will generate style. others item will be same.
1368 $s .= $preview->getStyleTag($only_used_style_blocks);
1369 }
1370
1371 $s .= $preview->get_interaction_set_as_initial_css();
1372
1373
1374
1375 $s .= $html;
1376 $s .= $preview->getScriptTag($should_take_app_script);
1377 return $s;
1378 }
1379
1380 private static function collect_search_related_collection_ids($data)
1381 {
1382 $result = array();
1383 foreach ($data as $item) {
1384 if (
1385 isset($item['properties']['dynamicContent']['related']) &&
1386 $item['properties']['dynamicContent']['related'] === true &&
1387 !empty($item['properties']['dynamicContent']['relatedCollection'])
1388 ) {
1389 $result[$item['properties']['dynamicContent']['relatedCollection']] = true;
1390 }
1391 }
1392 return $result;
1393 }
1394
1395 public static function get_custom_fonts_tags()
1396 {
1397 $custom_fonts = HelperFunctions::get_global_data_using_key(KIRKI_USER_CUSTOM_FONTS_META_KEY);
1398 if ($custom_fonts) {
1399 $s = '';
1400 foreach ($custom_fonts as $key => $fonts_data) {
1401 $s .= self::getFontsHTMLMarkup($fonts_data);
1402 }
1403 return $s;
1404 }
1405 }
1406
1407 public static function getFontsHTMLMarkup($fonts_data)
1408 {
1409 $font_family = str_replace(' ', '+', $fonts_data['family']);
1410 if (isset($fonts_data['fontUrl']) && !in_array($font_family, self::$printed_font_family_tracker, true)) {
1411 self::$printed_font_family_tracker[] = $font_family;
1412
1413 $font_url = isset($fonts_data['localUrl']) ? $fonts_data['localUrl'] : $fonts_data['fontUrl'];
1414
1415 //phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
1416 return '<link class="' . 'kirki-custom-fonts-link" href="' . $font_url . '" rel="stylesheet">';
1417 }
1418 return '';
1419 }
1420
1421 /**
1422 * Generate new id and html string for: symbol, collection etc.
1423 *
1424 * @param array $data blocks.
1425 * @param array $style_blocks style_blocks.
1426 * @param string $root data root.
1427 */
1428 public static function rec_update_data_id_then_return_new_html($data, $style_blocks, $root = 'body', $options = [], $get_style = true)
1429 {
1430 $data_helper = new DataHelper();
1431 $data_helper->rec_update_data_id_to_new_id($data, $style_blocks, $root, null);
1432 $data = $data_helper->temp_data;
1433 $style_blocks = $data_helper->temp_styles;
1434 $root = isset($data_helper->temp_ids[$root]) ? $data_helper->temp_ids[$root] : false;
1435
1436 $params = array(
1437 'blocks' => $data,
1438 'style_blocks' => $style_blocks,
1439 'root' => $root,
1440 'post_id' => null,
1441 'options' => $options,
1442 'get_style' => $get_style,
1443 'get_all_style_forcefully_if_get_style_true' => true,//this will generate collection first item all style
1444 );
1445
1446 return self::get_html_using_preview_script($params);
1447 }
1448
1449 // hook
1450 public static function kirki_html_generator($s, $post_id, $staging_version = false)
1451 {
1452 $d = self::is_kirki_type_data($post_id, $staging_version);
1453 if ($d) {
1454 $params = array(
1455 'blocks' => $d['blocks'],
1456 'style_blocks' => $d['styles'],
1457 'root' => 'root',
1458 'post_id' => $post_id,
1459 );
1460 return self::get_html_using_preview_script($params);
1461 }
1462 return false;
1463 }
1464
1465 /**
1466 * Find symbol for post id using condition
1467 * it will find and return selected symbols html and css;
1468 *
1469 * @param string $type : post | user
1470 * @param array $context : post object or user object
1471 * @return symbol || bool(false)
1472 */
1473 public static function find_template_for_this_context($type = 'post', $context = null)
1474 {
1475 $templates = Page::fetch_list('kirki_template', true, array('publish'));
1476 if (!$templates || !$context)
1477 return false;
1478 foreach ($templates as $key => $template) {
1479 if (isset($template['conditions'])) {
1480 $conditions = $template['conditions'];
1481 if ($type === 'user') {
1482 if (self::check_all_conditions_for_this_user($context, $conditions)) {
1483 return $template;
1484 }
1485 } else if ($type === 'post') {
1486 if (self::check_all_conditions_for_this_post($context, $conditions)) {
1487 return $template;
1488 }
1489 } else if ($type === 'term') {
1490 if (self::check_all_conditions_for_this_term($context, $conditions)) {
1491 return $template;
1492 }
1493 }
1494 }
1495 }
1496 return false;
1497 }
1498
1499 /**
1500 * Generate popup html
1501 *
1502 * @return strint
1503 */
1504 public static function get_page_popups_html()
1505 {
1506 global $post;
1507 if ($post) {
1508 $popups = self::get_page_popups();
1509 if (count($popups) > 0) {
1510 $s = '';
1511 foreach ($popups as $key => $popup) {
1512 $params = array(
1513 'blocks' => $popup['blocks'],
1514 'style_blocks' => $popup['styleBlocks'],
1515 'root' => $popup['root'],
1516 'post_id' => $popup['id'],
1517 );
1518 $s .= self::get_html_using_preview_script($params);
1519 }
1520 return do_shortcode($s);
1521 }
1522 }
1523 return '';
1524 }
1525
1526 /**
1527 * Get Custom popups
1528 * it will find and return selected popups;
1529 *
1530 * @return array
1531 */
1532 public static function get_page_popups()
1533 {
1534 global $post;
1535 if ($post) {
1536 $popups = Page::fetch_list('kirki_popup', true, array('publish'));
1537 return self::find_popups_for_this_post($popups, $post);
1538 }
1539 return array();
1540 }
1541
1542 /**
1543 * Find popups for post id using condition
1544 * it will find and return selected popups arr;
1545 *
1546 * @param object $popups popup block object.
1547 * @param object $post post object.
1548 * @return popups || [];
1549 */
1550 public static function find_popups_for_this_post($popups, $post)
1551 {
1552 $arr = array();
1553 $post_id = $post->ID;
1554 foreach ($popups as $key => $popup) {
1555 $popup = self::format_single_popup_data_for_html_print($popup);
1556 if (self::check_popup_pagearr_logic($popup, $post_id)) {
1557 $arr[] = $popup;
1558 }
1559 }
1560 return $arr;
1561 }
1562
1563 private static function format_single_popup_data_for_html_print($popup)
1564 {
1565 if (!$popup['blocks'])
1566 return $popup;
1567 $root = false;
1568 foreach ($popup['blocks'] as $key2 => &$b) {
1569 if ('root' === $b['parentId']) {
1570 $root = $b['id'];
1571
1572 if (isset($b['properties']['attributes'])) {
1573 $b['properties']['attributes']['popup-id'] = $popup['id'];
1574 } else {
1575 $b['properties']['attributes'] = array(
1576 'popup-id' => $popup['id'],
1577 );
1578 }
1579 $popup['root'] = $root;
1580 }
1581 }
1582
1583 return $popup;
1584 }
1585
1586 /**
1587 * Check popup is active apply for this post or not.
1588 *
1589 * @param array $popup popup from Page::fetch_list.
1590 * @param int $post_id wp post id.
1591 * @return bool
1592 */
1593 private static function check_popup_pagearr_logic($popup, $post_id)
1594 {
1595 if (!isset($popup['root']))
1596 return false;
1597 $root = $popup['root'];
1598 if ($root && isset($popup['blocks'][$root]['properties']['popup']['visibilityConditions'])) {
1599 $conditions = $popup['blocks'][$root]['properties']['popup']['visibilityConditions'];
1600 $post = get_post($post_id);
1601 if (self::check_all_conditions_for_this_post($post, $conditions) || in_array($popup['id'], Preview::$only_used_popup_id_array, true)) {
1602 return true;
1603 }
1604 }
1605 return false;
1606 }
1607
1608 /**
1609 * Check all conditon for this post
1610 * This method will match all the condition and return true or false
1611 * category = * || post_type
1612 * taxonomy = 1=single post || * = all post || taxonomy slug
1613 * apply[to] = * = all || id = post id || term id
1614 * visibility = show || hide
1615 *
1616 * @param object $post post object.
1617 * @param object $conditions symbol visibility condiotions object.
1618 *
1619 * @return Boolean
1620 */
1621 public static function check_all_conditions_for_this_post($post, $conditions)
1622 {
1623 $show_flag = false;
1624 $hide_flag = false;
1625
1626 foreach ($conditions as $key => $condition) {
1627 if (!isset($condition['from'])) {
1628 $condition['from'] = 'post';
1629 }
1630 if ($condition['from'] === 'term') {
1631 return false;
1632 }
1633 if (isset($condition['category'])) {
1634 //legacy
1635 if ($condition['category'] === '*') {
1636 $condition['type'] = '*';
1637 } else {
1638 $condition['post_type'] = $condition['category'];
1639 $condition['type'] = 'post';
1640 }
1641 }
1642
1643 if (!isset($condition['visibility'])) {
1644 $condition['visibility'] = 'show';
1645 }
1646
1647 if ($condition['type'] === '*') {
1648 // Entire site
1649 $show_flag = $condition['visibility'] === 'show';
1650 } elseif ($condition['type'] === 'post') {
1651 if ($condition['post_type'] === $post->post_type) {
1652 // Post type related
1653 if ($condition['where'] == '*') {
1654 // All posts
1655 $show_flag = $condition['visibility'] === 'show';
1656 } elseif ($condition['where'] == 'single') {
1657 // Single post
1658 if ($condition['to'] === $post->ID) {
1659 $show_flag = $condition['visibility'] === 'show';
1660 $hide_flag = $condition['visibility'] === 'hide';
1661 }
1662 } else {
1663 // Taxonomy
1664 $taxonomy = $condition['where'];
1665 $term = $condition['to'];
1666 if ($term === '*') {
1667 // All terms
1668 $show_flag = $condition['visibility'] === 'show';
1669 } else {
1670 if (has_term($term, $taxonomy, $post->ID)) {
1671 $show_flag = $condition['visibility'] === 'show';
1672 $hide_flag = $condition['visibility'] === 'hide';
1673 }
1674 }
1675 }
1676 }
1677 }
1678
1679 // If hide flag is set, stop and return false
1680 if ($hide_flag) {
1681 return false;
1682 }
1683 }
1684 return $show_flag;
1685 }
1686
1687
1688 public static function check_all_conditions_for_this_term($term, $conditions)
1689 {
1690 $show_flag = false;
1691 $hide_flag = false;
1692 foreach ($conditions as $key => $condition) {
1693 if (!isset($condition['visibility'])) {
1694 $condition['visibility'] = 'show';
1695 }
1696 if (!isset($condition['from'])) {
1697 $condition['from'] = 'post';
1698 }
1699
1700 if ($condition['from'] !== 'term') {
1701 return false;
1702 }
1703
1704 if ($condition['type'] === '*') {
1705 // Entire site
1706 $show_flag = $condition['visibility'] === 'show';
1707 } elseif ($condition['type'] === 'post') {
1708
1709 if ($condition['post_type'] === $term['post_type']) {
1710 // Post type related
1711 if ($condition['where'] == '*') {
1712 // All posts
1713 $show_flag = $condition['visibility'] === 'show';
1714 } else {
1715 // Taxonomy
1716 $taxonomy = $condition['where'];
1717 $con_term = $condition['to'];
1718 if ($con_term === '*') {
1719 // All terms
1720 $show_flag = $condition['visibility'] === 'show';
1721 } else {
1722 if ($term['taxonomy'] == $con_term) {
1723 $show_flag = $condition['visibility'] === 'show';
1724 $hide_flag = $condition['visibility'] === 'hide';
1725 }
1726 }
1727 }
1728 }
1729 }
1730
1731 // If hide flag is set, stop and return false
1732 if ($hide_flag) {
1733 return false;
1734 }
1735 }
1736 return $show_flag;
1737 }
1738
1739 public static function check_all_conditions_for_this_user($user, $conditions)
1740 {
1741 $show_flag = false;
1742 $hide_flag = false;
1743 foreach ($conditions as $key => $condition) {
1744
1745 if (isset($condition['category'])) {
1746 //legacy
1747 if ($condition['category'] === '*') {
1748 $condition['type'] = '*';
1749 } else {
1750 $condition['post_type'] = $condition['category'];
1751 $condition['type'] = 'post';
1752 }
1753 }
1754
1755 if (!isset($condition['visibility'])) {
1756 $condition['visibility'] = 'show';
1757 }
1758
1759 if ($condition['type'] === '*') {
1760 // Entire site
1761 $show_flag = $condition['visibility'] === 'show';
1762 } elseif ($condition['type'] === 'user') {
1763 if ($condition['where'] === '*') {
1764 $show_flag = $condition['visibility'] === 'show';
1765 } elseif ($condition['where'] === 'role') {
1766 $user_role = $condition['to'];
1767
1768 if ($user_role === '*') {
1769 $show_flag = $condition['visibility'] === 'show';
1770 } else {
1771 $curr_user = $user;
1772 if (in_array($user_role, $curr_user['roles'])) {
1773 $show_flag = $condition['visibility'] === 'show';
1774 }
1775 }
1776 }
1777 }
1778
1779 if ($hide_flag) {
1780 return false;
1781 }
1782 }
1783
1784 return $show_flag;
1785 }
1786
1787
1788 private static function attribute_in_post_table($attr = '')
1789 {
1790 $post_table_attributes = array(
1791 'ID',
1792 'post_author',
1793 'post_date',
1794 'post_date_gmt',
1795 'post_content',
1796 'post_title',
1797 'post_excerpt',
1798 'post_status',
1799 'post_name',
1800 'post_type',
1801 'post_category',
1802 'term'
1803 );
1804
1805 return in_array($attr, $post_table_attributes, true);
1806 }
1807
1808 private static function sort_filters_by_relation($filter_items = array())
1809 {
1810 $sorted_array = array();
1811
1812 if (is_array($filter_items)) {
1813 array_walk($filter_items, function ($item) use (&$sorted_array) {
1814 $relation_raw = isset($item['relation']) ? $item['relation'] : 'OR';
1815 $relation = in_array(strtoupper($relation_raw), ['AND', 'OR'], true) ? strtoupper($relation_raw) : 'OR';
1816
1817 if (!isset($sorted_array[$relation])) {
1818 $sorted_array[$relation] = array();
1819 }
1820
1821 unset($item['relation']);
1822
1823 $sorted_array[$relation][] = $item;
1824 });
1825 }
1826
1827 return $sorted_array;
1828 }
1829
1830 /**
1831 * text, number, date/time, options, switch
1832 */
1833 private static function post_table_filter_query($filter_item, $data_type)
1834 {
1835 $field_name = $filter_item['id'];
1836 $sorted_array = self::sort_filters_by_relation($filter_item['items']);
1837
1838 $where_sql = '';
1839
1840 array_walk($sorted_array, function ($sorted_array_item, $condition) use ($data_type, $field_name, &$where_sql) {
1841 global $wpdb;
1842
1843 $conditions = array();
1844 $column_name = "$wpdb->posts.$field_name";
1845
1846 array_walk($sorted_array_item, function ($filter_condition_item) use ($data_type, $column_name, &$conditions) {
1847 switch ($data_type) {
1848 case 'text': {
1849 $conditions[] = PostsQueryUtils::post_table_text_query($column_name, $filter_condition_item['condition'], $filter_condition_item['value']);
1850 break;
1851 }
1852
1853 // case 'date': {
1854 // $conditions[] = PostsQueryUtils::post_table_text_query($column_name, $filter_condition_item['condition'], $filter_condition_item['value']);
1855 // break;
1856 // }
1857
1858 // case 'number': {
1859 // $conditions[] = PostsQueryUtils::post_table_number_query($column_name, $filter_condition_item['condition'], $filter_condition_item['value']);
1860 // break;
1861 // }
1862
1863 // case 'option': {
1864 // $conditions[] = PostsQueryUtils::post_table_options_query($column_name, $filter_condition_item['condition'], $filter_condition_item['value']);
1865 // break;
1866 // }
1867
1868 // case 'switch': {
1869 // $conditions[] = PostsQueryUtils::post_table_switch_query($column_name, $filter_condition_item['condition'], $filter_condition_item['value']);
1870 // break;
1871 // }
1872
1873 default: {
1874 break;
1875 }
1876 }
1877 });
1878
1879 if (empty($conditions)) {
1880 return;
1881 }
1882
1883 $condition_sql = implode(" {$condition} ", $conditions);
1884
1885 if ('OR' === $condition) {
1886 $condition_sql = "({$condition_sql})";
1887 }
1888
1889 /**
1890 * A query to
1891 * [x start with 'JoomShaper' or 'IcoFont' or 'Kirki'
1892 * and contains 'website' and ends with 'Ollyo']
1893 * will be like below:
1894 *
1895 * AND (x LIKE 'JoomShaper%' OR x LIKE 'IcoFont%' OR x LIKE 'Kirki%') AND x LIKE '%website%' AND x LIKE '%Ollyo'
1896 */
1897 $where_sql .= " AND $condition_sql";
1898 });
1899
1900 if (empty($where_sql)) {
1901 return null;
1902 }
1903
1904 $callback = function ($where) use ($where_sql) {
1905 $where .= $where_sql;
1906 return $where;
1907 };
1908
1909 add_filter('posts_where', $callback);
1910
1911 return $callback;
1912 }
1913
1914 /**
1915 * text, number, date/time, options, switch
1916 * help: https://wordpress.stackexchange.com/questions/159426/meta-query-with-string-starting-like-pattern
1917 */
1918
1919 private static function post_meta_table_filter_query($filter_item, $key, $data_type)
1920 {
1921 $sorted_array = self::sort_filters_by_relation($filter_item['items']);
1922 $meta_query = array();
1923
1924 array_walk($sorted_array, function ($sorted_array_item, $condition) use (&$meta_query, $key, $data_type) {
1925 if (count($sorted_array_item) > 0) {
1926 $condition_arr = array('relation' => $condition);
1927
1928 array_walk($sorted_array_item, function ($filter_condition_item) use (&$condition_arr, $key, $data_type) {
1929
1930 switch ($data_type) {
1931 case 'text': {
1932 $condition_arr[] = PostsQueryUtils::post_meta_table_text_query($key, $filter_condition_item['condition'], $filter_condition_item['value']);
1933 break;
1934 }
1935
1936 case 'date': {
1937 $condition_arr[] = PostsQueryUtils::post_meta_table_date_query($key, $filter_condition_item); // ['start-date' => '', 'end-date' => '']);
1938 break;
1939 }
1940
1941 case 'number': {
1942 $condition_arr[] = PostsQueryUtils::post_meta_table_number_query($key, $filter_condition_item['condition'], $filter_condition_item['value']);
1943 break;
1944 }
1945
1946 case 'option': {
1947 $condition_arr[] = PostsQueryUtils::post_meta_table_options_query($key, $filter_condition_item['condition'], $filter_condition_item['values']);
1948 break;
1949 }
1950
1951 case 'switch': {
1952 $condition_arr[] = PostsQueryUtils::post_meta_table_switch_query($key, $filter_condition_item['condition']);
1953 break;
1954 }
1955
1956 default: {
1957 break;
1958 }
1959 }
1960 });
1961
1962 $meta_query[] = $condition_arr;
1963 }
1964 });
1965
1966 return $meta_query;
1967 }
1968
1969 /**
1970 * filter query for reference table
1971 *
1972 * @param object $filter_item filter item.
1973 * @param string $key field meta key.
1974 * @param array $args args.
1975 *
1976 * @return array args
1977 */
1978
1979 private static function cm_reference_table_filter_query($filter_item, $key, $args)
1980 {
1981 global $wpdb;
1982
1983 $sorted_array = self::sort_filters_by_relation($filter_item['items']);
1984
1985 $post_ids_in = [];
1986 $post_ids_not_in = [];
1987
1988 $has_in_condition = false;
1989 $has_not_in_condition = false;
1990
1991 foreach ($sorted_array as $relation_str => $filters_by_relation) {
1992 foreach ($filters_by_relation as $filter_condition_item) {
1993 $condition = $filter_condition_item['condition'];
1994 $value = isset($filter_condition_item['value']) ? (int) $filter_condition_item['value'] : null;
1995
1996 if (!$value)
1997 continue;
1998
1999 $results = $wpdb->get_col($wpdb->prepare(
2000 "SELECT post_id FROM {$wpdb->prefix}kirki_cm_reference WHERE field_meta_key = %s AND ref_post_id = %d",
2001 $key,
2002 $value
2003 ));
2004
2005 $results = array_map('intval', $results);
2006
2007 if ($condition === 'in') {
2008 $has_in_condition = true;
2009 if ($relation_str === 'AND') {
2010 $post_ids_in[] = $results;
2011 } else {
2012 $post_ids_in = array_merge($post_ids_in, $results);
2013 }
2014 } elseif ($condition === 'not-in') {
2015 $has_not_in_condition = true;
2016 if ($relation_str === 'AND') {
2017 $post_ids_not_in[] = $results;
2018 } else {
2019 $post_ids_not_in = array_merge($post_ids_not_in, $results);
2020 }
2021 }
2022 }
2023 }
2024
2025 // Handle post__in only if there was an 'in' condition
2026 if ($has_in_condition) {
2027 if (!empty($post_ids_in)) {
2028 $post_ids_in = is_array(reset($post_ids_in))
2029 ? array_reduce($post_ids_in, 'array_intersect', array_shift($post_ids_in)) // ids-> [[1,2], [2,3]] -> array_reduce($ids, 'array_intersect', [1,2])
2030 : $post_ids_in;
2031
2032 $args['post__in'] = isset($args['post__in'])
2033 ? array_intersect($args['post__in'], $post_ids_in)
2034 : $post_ids_in;
2035
2036 if (empty($args['post__in'])) {
2037 $args['post__in'] = [0];
2038 }
2039 } else {
2040 // 'in' condition was given, but returned nothing
2041 $args['post__in'] = [0];
2042 }
2043 }
2044
2045 // Handle post__not_in normally
2046 if ($has_not_in_condition && !empty($post_ids_not_in)) {
2047 $post_ids_not_in = is_array(reset($post_ids_not_in))
2048 ? array_merge(...$post_ids_not_in)
2049 : $post_ids_not_in;
2050
2051 $args['post__not_in'] = isset($args['post__not_in'])
2052 ? array_merge($args['post__not_in'], $post_ids_not_in)
2053 : $post_ids_not_in;
2054 }
2055
2056 return $args;
2057 }
2058
2059 /**
2060 * handle legacy filter data
2061 *
2062 * @param object $params filter array.
2063 *
2064 * @return array filter array
2065 */
2066 public static function handle_legacy_filter_to_new_filter($filters)
2067 {
2068 $new_filters = array();
2069
2070 if (is_array($filters)) {
2071 foreach ($filters as $key => $item) {
2072 if (!isset($item['id']) && isset($item['type']) && $item['type']) {
2073 switch ($item['type']) {
2074 case 'date': {
2075 $new_filters[] = array(
2076 'type' => 'post_date',
2077 'id' => 'post_date',
2078 'title' => 'Post Date',
2079 'items' => [
2080 array(
2081 'start-date' => isset($item['start-date']) ? $item['start-date'] : '',
2082 'end-date' => isset($item['end-date']) ? $item['end-date'] : '',
2083 'relation' => 'OR',
2084 )
2085 ],
2086 );
2087
2088 break;
2089 }
2090
2091 case 'author': {
2092 $new_filters[] = array(
2093 'type' => 'post_author',
2094 'id' => 'post_author',
2095 'title' => 'Author',
2096 'items' => [
2097 array(
2098 'condition' => 'in',
2099 'values' => $item['values'],
2100 'relation' => 'OR',
2101 )
2102 ],
2103 );
2104
2105 break;
2106 }
2107
2108 case 'category': {
2109 $new_filters[] = array(
2110 'type' => 'post_category',
2111 'id' => 'post_category',
2112 'title' => 'Category',
2113 'items' => [
2114 array(
2115 'condition' => 'in',
2116 'values' => $item['values'],
2117 'relation' => 'OR',
2118 )
2119 ],
2120 );
2121 break;
2122 }
2123
2124 default: {
2125 break;
2126 }
2127 }
2128 } else {
2129 $new_filters[] = $item;
2130 }
2131 }
2132 }
2133 return $new_filters;
2134 }
2135
2136 /**
2137 * Static callback for posts_where filter to allow removal with remove_filter.
2138 *
2139 * @param string $where The WHERE clause.
2140 * @return string Modified WHERE clause.
2141 */
2142 public static function posts_where_filter_callback($where)
2143 {
2144 global $wpdb;
2145
2146 $params = self::$posts_where_filter_params;
2147 if (empty($params)) {
2148 return $where;
2149 }
2150
2151 $query = $params['query'];
2152 $reference_where_sql = $params['reference_where_sql'];
2153 $post_parent = $params['post_parent'];
2154
2155 $search = esc_sql($wpdb->esc_like($query));
2156
2157 $where .= $wpdb->prepare(
2158 " OR (
2159 ({$wpdb->posts}.post_title LIKE %s OR {$wpdb->posts}.post_content LIKE %s)
2160 AND {$wpdb->posts}.post_parent = %d
2161 )",
2162 "%{$search}%",
2163 "%{$search}%",
2164 $post_parent
2165 );
2166
2167 if (!empty($reference_where_sql)) {
2168 $where .= " {$reference_where_sql}";
2169 }
2170
2171 return $where;
2172 }
2173
2174 /**
2175 * Get dynamic collection data
2176 *
2177 * @param object $params query object.
2178 *
2179 * @return array post array
2180 */
2181 public static function get_posts($params)
2182 {
2183 $name = isset($params['name']) ? $params['name'] : null;
2184 $sorting = isset($params['sorting']) ? $params['sorting'] : null;
2185 $filters = isset($params['filters']) ? $params['filters'] : null;
2186 $inherit = (bool) ($params['inherit'] ?? false);
2187 $related = (bool) ($params['related'] ?? false);
2188 $post_parent = (int) ($params['post_parent'] ?? 0);
2189 $post_status = isset($params['post_status']) ? $params['post_status'] : 'publish';
2190 $query = isset($params['q']) ? $params['q'] : '';
2191 $IDs = isset($params['IDs']) ? $params['IDs'] : [];
2192 $related_post_parent = isset($params['related_post_parent']) ? $params['related_post_parent'] : self::get_post_id_if_possible_from_url();
2193
2194 // add new
2195 $current_page = isset($params['current_page']) ? $params['current_page'] : 1;
2196 $item_per_page = isset($params['item_per_page']) ? $params['item_per_page'] : 3;
2197 $offset = isset($params['offset']) ? $params['offset'] : 0;
2198 $context = isset($params['context']) ? $params['context'] : null;
2199 $tax_query = [
2200 'relation' => 'AND',
2201 ];
2202
2203 // Calculate the offset
2204 $offset = ($current_page - 1) * $item_per_page + $offset;
2205
2206 $args = array(
2207 'posts_per_page' => $item_per_page,
2208 'paged' => $current_page,
2209 'offset' => $offset,
2210 'post_type' => $name,
2211 'suppress_filters' => true,
2212 'post_status' => $post_status,
2213 's' => $query,
2214 );
2215
2216 if (!empty($query)) {
2217 self::search_posts_by_query($name, $query, $post_parent, $args);
2218 } else {
2219 remove_filter('posts_where', [HelperFunctions::class, 'posts_where_filter_callback']);
2220 }
2221
2222 $filters = self::handle_legacy_filter_to_new_filter($filters);
2223 $added_filters = array();
2224
2225 /**
2226 * Combine search and filters with AND logic
2227 *
2228 * When both search query and filters are present, we need to ensure they work together:
2229 * - Search creates meta_query with 'OR' relation (matches any custom field)
2230 * - Filters add additional conditions
2231 * - Final structure: AND(search_conditions, filter_conditions)
2232 *
2233 * This makes filters compulsory when searching, narrowing results further.
2234 */
2235 $search_meta_query = isset($args['meta_query']) ? $args['meta_query'] : null;
2236 $has_search = !empty($query) && $search_meta_query !== null;
2237 $has_filters = !empty($filters) && is_array($filters);
2238
2239 // Reset meta_query if both search and filters exist to rebuild with AND relation
2240 if ($has_search && $has_filters) {
2241 $args['meta_query'] = [
2242 'relation' => 'AND',
2243 $search_meta_query, // Search conditions (with OR relation internally)
2244 ];
2245 }
2246
2247 if (isset($filters) && is_array($filters)) {
2248 foreach ($filters as $filter_item) {
2249 if (isset($filter_item['parent']) && $filter_item['parent']) {
2250 $filter_item['id'] = 'term';
2251 }
2252 $field_name = isset($filter_item['id']) ? $filter_item['id'] : '';
2253
2254 if (!$field_name) {
2255 continue;
2256 }
2257
2258 if (self::attribute_in_post_table($filter_item['id']) && is_array($filter_item['items'])) {
2259
2260 switch ($field_name) {
2261 case 'post_excerpt':
2262 case 'post_content':
2263 case 'post_title': {
2264 $callback = self::post_table_filter_query($filter_item, 'text');
2265 if ($callback) {
2266 $added_filters[] = $callback;
2267 }
2268 break;
2269 }
2270
2271 case 'post_date':
2272 case 'post_date_gmt': {
2273 /**
2274 * $filter_item['items'] max contain one array.
2275 * in array may contain start-date, end-date
2276 * Like: [{"start-date": "2020-01-01","end-date": "2020-01-02"}]
2277 */
2278
2279 if (isset($filter_item['items'], $filter_item['items'][0])) {
2280 $items = $filter_item['items'];
2281 $item = $items[0]; // Get first item in array.
2282
2283 $date_query = array('column' => $field_name);
2284 $date_query['inclusive'] = true;
2285
2286 if (!empty($item['start-date'])) {
2287 $date_query['after'] = $item['start-date'];
2288 }
2289
2290 if (!empty($item['end-date'])) {
2291 $date_query['before'] = $item['end-date'];
2292 }
2293
2294 $args['date_query'] = $date_query;
2295 }
2296
2297 break;
2298 }
2299
2300 case 'post_author': {
2301
2302 /**
2303 * $filter_item['items'] must not contain more than 2 array of conditions.
2304 * 1 array may contain 'in' conditions and another for 'not-in' conditions
2305 * And values of 'in' and 'not-in' conditions should not collide
2306 * Like: the condition should not be author 'in' [1, 2, 3] and 'not-in' [2, 4, 5]
2307 */
2308 $items = $filter_item['items'];
2309
2310 foreach ($items as $item) {
2311 if (isset($item['condition'], $item['values']) && is_array($item['values'])) {
2312 if ($item['condition'] === 'in') {
2313 $args['author__in'] = $item['values'];
2314 }
2315
2316 if ($item['condition'] === 'not-in') {
2317 $args['author__not_in'] = $item['values'];
2318 }
2319 }
2320 }
2321
2322 break;
2323 }
2324
2325 case 'term': {
2326 $items = $filter_item['items'];
2327
2328
2329 foreach ($items as $item) {
2330 if (isset($item['condition'], $item['values']) && is_array($item['values'])) {
2331
2332 if ($item['condition'] === 'in' && !empty($item['values'])) {
2333 array_push($tax_query, [
2334 'taxonomy' => $filter_item['type'],
2335 'field' => 'term_id',
2336 'terms' => $item['values'],
2337 'operator' => 'IN',
2338 ]);
2339 }
2340
2341 if ($item['condition'] === 'not-in' && !empty($item['values'])) {
2342 array_push($tax_query, [
2343 'taxonomy' => $filter_item['type'],
2344 'field' => 'term_id',
2345 'terms' => $item['values'],
2346 'operator' => 'NOT IN',
2347 ]);
2348 }
2349 }
2350 }
2351 break;
2352 }
2353 }
2354 } else {
2355 $key = ContentManagerHelper::get_child_post_meta_key_using_field_id($post_parent, $field_name);
2356 $data_type = $filter_item['type'] ?? 'text';
2357
2358 if (!isset($args['meta_query'])) {
2359 $args['meta_query'] = array();
2360 } elseif (!is_array($args['meta_query'])) {
2361 $args['meta_query'] = array();
2362 }
2363
2364 // Ensure meta_query has proper structure when combining search + filters
2365 if ($has_search && !isset($args['meta_query']['relation'])) {
2366 $args['meta_query']['relation'] = 'AND';
2367 }
2368
2369 switch ($data_type) {
2370 default:
2371 case 'rich_text':
2372 case 'text':
2373 case 'phone':
2374 case 'url':
2375 case 'email': {
2376 $args['meta_query'][] = self::post_meta_table_filter_query($filter_item, $key, 'text');
2377 break;
2378 }
2379
2380 case 'date': {
2381 $args['meta_query'][] = self::post_meta_table_filter_query($filter_item, $key, 'date');
2382 break;
2383 }
2384
2385 case 'number': {
2386 $args['meta_query'][] = self::post_meta_table_filter_query($filter_item, $key, 'number');
2387 break;
2388 }
2389
2390 case 'option': {
2391 $args['meta_query'][] = self::post_meta_table_filter_query($filter_item, $key, 'option');
2392 break;
2393 }
2394
2395 case 'switch': {
2396 $args['meta_query'][] = self::post_meta_table_filter_query($filter_item, $key, 'switch');
2397 break;
2398 }
2399
2400 case 'taxonomy': {
2401 if (empty($args['tax_query'])) {
2402 $tax_query = array(
2403 'relation' => 'AND'
2404 );
2405 }
2406
2407 if (
2408 isset($filter_item['taxonomy'], $filter_item['terms']) &&
2409 is_array($filter_item['terms'])
2410 ) {
2411 $operators = array('NOT IN', 'IN');
2412
2413 $operator = 'IN';
2414
2415 if (isset($filter_item['operator']) && in_array($filter_item['operator'], $operators, true)) {
2416 $operator = $filter_item['operator'];
2417 }
2418
2419 array_push($tax_query, [
2420 'taxonomy' => $filter_item['taxonomy'],
2421 'field' => 'term_id', // So far this is fixed
2422 'terms' => $filter_item['terms'],
2423 'operator' => $operator,
2424 ]);
2425 }
2426 break;
2427 }
2428
2429 case 'author': {
2430 if (isset($filter_item['condition'], $filter_item['values']) && is_array($filter_item['values'])) {
2431 if ($filter_item['condition'] === 'is-equal') {
2432 $args['author__in'] = $filter_item['values'];
2433 }
2434
2435 if ($filter_item['condition'] === 'is-not-equal') {
2436 $args['author__not_in'] = $filter_item['values'];
2437 }
2438 }
2439 break;
2440 }
2441
2442 case 'multi-reference':
2443 case 'reference': {
2444 $args = self::cm_reference_table_filter_query($filter_item, $key, $args);
2445 break;
2446 }
2447 }
2448 }
2449 }
2450 }
2451
2452 if (count($IDs) > 0) {
2453 $args['post__in'] = $IDs;
2454 unset($args['post_parent']);
2455 $args['post_type'] = 'any';
2456 $inherit = false;
2457 $post_parent = false;
2458 }
2459
2460 if (count($tax_query) > 1) {
2461 $args['tax_query'] = $tax_query;
2462 }
2463
2464 if (isset($sorting)) {
2465 // Set the sort order (ASC/DESC)
2466 if (isset($sorting['order'])) {
2467 $args['order'] = $sorting['order'];
2468 }
2469
2470 // Check if the name is set and contains 'kirki_cm' && not include 'kirki_cm_post_meta'
2471 if (isset($name) && str_contains($name, KIRKI_CONTENT_MANAGER_PREFIX) && !in_array($sorting['orderby'], KIRKI_WORDPRESS_SORT_BY_OPTIONS)) {
2472 $args['orderby'] = 'meta_value'; // Use 'meta_value' or 'meta_value_num' as needed
2473 if (isset($sorting['orderby']) && !empty($sorting['orderby'])) {
2474 $args['meta_key'] = ContentManagerHelper::get_child_post_meta_key_using_field_id($post_parent, $sorting['orderby']);
2475 }
2476 } else {
2477 // For other cases, set the orderby based on the sorting parameter
2478 if (isset($sorting['orderby']) && !empty($sorting['orderby'])) {
2479 $args['orderby'] = $sorting['orderby'];
2480 }
2481 }
2482 }
2483
2484 if ($inherit || $post_parent) {
2485 //TODO: if terms page then show terms post only. like tag, category.
2486 $args['post_parent'] = $post_parent;
2487 }
2488
2489 if (!empty($context) && $inherit) {
2490 if ($context['collectionType'] == 'user') {
2491 $args['author'] = $context['id'];
2492 unset($args['post_parent']);
2493 }
2494 if ($context['collectionType'] == 'term') {
2495 $args['tax_query'] = array(
2496 array(
2497 'taxonomy' => $context['taxonomy'], // Replace 'category' with your taxonomy
2498 'field' => 'term_id', // Use 'slug' if you want to query by slug
2499 'terms' => $context['id'], // Replace 123 with your term ID
2500 )
2501 );
2502 unset($args['post_parent']);
2503 }
2504 }
2505
2506 if ($related) {
2507 $post = get_post($related_post_parent);
2508
2509 if ($post) {
2510 $args['post_type'] = $post->post_type;
2511 if (str_contains($post->post_type, 'kirki_cm_')) {
2512 // filter related posts for content manager post
2513 $referenced_post_ids = self::get_referenced_post_ids($post_parent, $post);
2514 $args['post__in'] = array_map('intval', $referenced_post_ids);
2515 } else {
2516 $args['tax_query'] = self::buildTaxonomyForRelatedPosts($post);
2517 $args['post__not_in'] = [$post->ID];
2518 }
2519
2520 }
2521 }
2522
2523 // Disable suppress_filters when post_table text filters (post_title, post_content, post_excerpt)
2524 // are present, since they rely on posts_where hooks to modify the SQL query.
2525 if (!empty($added_filters)) {
2526 $args['suppress_filters'] = false;
2527 }
2528
2529 // Run the WP_Query
2530
2531 $query = new WP_Query($args);
2532 foreach ($added_filters as $callback) {
2533 remove_filter('posts_where', $callback);
2534 }
2535
2536 $posts = $query->posts;
2537
2538
2539 $custom_logo_id = get_theme_mod('custom_logo');
2540 $image = wp_get_attachment_image_src($custom_logo_id, 'full');
2541
2542 $kirki_content_manager_post_type_fields = array();
2543
2544 if (isset($args['post_type']) && KIRKI_CONTENT_MANAGER_PREFIX === $args['post_type']) {
2545 $post_parent = $args['post_parent'];
2546 $kirki_content_manager_post_type_fields = ContentManagerHelper::get_post_type_custom_field_keys($post_parent);
2547 }
2548
2549 foreach ($posts as $key => &$post) {
2550 if (is_null($post)) {
2551 unset($posts[$key]);
2552 continue;
2553 }
2554 if (
2555 KIRKI_CONTENT_MANAGER_PREFIX === $post->post_type && is_array($kirki_content_manager_post_type_fields)
2556 ) {
2557 foreach ($kirki_content_manager_post_type_fields as $field_key) {
2558 $meta_key = ContentManagerHelper::get_child_post_meta_key_using_field_id($post->post_parent, $field_key['id']);
2559 $post->{$field_key['id']} = get_post_meta($post->ID, $meta_key, true);
2560
2561 if (
2562 isset($field_key['type']) &&
2563 $field_key['type'] === 'image' &&
2564 $post->{$field_key['id']}
2565 ) {
2566 $post->{$field_key['id']} = array(
2567 'wp_attachment_id' => $post->{$field_key['id']}['id'],
2568 'src' => $post->{$field_key['id']}['url'],
2569 );
2570 }
2571 }
2572 }
2573
2574
2575 $post->post_id = $post->ID;
2576 $post->author_profile_picture = array(
2577 'src' => get_avatar_url($post->post_author)
2578 );
2579 $post->post_author = get_the_author_meta('display_name', $post->post_author);
2580 $post->post_time = get_the_time('', $post->ID);
2581 $post->featured_image = array(
2582 'wp_attachment_id' => get_post_thumbnail_id($post->ID),
2583 'src' => get_the_post_thumbnail_url($post->ID)
2584 );
2585 $post->site_logo = isset($image[0]) ? $image[0] : '';
2586 $post->post_page_link = \get_permalink($post->ID);
2587 $post->author_posts_page_link = \get_author_posts_url($post->post_author);
2588
2589 unset($post->post_excerpt);
2590 }
2591 ;
2592
2593 // Get total posts and total pages for pagination
2594 $total_posts = $query->found_posts;
2595 $total_posts_updated = max(0, $total_posts - $offset);
2596 $total_pages = ceil($total_posts_updated / $item_per_page);
2597
2598 // Calculate previous and next page numbers
2599 $prev_page = ($current_page > 1) ? $current_page - 1 : null;
2600 $next_page = ($current_page < $total_pages) ? $current_page + 1 : null;
2601
2602 // Return the query and pagination info
2603 return array(
2604 'data' => $posts,
2605 'pagination' => array(
2606 'per_page' => $item_per_page,
2607 'current_page' => $current_page,
2608 'total_pages' => $total_pages,
2609 'total_count' => $total_posts,
2610 'previous' => $prev_page,
2611 'next' => $next_page,
2612 ),
2613 );
2614 }
2615
2616
2617 public static function search_posts_by_query($name, $query, $post_parent, &$args)
2618 {
2619 global $wpdb;
2620
2621 if (!str_contains($name, 'kirki_cm_')) {
2622 return;
2623 }
2624
2625 unset($args['s']);
2626
2627 $all_custom_fields = ContentManagerHelper::get_post_type_custom_field_keys($post_parent);
2628 $meta_query_args = ['relation' => 'OR'];
2629 $reference_where_sql = '';
2630
2631 foreach ($all_custom_fields as $data) {
2632 if (!$data)
2633 continue;
2634
2635 $meta_key = ContentManagerHelper::get_child_post_meta_key_using_field_id($post_parent, $data['id']);
2636
2637 if (in_array($data['type'], ['text'], true)) {
2638 $meta_query_args[] = [
2639 'key' => $meta_key,
2640 'value' => $query,
2641 'compare' => 'LIKE',
2642 ];
2643 }
2644
2645 if (in_array($data['type'], ['reference'], true)) {
2646 $matched_post_ids = self::get_matched_post_ids_recursive($data['ref_collection'], $query);
2647
2648 if (!empty($matched_post_ids)) {
2649 $ids = implode(',', array_map('intval', $matched_post_ids));
2650 $reference_where_sql .= " OR {$wpdb->posts}.ID IN (
2651 SELECT post_id
2652 FROM {$wpdb->prefix}kirki_cm_reference
2653 WHERE field_meta_key = '{$meta_key}'
2654 AND ref_post_id IN ($ids)
2655 )";
2656 }
2657 }
2658 }
2659
2660 if (count($meta_query_args) > 1) {
2661 $args['meta_query'] = $meta_query_args;
2662 }
2663
2664 // Store filter parameters for the callback
2665 self::$posts_where_filter_params = [
2666 'query' => $query,
2667 'reference_where_sql' => $reference_where_sql,
2668 'post_parent' => $post_parent,
2669 ];
2670
2671 add_filter('posts_where', [__CLASS__, 'posts_where_filter_callback']);
2672 }
2673
2674 public static function get_matched_post_ids_recursive($post_parent, $query, $depth = 0, $max_depth = 5)
2675 {
2676 global $wpdb;
2677
2678 if ($depth > $max_depth) {
2679 return [];
2680 }
2681
2682 $post_type = 'kirki_cm_' . $post_parent;
2683
2684 $matched_post_ids = $wpdb->get_col(
2685 $wpdb->prepare(
2686 "SELECT ID FROM {$wpdb->posts}
2687 WHERE post_title LIKE %s
2688 AND post_status = 'publish'
2689 AND post_type = %s",
2690 '%' . $wpdb->esc_like($query) . '%',
2691 $post_type
2692 )
2693 );
2694
2695 $ref_custom_fields = ContentManagerHelper::get_post_type_custom_field_keys($post_parent);
2696 $meta_conditions = [];
2697
2698 foreach ($ref_custom_fields as $ref_field) {
2699 if (!$ref_field)
2700 continue;
2701
2702 if (in_array($ref_field['type'], ['text'], true)) {
2703 $ref_meta_key = ContentManagerHelper::get_child_post_meta_key_using_field_id($post_parent, $ref_field['id']);
2704 $meta_conditions[] = $wpdb->prepare(
2705 "(meta_key = %s AND meta_value LIKE %s)",
2706 $ref_meta_key,
2707 "%{$search}%"
2708 );
2709 }
2710 }
2711
2712 if (!empty($meta_conditions)) {
2713 $where_meta = implode(' OR ', $meta_conditions);
2714 $meta_post_ids = $wpdb->get_col("SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE {$where_meta}"); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2715 $matched_post_ids = array_merge($matched_post_ids, $meta_post_ids);
2716 }
2717
2718 foreach ($ref_custom_fields as $ref_field) {
2719 if (!$ref_field || !in_array($ref_field['type'], ['reference'], true)) {
2720 continue;
2721 }
2722
2723 $ref_post_parent = $ref_field['ref_collection'];
2724 $nested_matched_ids = self::get_matched_post_ids_recursive($ref_post_parent, $query, $depth + 1, $max_depth);
2725
2726 if (!empty($nested_matched_ids)) {
2727 $meta_key = ContentManagerHelper::get_child_post_meta_key_using_field_id($post_parent, $ref_field['id']);
2728 $ids = implode(',', array_map('intval', $nested_matched_ids));
2729 $ref_post_ids = $wpdb->get_col($wpdb->prepare("SELECT post_id FROM {$wpdb->prefix}kirki_cm_reference WHERE field_meta_key = %s AND ref_post_id IN ($ids)", $meta_key)); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
2730 $matched_post_ids = array_merge($matched_post_ids, $ref_post_ids);
2731 }
2732 }
2733
2734 return array_unique(array_map('intval', $matched_post_ids));
2735 }
2736
2737 public static function get_referenced_post_ids($post_parent, $post)
2738 {
2739 global $wpdb;
2740
2741 $allData = ContentManagerHelper::get_post_type_custom_field_keys($post_parent);
2742 $post_ids = [];
2743
2744 foreach ($allData as $data) {
2745 if ($data && in_array($data['type'], ['reference', 'multi-reference'], true)) {
2746 $meta_key = 'kirki_cm_field_' . $post_parent . '_' . $data['id'];
2747
2748 $results = $wpdb->get_results(
2749 $wpdb->prepare(
2750 "SELECT ref_post_id FROM {$wpdb->prefix}kirki_cm_reference WHERE field_meta_key = %s AND post_id = %d",
2751 $meta_key,
2752 $post->ID
2753 ),
2754 ARRAY_A
2755 );
2756
2757 foreach ($results as $id) {
2758 $related_posts = $wpdb->get_results(
2759 $wpdb->prepare(
2760 "SELECT post_id FROM {$wpdb->prefix}kirki_cm_reference WHERE field_meta_key = %s AND ref_post_id = %d",
2761 $meta_key,
2762 (int) $id['ref_post_id']
2763 ),
2764 ARRAY_A
2765 );
2766
2767 foreach ($related_posts as $related) {
2768 $related_id = (int) $related['post_id'];
2769
2770 if ($related_id !== (int) $post->ID) {
2771 $post_ids[] = $related_id;
2772 }
2773 }
2774
2775
2776 }
2777 }
2778 }
2779 $post_ids = array_values(array_unique($post_ids));
2780
2781 return !empty($post_ids) ? $post_ids : [0];
2782 }
2783
2784 public static function get_terms($params)
2785 {
2786 $terms_array = [];
2787 $current_page = isset($params['current_page']) ? (int) $params['current_page'] : 1;
2788 $item_per_page = isset($params['item_per_page']) ? (int) $params['item_per_page'] : 3;
2789 $offset = isset($params['offset']) ? (int) $params['offset'] : 0;
2790
2791 if (!empty($params['inherit'])) {
2792 $terms_array = get_the_terms($params['post_parent'], $params['taxonomy']);
2793 if (is_array($terms_array)) {
2794 // Convert WP_Term objects to arrays only if needed
2795 $terms_array = array_map(function ($term) {
2796 return is_object($term) && method_exists($term, 'to_array')
2797 ? $term->to_array()
2798 : (array) $term;
2799 }, $terms_array);
2800
2801 $calculated_offset = (($current_page - 1) * $item_per_page) + $offset;
2802 $terms_array = array_slice($terms_array, $calculated_offset, $item_per_page);
2803 } else {
2804 $terms_array = [];
2805 }
2806 } else {
2807 $params['offset'] = (($current_page - 1) * $item_per_page) + $offset;
2808 $params['number'] = $item_per_page;
2809
2810 $terms_array = get_terms($params);
2811
2812 if (is_array($terms_array)) {
2813 foreach ($terms_array as &$item) {
2814 if (is_object($item) && method_exists($item, 'to_array')) {
2815 $item = $item->to_array();
2816 } elseif (is_object($item)) {
2817 $item = (array) $item;
2818 }
2819 }
2820 } else {
2821 $terms_array = [];
2822 }
2823 }
2824
2825 // Count total terms
2826 $total_terms = 0;
2827 if (!empty($params['inherit'])) {
2828 $t = get_the_terms($params['post_parent'], $params['taxonomy']);
2829 if (is_array($t)) {
2830 $total_terms = count($t);
2831 } else {
2832 $total_terms = wp_count_terms(['taxonomy' => $params['taxonomy']]);
2833 }
2834 } else {
2835 $total_terms = wp_count_terms(['taxonomy' => $params['taxonomy']]);
2836 }
2837
2838 $total_pages = ($item_per_page > 0) ? ceil($total_terms / $item_per_page) : 1;
2839 $prev_page = ($current_page > 1) ? $current_page - 1 : null;
2840 $next_page = ($current_page < $total_pages) ? $current_page + 1 : null;
2841
2842 return [
2843 'data' => $terms_array,
2844 'pagination' => [
2845 'per_page' => $item_per_page,
2846 'current_page' => $current_page,
2847 'total_pages' => $total_pages,
2848 'total_count' => $total_terms,
2849 'previous' => $prev_page,
2850 'next' => $next_page,
2851 ],
2852 ];
2853 }
2854
2855 public static function buildTaxonomyForRelatedPosts(\WP_Post $post)
2856 {
2857 $taxonomies = get_object_taxonomies($post->post_type);
2858 $taxQuery = [
2859 'relation' => 'OR',
2860 ];
2861
2862 foreach ($taxonomies as $taxonomy) {
2863 $taxQuery[] = [
2864 'taxonomy' => $taxonomy,
2865 'field' => 'slug',
2866 'terms' => array_filter(wp_get_object_terms($post->ID, $taxonomy, ['fields' => 'slugs']), function ($termSlug) {
2867 return strtolower($termSlug) !== 'uncategorized';
2868 }),
2869 ];
2870 }
2871
2872
2873 return $taxQuery;
2874 }
2875
2876
2877 /**
2878 * Get dynamic collectiond data
2879 *
2880 * @param object $params query object.
2881 *
2882 * @return array post array
2883 */
2884 public static function get_comments($params)
2885 {
2886 $parent = (int) ($params['parent'] ?? 0);
2887 $post_id = (int) ($params['post_id'] ?? 0);
2888 $type = ($params['type'] ?? 'comment');
2889 $sorting = isset($params['sorting']) ? $params['sorting'] : null;
2890 $filters = isset($params['filters']) ? $params['filters'] : null;
2891 // add new
2892 $current_page = isset($params['current_page']) ? $params['current_page'] : 1;
2893 $item_per_page = isset($params['item_per_page']) ? $params['item_per_page'] : 3;
2894 $offset = isset($params['offset']) ? $params['offset'] : 0;
2895
2896 // Calculate the offset
2897 $offset_cal = ($current_page - 1) * $item_per_page + $offset;
2898
2899 $args = array(
2900 'parent' => $parent,
2901 'post_id' => $post_id,
2902 'type' => $type,
2903 'number' => $item_per_page,
2904 'paged' => $current_page,
2905 'offset' => $offset_cal,
2906 'count' => false
2907 );
2908
2909 if (isset($filters) && is_array($filters)) {
2910 foreach ($filters as $filter_item) {
2911 $field_name = isset($filter_item['id']) ? $filter_item['id'] : '';
2912
2913 if (!$field_name) {
2914 continue;
2915 }
2916 switch ($field_name) {
2917 case 'comment_date':
2918 case 'comment_date_gmt': {
2919 /**
2920 * $filter_item['items'] max contain one array.
2921 * in array may contain start-date, end-date
2922 * Like: [{"start-date": "2020-01-01","end-date": "2020-01-02"}]
2923 */
2924 if (isset($filter_item['items'], $filter_item['items'][0])) {
2925 $items = $filter_item['items'];
2926 $item = $items[0]; // Get first item in array.
2927
2928 $date_query = array('column' => $field_name);
2929 $date_query['inclusive'] = true;
2930
2931 if (!empty($item['start-date'])) {
2932 $date_query['after'] = $item['start-date'];
2933 }
2934
2935 if (!empty($item['end-date'])) {
2936 $date_query['before'] = $item['end-date'];
2937 }
2938
2939 $args['date_query'] = $date_query;
2940 }
2941
2942 break;
2943 }
2944
2945 case 'comment_author': {
2946 $items = $filter_item['items']; // $items['items'] max contain one array.
2947
2948 foreach ($items as $item) {
2949 if (isset($item['condition'], $item['values']) && is_array($item['values'])) {
2950 if ($item['condition'] === 'in') {
2951 $args['author__in'] = $item['values'];
2952 }
2953
2954 if ($item['condition'] === 'not-in') {
2955 $args['author__not_in'] = $item['values'];
2956 }
2957 }
2958 }
2959
2960 break;
2961 }
2962
2963 case 'comment_approved': {
2964 $items = $filter_item['items']; // $items['items'] max contain one array.
2965
2966 foreach ($items as $item) {
2967 if (isset($item['condition'], $item['values']) && is_array($item['values'])) {
2968 if ($item['condition'] === 'in') {
2969 $args['status'] = $item['values'];
2970 }
2971 }
2972 }
2973 }
2974 }
2975 }
2976 }
2977
2978 if (isset($sorting)) {
2979 // Set the sort order (ASC/DESC)
2980 if (isset($sorting['order'])) {
2981 $args['order'] = $sorting['order'];
2982 }
2983
2984 if (isset($sorting['orderby']) && !empty($sorting['orderby'])) {
2985 $args['orderby'] = $sorting['orderby'];
2986 }
2987 }
2988
2989 $comments = get_comments($args);
2990 unset($args['number']);
2991 unset($args['paged']);
2992
2993 if (is_array($comments)) {
2994 foreach ($comments as &$comment) {
2995 $comment = (object) (array) $comment;
2996
2997 $author_posts_page_link = $comment->comment_author_url;
2998
2999 if (!$author_posts_page_link) {
3000 $author_posts_page_link = \get_author_posts_url($comment->user_id);
3001 }
3002
3003 $comment->author_profile_picture = array(
3004 'src' => get_avatar_url($comment->user_id)
3005 );
3006 $comment->author_posts_page_link = $author_posts_page_link;
3007 }
3008 }
3009
3010 // Get total comments count
3011 $total_comments = get_comments(array_merge($args, array('count' => true)));
3012 $total_comments = $total_comments - $offset;
3013
3014 // Calculate total pages
3015 $total_pages = ceil($total_comments / $item_per_page);
3016
3017 // Calculate previous and next pages
3018 $prev_page = ($current_page > 1) ? $current_page - 1 : null;
3019 $next_page = ($current_page < $total_pages) ? $current_page + 1 : null;
3020
3021 // return $comments;
3022 return array(
3023 'data' => $comments, // Raw comments data
3024 'pagination' => array(
3025 'per_page' => $item_per_page,
3026 'current_page' => $current_page,
3027 'total_pages' => $total_pages,
3028 'total_count' => $total_comments,
3029 'previous' => $prev_page,
3030 'next' => $next_page,
3031 ),
3032 );
3033 }
3034
3035
3036 /**
3037 * Remove all default assets
3038 *
3039 * @return void
3040 */
3041 public static function remove_wp_assets()
3042 {
3043 /*
3044 // Remove all WordPress actions
3045 // remove_all_actions('wp_head');
3046 // remove_all_actions('wp_print_styles');
3047 // remove_all_actions('wp_print_head_scripts');
3048 // remove_all_actions('wp_footer');
3049
3050 // // Handle `wp_head`
3051 // add_action('wp_head', 'wp_enqueue_scripts', 1);
3052 // add_action('wp_head', 'wp_print_styles', 8);
3053 // add_action('wp_head', 'wp_print_head_scripts', 9);
3054 // add_action('wp_head', 'wp_site_icon');
3055
3056 // // Handle `wp_footer`
3057 // add_action('wp_footer', 'wp_print_footer_scripts', 20);
3058
3059 // // Handle `wp_enqueue_scripts`
3060 // remove_all_actions('wp_enqueue_scripts');
3061
3062 // // Also remove all scripts hooked into after_wp_tiny_mce.
3063 // remove_all_actions('after_wp_tiny_mce');
3064 */
3065 // remove admin-bar.
3066 add_filter('show_admin_bar', '__return_false', PHP_INT_MAX);
3067 }
3068
3069 /**
3070 * Get server protocol
3071 * currently not in use
3072 *
3073 * @return string protocol name.
3074 */
3075 public static function get_protocol()
3076 {
3077 $protocol = isset($_SERVER['HTTPS']) ? 'https' : 'http';
3078 return $protocol;
3079 }
3080
3081 /**
3082 * Check if the current user has specific role ($role)
3083 *
3084 * @param string $role The role to check.
3085 * @return boolean
3086 */
3087 public static function user_is($role)
3088 {
3089 $user = wp_get_current_user();
3090 $roles = $user->roles;
3091
3092 return is_array($roles) && count($roles) && in_array($role, $roles, true) ? true : false;
3093 }
3094
3095 /**
3096 * Check if the user has access to edit/create specific/all post
3097 *
3098 * @param int $post_id post id.
3099 * @return boolean
3100 *
3101 * @deprecated
3102 * @see Kirki\App\Wordpress\User::has_edit_access()
3103 */
3104 public static function user_has_post_edit_access()
3105 {
3106 return self::has_access(
3107 array(
3108 KIRKI_ACCESS_LEVELS['FULL_ACCESS'],
3109 KIRKI_ACCESS_LEVELS['CONTENT_ACCESS'],
3110 )
3111 );
3112 }
3113
3114 /**
3115 * Check if the user has access to editor
3116 *
3117 * @return boolean
3118 */
3119 public static function user_has_editor_access()
3120 {
3121 if (isset($_GET['editor-preview-token'])) {
3122 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3123 $editor_preview_token = self::sanitize_text(isset($_GET['editor-preview-token']) ? $_GET['editor-preview-token'] : '');
3124 return self::is_post_editor_preview_token_valid($editor_preview_token);
3125 }
3126 return self::has_access(
3127 array(
3128 KIRKI_ACCESS_LEVELS['FULL_ACCESS'],
3129 KIRKI_ACCESS_LEVELS['CONTENT_ACCESS'],
3130 KIRKI_ACCESS_LEVELS['VIEW_ACCESS'],
3131 )
3132 );
3133 }
3134
3135 public static function getallheaders()
3136 {
3137 $headers = [];
3138
3139 foreach ($_SERVER as $name => $value) {
3140 if (strpos($name, 'HTTP_') === 0) {
3141 $key = substr($name, 5);
3142 } elseif (in_array($name, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'])) {
3143 $key = $name;
3144 } else {
3145 continue;
3146 }
3147
3148 // Convert HEADER_NAME → Header-Name
3149 $key = str_replace('_', ' ', strtolower($key));
3150 $key = ucwords($key);
3151 $key = str_replace(' ', '-', $key);
3152
3153 $headers[$key] = $value;
3154 }
3155
3156 return $headers;
3157 }
3158
3159 /**
3160 * Check if the request is from the editor preview.
3161 *
3162 * @deprecated Use Kirki\App\Supports\EditorPreview::has_valid_token
3163 * @see \Kirki\App\Supports\EditorPreview::has_valid_token()
3164 * @return bool
3165 */
3166 public static function is_api_call_from_editor_preview()
3167 {
3168 // Check the Editor-Preview-Token header
3169 $headers = self::getallheaders();
3170 $editor_preview_token = isset($headers['Editor-Preview-Token']) ? $headers['Editor-Preview-Token'] : null;
3171 if ($editor_preview_token && HelperFunctions::is_post_editor_preview_token_valid($editor_preview_token)) {
3172 return true;
3173 }
3174 return false;
3175 }
3176
3177 /**
3178 * Check if the Editor-Preview-Token header is valid
3179 *
3180 * @deprecated Use Kirki\App\Supports\EditorPreview::has_valid_token
3181 * @see Kirki\App\Supports\EditorPreview::has_valid_token()
3182 * @return bool
3183 */
3184 public static function is_api_header_post_editor_preview_token_valid()
3185 {
3186 // Check the Editor-Preview-Token header
3187 $headers = self::getallheaders();
3188 $editor_preview_token = isset($headers['Editor-Preview-Token']) ? $headers['Editor-Preview-Token'] : null;
3189 if (HelperFunctions::is_post_editor_preview_token_valid($editor_preview_token)) {
3190 return true;
3191 }
3192 return false;
3193 }
3194
3195
3196 /**
3197 * Check if the Editor-Preview-Token header is valid
3198 *
3199 * @deprecated Use Kirki\App\Supports\EditorPreview::has_valid_token
3200 * @see Kirki\App\Supports\EditorPreview::has_valid_token()
3201 * @return bool
3202 */
3203 public static function is_post_editor_preview_token_valid($token)
3204 {
3205 $status = HelperFunctions::get_global_data_using_key('kirki_editor_read_only_access_status');
3206 if ($status) {
3207 $kirki_editor_read_only_access_token = HelperFunctions::get_global_data_using_key('kirki_editor_read_only_access_token');
3208 if ($kirki_editor_read_only_access_token && $kirki_editor_read_only_access_token === $token) {
3209 return true;
3210 }
3211 }
3212 return false;
3213 }
3214
3215 /**
3216 * Check if the current user has specific access
3217 *
3218 * @deprecated Use Kirki\App\Wordpress\User::has_access
3219 * @see Kirki\App\Wordpress\User
3220 * @param string|string[] $access_level The access level to check access.
3221 *
3222 */
3223 public static function has_access($access_level)
3224 {
3225 if (!function_exists('wp_get_current_user')) {
3226 return false;
3227 }
3228
3229 $user = wp_get_current_user();
3230 $roles = $user->roles;
3231 $has_access = false;
3232
3233 if (is_array($access_level)) {
3234 foreach ($roles as $role) {
3235 $access = get_option('kirki_' . $role);
3236 if (!empty($access) && in_array($access, $access_level, true)) {
3237 $has_access = true;
3238 break;
3239 }
3240 }
3241 } elseif (is_string($access_level)) {
3242 foreach ($roles as $role) {
3243 $access = get_option('kirki_' . $role);
3244 if (!empty($access) && $access === $access_level) {
3245 $has_access = true;
3246 break;
3247 }
3248 }
3249 }
3250
3251 return $has_access;
3252 }
3253
3254 /**
3255 * This method will collect license info from kirki.com
3256 *
3257 * @param string $license_key user license key.
3258 * @return array license info.
3259 */
3260 public static function get_my_license_info($license_key)
3261 {
3262 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3263 $info = self::http_get(KIRKI_CORE_PLUGIN_URL . '/?license_key=' . $license_key . '&host=' . rawurlencode(self::sanitize_text(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null)));
3264 $info = json_decode($info, true);
3265 if ($info && isset($info['success'])) {
3266 return $info['data'];
3267 } else {
3268 return array('key' => $license_key);
3269 }
3270 }
3271
3272 /**
3273 * HTTP get
3274 *
3275 * @param string $url api endpoint url.
3276 * @return string|bool response.
3277 *
3278 * @deprecated
3279 * @see \Kirki\Framework\Supports\Facades\Http::get()
3280 */
3281 public static function http_get($url, $args = array())
3282 {
3283 try {
3284 $response = wp_remote_get($url, $args);
3285
3286 if ((!is_wp_error($response)) && (200 === wp_remote_retrieve_response_code($response))) {
3287 $responseBody = $response['body'];
3288
3289 return $responseBody;
3290 }
3291
3292 return false;
3293 } catch (\Exception $ex) {
3294 return false;
3295 }
3296 }
3297
3298 /**
3299 * HTTP post
3300 *
3301 * @param string $url api endpoint url.
3302 * @param array $options options.
3303 * @return array|WP_Error response.
3304 *
3305 * @deprecated
3306 * @see \Kirki\Framework\Supports\Facades\Http::post()
3307 */
3308 public static function http_post($url, $options)
3309 {
3310 $res = wp_remote_post($url, $options);
3311 return $res;
3312 }
3313
3314 /**
3315 * Text domain load hooks
3316 *
3317 * @param string $handle kirki handle.
3318 * @return void
3319 */
3320 public static function load_script_text_domain($handle)
3321 {
3322 wp_set_script_translations($handle, 'kirki', KIRKI_PLUGIN_PATH . 'languages/');
3323 }
3324
3325 /**
3326 * Delete kirki related meta if a post is deleted.
3327 *
3328 * @param int $post_id post id.
3329 * @return void
3330 */
3331 public static function delete_post_with_meta_key($post_id)
3332 {
3333 delete_post_meta($post_id, KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS);
3334 delete_post_meta($post_id, KIRKI_META_NAME_FOR_USED_STYLE_BLOCK_IDS . '_random');
3335 delete_post_meta($post_id, 'kirki');
3336 delete_post_meta($post_id, KIRKI_META_NAME_FOR_POST_EDITOR_MODE);
3337 delete_post_meta($post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY);
3338 delete_post_meta($post_id, KIRKI_GLOBAL_STYLE_BLOCK_META_KEY . '_random');
3339 delete_post_meta($post_id, KIRKI_META_NAME_FOR_USED_FONT_LIST);
3340 }
3341 /**
3342 * Get the query string for the media type
3343 *
3344 * @param string $type media type.
3345 * @return string The query string.
3346 * @example HelperFunctions::get_media_type_query_string('image') => 'image/jpeg, image/png, image/gif'
3347 */
3348 public static function get_media_type_query_string($type)
3349 {
3350 return implode(
3351 ', ',
3352 array_map(
3353 function ($v) {
3354 return "'" . $v . "'";
3355 },
3356 KIRKI_SUPPORTED_MEDIA_TYPES[$type]
3357 )
3358 );
3359 }
3360
3361 /**
3362 * This is for component configuration/object javascript variable.
3363 *
3364 * @return string script tag
3365 */
3366 public static function get_empty_variables()
3367 {
3368 $s = "<script id='kirki-elements-property-empty-vars'>";
3369 $s .= 'var ' . 'kirkiSliders = [], ' . 'kirkiMaps = [], ' . 'kirkiLotties = [], ' . 'kirkiPopups = [], ' . 'kirkiLightboxes = [], ' . 'kirkiReCaptchas = [], ' . 'kirkiVideos = [], ' . 'kirkiTabs = [], ' . 'kirkiInteractions = [], ' . 'kirkiCollections = [], ' . 'kirkiDropdown = [], ' . 'kirkiForms = [];';
3370 $s .= '</script>';
3371 return $s;
3372 }
3373
3374 /**
3375 * Check kirki and kirki pro is active or not
3376 *
3377 * @param string $plugin_main_file plugin main PHP file name.
3378 * @return boolean
3379 */
3380 public static function is_plugin_activate($plugin_main_file)
3381 {
3382 if (in_array($plugin_main_file, apply_filters('active_plugins', get_option('active_plugins')), true)) {
3383 // plugin is activated.
3384 return true;
3385 }
3386 return false;
3387 }
3388
3389 /**
3390 * This function will verify nonce
3391 * ACT like API calls auth middleware
3392 *
3393 * @param string $action ajax action name.
3394 *
3395 * @return void
3396 */
3397 public static function verify_nonce($action = -1)
3398 {
3399 //phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
3400 $headers = static::getallheaders();
3401 $header_nonce = isset($headers['X-Wp-Nonce']) ? static::sanitize_text($headers['X-Wp-Nonce']) : '';
3402 $nonce = $header_nonce ? $header_nonce : static::sanitize_text(isset($_GET['_wpnonce']) ? $_GET['_wpnonce'] : '');
3403
3404 if (!wp_verify_nonce($nonce, $action)) {
3405 wp_send_json_error('Not authorized');
3406 exit;
3407 }
3408 }
3409
3410 /**
3411 * Unslash and sanitize text
3412 *
3413 * @param string $v text.
3414 * @return string sanitized text.
3415 */
3416 public static function sanitize_text($v)
3417 {
3418 return sanitize_text_field(wp_unslash($v));
3419 }
3420
3421 /**
3422 * Get current WordPress session ID.
3423 * This method generates a unique session ID if none exists.
3424 *
3425 * @deprecated
3426 * @see \Kirki\App\Supports\Session::get_session_id()
3427 *
3428 * @return string Session ID.
3429 */
3430 public static function get_session_id()
3431 {
3432
3433 // First, check if a session ID is already stored in the static variable.
3434 if (self::$global_session_id) {
3435 return self::$global_session_id;
3436 }
3437
3438 // Check if a session ID exists in a cookie.
3439 if (isset($_COOKIE['kirki_session_id'])) {
3440 self::$global_session_id = sanitize_text_field($_COOKIE['kirki_session_id']);
3441 return self::$global_session_id;
3442 }
3443
3444 // Generate a new session ID.
3445 self::$global_session_id = wp_generate_uuid4();
3446 // Set the session ID in a cookie.
3447 setcookie('kirki_session_id', self::$global_session_id, time() + (DAY_IN_SECONDS * 7), COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
3448
3449 return self::$global_session_id;
3450 }
3451
3452 /**
3453 * Get session data by key using WordPress transients.
3454 *
3455 * @deprecated
3456 * @see \Kirki\App\Supports\Session::get()
3457 *
3458 * @param string $key The key of the session data to retrieve.
3459 * @return mixed|null The session data if found, null otherwise.
3460 */
3461 public static function get_session_data($key)
3462 {
3463 // Get the current session ID.
3464 $session_id = self::get_session_id();
3465
3466 // Retrieve the session data.
3467 $session_data = get_transient('kirki_session_' . $session_id);
3468
3469 if (isset($session_data[$key])) {
3470 return $session_data[$key];
3471 }
3472
3473 return null;
3474 }
3475
3476 /**
3477 * Add or update session data using WordPress transients.
3478 *
3479 * @deprecated
3480 * @see \Kirki\App\Supports\Session::put()
3481 *
3482 * @param string $key The key of the session data.
3483 * @param mixed $value The value of the session data.
3484 * @return void
3485 */
3486 public static function set_session_data($key, $value)
3487 {
3488 // Get the current session ID.
3489 $session_id = self::get_session_id();
3490
3491 // Retrieve existing session data.
3492 $session_data = get_transient('kirki_session_' . $session_id) ?: array();
3493
3494 // Update the session data.
3495 $session_data[$key] = $value;
3496
3497 // Save the updated session data with a 24-hour expiration time.
3498 set_transient('kirki_session_' . $session_id, $session_data, DAY_IN_SECONDS);
3499 }
3500
3501 /**
3502 * Delete session data by key using WordPress transients.
3503 *
3504 * @deprecated
3505 * @see \Kirki\App\Supports\Session::forget()
3506 *
3507 * @param string $key The key of the session data to delete.
3508 * @return void
3509 */
3510 public static function delete_session_data($key)
3511 {
3512 // Get the current session ID.
3513 $session_id = self::get_session_id();
3514
3515 // Retrieve existing session data.
3516 $session_data = get_transient('kirki_session_' . $session_id);
3517
3518 if (isset($session_data[$key])) {
3519 unset($session_data[$key]);
3520
3521 // Save the updated session data or delete the transient if empty.
3522 if (!empty($session_data)) {
3523 set_transient('kirki_session_' . $session_id, $session_data, DAY_IN_SECONDS);
3524 } else {
3525 delete_transient('kirki_session_' . $session_id);
3526 }
3527 }
3528 }
3529
3530
3531 /**
3532 * Is Pro user checking function.
3533 *
3534 * @return bool
3535 */
3536 public static function is_pro_user()
3537 {
3538 $common_data = WpAdmin::get_common_data(true);
3539
3540 $bool = isset($common_data['license_key']['valid']) && boolval($common_data['license_key']['valid']) === true;
3541
3542 return $bool;
3543 }
3544
3545
3546 /**
3547 * Get all view port lists
3548 *
3549 * @return string viewports list variable in script markup.
3550 */
3551 public static function get_view_port_lists()
3552 {
3553 $s = '';
3554 $list = UserData::get_view_port_list();
3555 if ($list) {
3556 $s .= "<script id='kirki-viewport-lists'>";
3557 $s .= 'var ' . 'kirkiViewports = ' . wp_json_encode($list) . ';';
3558 $s .= '</script>';
3559 }
3560 return $s;
3561 }
3562
3563 /**
3564 * Get all css variables
3565 *
3566 * @return string variables in script markup.
3567 */
3568 public static function get_kirki_css_variables_data()
3569 {
3570 $s = '';
3571 $variableData = UserData::get_kirki_variable_data();
3572 if ($variableData) {
3573 $s .= "<script id='kirki-variable-lists'>";
3574 $s .= 'var ' . 'kirkiCSSVariable = ' . wp_json_encode($variableData) . ';';
3575 $s .= '</script>';
3576 }
3577 return $s;
3578 }
3579
3580 /**
3581 * Get smooth scroll script
3582 *
3583 * @return string script markup.
3584 */
3585 public static function get_smooth_scroll_script()
3586 {
3587 $common_data = WpAdmin::get_common_data(true);
3588 $smooth_scroll_enabled = isset($common_data['smooth_scroll'], $common_data['smooth_scroll']['enabled']) ? $common_data['smooth_scroll']['enabled'] : false;
3589
3590 $smooth_scroll_value = isset($common_data['smooth_scroll'], $common_data['smooth_scroll']['value']) ? $common_data['smooth_scroll']['value'] : 1;
3591
3592 /**
3593 * User value 1 to 200
3594 *
3595 * Min duration 1s
3596 * Max duration 12s
3597 *
3598 */
3599 $duration = ceil(($smooth_scroll_value / 200) * 12);
3600
3601 $s = '';
3602
3603 if ($smooth_scroll_enabled) {
3604 $s .= "<script id='kirki-smooth-scroll'>";
3605 $s .= "
3606 window.document.addEventListener('DOMContentLoaded', function () {
3607 if (typeof KirkiSmoothScroll !== 'undefined') {
3608 const params = {
3609 autoRaf: true,
3610 anchors: true,
3611 allowNestedScroll: true,
3612 duration: $duration,
3613 };
3614
3615 const kirkiSmoothScroll = new KirkiSmoothScroll(params);
3616
3617 kirkiSmoothScroll.on('scroll');
3618 }
3619 });
3620 ";
3621 $s .= '</script>';
3622 }
3623
3624 return $s;
3625 }
3626
3627 /**
3628 * Format the date with date format
3629 *
3630 * @return string
3631 */
3632 public static function format_date($date, $format)
3633 {
3634 if ($date && $format) {
3635 $date_formats_arr = [
3636 'DD/MM/YYYY' => 'd/m/Y',
3637 'DD-MM-YYYY' => 'd-m-Y',
3638 'DD.MM.YYYY' => 'd.m.Y',
3639 'MM/DD/YYYY' => 'm/d/Y',
3640 'MM-DD-YYYY' => 'm-d-Y',
3641 'MM.DD.YYYY' => 'm.d.Y',
3642 'MMMM DD, YYYY' => 'F j, Y',
3643 'MMM DD, YYYY' => 'M j, Y',
3644 'YYYY-MM-DD' => 'Y-m-d',
3645 'YYYY/MM/DD' => 'Y/m/d',
3646 'YY.MM.DD' => 'y.m.d',
3647 'YY/MM/DD' => 'y/m/d',
3648 'YY-MM-DD' => 'y-m-d',
3649 ];
3650
3651 $timestamp = strtotime($date);
3652 if ($timestamp === false) {
3653 return $date; // fallback (avoid fatal)
3654 }
3655
3656 $datetime = (new \DateTime())->setTimestamp($timestamp);
3657 return $datetime->format($date_formats_arr[$format] ?? $format);
3658 }
3659
3660 return $date;
3661 }
3662
3663 /**
3664 * Format the time with time format
3665 *
3666 * @return string
3667 */
3668 public static function convert_time_format($timeString, $format = 'h:i a')
3669 {
3670 $dateTime = null;
3671
3672 try {
3673 $dateTime = new \DateTime($timeString);
3674 if ($dateTime && $timeString) {
3675 return $dateTime->format($format);
3676 }
3677 } catch (\Exception $e) {
3678 // if timeString is an invalid time format
3679 $parts = explode(' ', $timeString);
3680 if (count($parts) > 1) {
3681 // Remove the last part (am/pm)
3682 $time_value = $parts[0];
3683 try {
3684 $dateTime = new \DateTime($time_value);
3685 if ($dateTime && $timeString) {
3686 return $dateTime->format($format);
3687 }
3688 } catch (\Exception $e2) {
3689 return $timeString;
3690 }
3691 }
3692 return $timeString;
3693 }
3694
3695 return $timeString;
3696 }
3697
3698 /**
3699 * Get single post if has a kirki type post
3700 *
3701 * @return object|bool
3702 */
3703 public static function get_last_edited_kirki_editor_type_page()
3704 {
3705 $args = array(
3706 'post_type' => 'page', // Change to 'post' if you want to search for posts
3707 'post_status' => ['publish', 'draft'],
3708 'numberposts' => 1, // Number of results to retrieve (change as needed)
3709 'meta_key' => 'kirki_editor_mode',
3710 'meta_value' => 'kirki',
3711 'orderby' => 'modified', // Order by post date
3712 'order' => 'DESC', // Sort in descending order
3713 );
3714
3715 $pages = get_posts($args);
3716 if (count($pages) > 0) {
3717 return $pages[0];
3718 }
3719 return false;
3720 }
3721
3722 public static function get_kirki_version_from_db()
3723 {
3724 $version = wp_cache_get('kirki_version', 'kirki');
3725
3726 if (false === $version) {
3727 $version = get_option('kirki_version', '');
3728
3729 if (!empty($version)) {
3730 wp_cache_set('kirki_version', $version, 'kirki');
3731 }
3732 }
3733
3734 return $version;
3735 }
3736
3737 public static function set_kirki_version_in_db()
3738 {
3739 $version = self::get_kirki_version_from_db();
3740
3741 if ($version && version_compare($version, KIRKI_VERSION, '==')) {
3742 // No need to update the version if it's already equal to the current version.
3743 return;
3744 }
3745
3746 update_option('kirki_version', KIRKI_VERSION, false);
3747 wp_cache_set('kirki_version', KIRKI_VERSION, 'kirki');
3748 }
3749
3750 public static function accepted_file_types_by_plugin($accepted_media_types = KIRKI_SUPPORTED_MEDIA_TYPES)
3751 {
3752 $result = array();
3753
3754 foreach ($accepted_media_types as $value) {
3755 if (is_array($value)) {
3756 $result = array_merge($result, self::accepted_file_types_by_plugin($value));
3757 } else {
3758 $result[] = $value;
3759 }
3760 }
3761
3762 return $result;
3763 }
3764
3765 public static function content_manager_link_filter($dynamic_content = array(), $href = "#")
3766 {
3767 $current_post = get_post(self::get_post_id_if_possible_from_url());
3768
3769 if ($current_post->post_type === KIRKI_CONTENT_MANAGER_PREFIX) {
3770 $fields = ContentManagerHelper::get_post_type_custom_field_keys($current_post->post_parent);
3771
3772 if (isset($fields[$dynamic_content['value']]) && is_array($fields[$dynamic_content['value']])) {
3773 if ('email' === $fields[$dynamic_content['value']]['type']) {
3774 $href = "mailto:$href";
3775 } else if ('phone' === $fields[$dynamic_content['value']]['type']) {
3776 $href = "tel:$href";
3777 }
3778 }
3779 }
3780
3781 return $href;
3782 }
3783
3784 public static function check_string_has_this_tags($string, $tag)
3785 {
3786 // Check if the string contains either a <p> tag or an <h1> tag
3787 return preg_match("/<" . $tag . "[^>]*>/i", $string) === 1;
3788 }
3789
3790 /**
3791 * @deprecated
3792 * @see Kirki\App\Managers\GlobalDataManager::get_post_id()
3793 */
3794 private static function get_global_data_post_id()
3795 {
3796 $post_id = get_option('KIRKI_GLOBAL_DATA_POST_TYPE_ID', get_option('DROIP_GLOBAL_DATA_POST_TYPE_ID', false));
3797 if ($post_id) {
3798 return $post_id;
3799 } else {
3800 //this block will run only once
3801 $posts = get_posts(array(
3802 'post_type' => KIRKI_GLOBAL_DATA_POST_TYPE_NAME,
3803 'numberposts' => 1,
3804 ));
3805 if ($posts) {
3806 $post_id = $posts[0]->ID;
3807 } else {
3808 //create new post
3809 $post = array(
3810 'post_title' => KIRKI_GLOBAL_DATA_POST_TYPE_NAME,
3811 'post_type' => KIRKI_GLOBAL_DATA_POST_TYPE_NAME,
3812 'post_status' => 'draft'
3813 );
3814 $post_id = wp_insert_post($post);
3815 }
3816 update_option('KIRKI_GLOBAL_DATA_POST_TYPE_ID', $post_id, true);
3817 }
3818
3819 return $post_id;
3820 }
3821
3822 /**
3823 * Get global data using key
3824 *
3825 * @deprecated
3826 * @see Kirki\App\Managers\GlobalDataManager::get()
3827 */
3828 public static function get_global_data_using_key($key)
3829 {
3830 //first get post using KIRKI_GLOBAL_DATA_POST_TYPE_NAME post_type name. if not found then create new one.
3831 $post_id = self::get_global_data_post_id();
3832 if (metadata_exists('post', $post_id, $key)) {
3833 return get_post_meta($post_id, $key, true);
3834 }
3835
3836 // this block will run only once for a legacy option key.
3837 $value = get_option($key, null);
3838 if (null !== $value) {
3839 update_post_meta($post_id, $key, $value);
3840 delete_option($key);
3841 }
3842
3843 return $value;
3844 }
3845
3846 /**
3847 * Get global data using key
3848 *
3849 * @deprecated
3850 * @see Kirki\App\Managers\GlobalDataManager::update()
3851 */
3852 public static function update_global_data_using_key($key, $value)
3853 {
3854 $post_id = self::get_global_data_post_id();
3855 update_post_meta($post_id, $key, $value);
3856
3857 }
3858
3859 public static function get_template_data_if_current_page_is_kirki_template()
3860 {
3861 $custom_data = get_query_var('kirki_custom_data');
3862 $data = false;
3863 $builder_div = '';
3864 if ($custom_data && isset($custom_data['kirki_template_content'])) {
3865 $action = HelperFunctions::sanitize_text(isset($_GET['action']) ? $_GET['action'] : null);
3866 $load_for = HelperFunctions::sanitize_text(isset($_GET['load_for']) ? $_GET['load_for'] : '');
3867
3868 if ($action === KIRKI_EDITOR_ACTION && $load_for === 'kirki-iframe' && !str_contains($custom_data['kirki_template_content'], 'kirki-builder')) {
3869 $template_edit_url = HelperFunctions::get_post_url_arr_from_post_id($custom_data['kirki_template_id'], ['editor_url' => true])['editor_url'];
3870 $builder_div = '<div id="' . 'kirki-builder' . '" template-error="' . $template_edit_url . '"></div>';
3871 }
3872
3873 $data = array(
3874 'content' => $custom_data['kirki_template_content'] . $builder_div,
3875 'template_id' => $custom_data['kirki_template_id']
3876 );
3877 }
3878 return $data;
3879 }
3880
3881 public static function get_custom_data_if_current_page_is_kirki_custom_post()
3882 {
3883 $custom_data = get_query_var('kirki_custom_data');
3884 $data = false;
3885 $builder_div = '';
3886 if ($custom_data && isset($custom_data['kirki_custom_post_content'])) {
3887 $action = HelperFunctions::sanitize_text(isset($_GET['action']) ? $_GET['action'] : null);
3888 $load_for = HelperFunctions::sanitize_text(isset($_GET['load_for']) ? $_GET['load_for'] : '');
3889
3890 if ($action === KIRKI_EDITOR_ACTION && $load_for === 'kirki-iframe' && !str_contains($custom_data['kirki_custom_post_content'], 'kirki-builder')) {
3891 $template_edit_url = HelperFunctions::get_post_url_arr_from_post_id($custom_data['kirki_custom_post_id'], ['editor_url' => true])['editor_url'];
3892 $builder_div = '<div id="' . 'kirki-builder' . '" template-error="' . $template_edit_url . '"></div>';
3893 }
3894
3895 $data = array(
3896 'content' => $custom_data['kirki_custom_post_content'] . $builder_div,
3897 'post_id' => $custom_data['kirki_custom_post_id']
3898 );
3899 }
3900 return $data;
3901 }
3902
3903 /**
3904 * Validate slug for a post.
3905 *
3906 * @param int|null $post_id
3907 * @param string $post_type
3908 * @param string $post_name
3909 * @return bool
3910 *
3911 * @deprecated Use Kirki\App\Supports\ContentManager::validate_slug() instead.
3912 * @see Kirki\App\Supports\ContentManager
3913 */
3914 public static function validate_slug($post_id, $post_type, $post_name)
3915 {
3916 global $wpdb;
3917 // Execute the query
3918 $result = $wpdb->get_var($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d", $post_name, $post_type, $post_id ? $post_id : 0));
3919
3920 // If a post with the same slug exists, return false
3921 if ($result) {
3922 return false;
3923 }
3924
3925 // If no post with the same slug exists, return true
3926 return true;
3927 }
3928
3929 /**
3930 * Summary of find_utility_page_for_this_context
3931 * @param mixed $type
3932 * @param mixed $get_by id | type.
3933 * @return mixed
3934 */
3935 public static function find_utility_page_for_this_context($value = '404', $get_by = 'type')
3936 {
3937 $utility_pages = Page::fetch_list('kirki_utility', true, array('publish'));
3938 if (count($utility_pages) > 0) {
3939 foreach ($utility_pages as $key => $page) {
3940 if ($get_by === 'type') {
3941 if ($page['utility_page_type'] === $value) {
3942 return $page;
3943 }
3944 } else if ($get_by === 'id') {
3945 if ($page['id'] === (int) $value) {
3946 return $page;
3947 }
3948 }
3949 }
3950 }
3951 return false;
3952 }
3953 public static function get_current_page_context()
3954 {
3955 $context = array(); // {id, type}
3956
3957 $obj = get_queried_object();
3958
3959 if (is_404()) {
3960 $context['type'] = '404';
3961 } else if ($obj instanceof WP_Post) {
3962 $context['id'] = $obj->ID;
3963 $context['type'] = 'post';
3964 } else if ($obj instanceof WP_User) {
3965 $context['id'] = $obj->ID;
3966 $context['type'] = 'user';
3967 } elseif ($obj instanceof WP_Term) {
3968 $context['id'] = $obj->term_id;
3969 $context['type'] = 'term';
3970 }
3971 // elseif ($obj instanceof WP_Post_Type) {
3972 // echo 'This is a WP_Post_Type object';
3973 // } elseif (is_null($obj)) {
3974 // echo 'No queried object (null)';
3975 // }
3976 else {
3977 $kirki_utility_page_type = get_query_var('kirki_utility_page_type');
3978 $kirki_utility_page_id = get_query_var('kirki_utility_page_id');
3979 if (!empty($kirki_utility_page_type)) {
3980 if (!self::check_utility_page_visibility_condition($kirki_utility_page_type)) {
3981 $context['type'] = '404';
3982 } else {
3983 $context['type'] = 'kirki_utility';
3984 $context['kirki_utility_page_type'] = $kirki_utility_page_type;
3985 $context['kirki_utility_page_id'] = $kirki_utility_page_id;
3986 }
3987 }
3988 }
3989 return $context;
3990 }
3991
3992
3993 /**
3994 * Get utility page slug using type.
3995 * { type: 'login', title: 'Login' },
3996 * { type: 'sign_up', title: 'Registration' },
3997 * { type: 'forgot_password', title: 'Forgot Password' },
3998 * { type: 'reset_password', title: 'Reset Password' },
3999 * { type: 'retrive_username', title: 'Retrive Username' },
4000 * { type: '404', title: '404' },
4001 *
4002 * @param string $type //utility page type.
4003 * @return string||bool //string or false.
4004 */
4005 public static function get_utility_page_url($type)
4006 {
4007 $utility_pages = Page::fetch_list('kirki_utility', true, array('publish'));
4008 foreach ($utility_pages as $key => $page) {
4009 $utility_page_type = $page['utility_page_type'];
4010
4011 $slug = $page['slug'];
4012 if ($utility_page_type === $type) {
4013 return home_url('/' . $slug);
4014 }
4015 }
4016 return false;
4017 }
4018 public static function check_utility_page_visibility_condition($type)
4019 {
4020 if ($type === 'login' || $type === 'sign_up' || $type === 'forgot_password' || $type === 'reset_password' || $type === 'retrive_username') {
4021 // Check if the user is already logged in
4022 if (is_user_logged_in()) {
4023 return false; // User is logged in, so the page should not be visible
4024 }
4025 // Add other conditions based on the type if needed
4026 if ($type === 'signup') {
4027 // Example: Check if registrations are enabled in WordPress
4028 if (!get_option('users_can_register')) {
4029 return false; // Registration is disabled
4030 }
4031 }
4032 // If the user is not logged in and other conditions pass, allow the page to be visible
4033 return true;
4034 }
4035 // If the type is not 'login' or 'signup', return false by default
4036 return true;
4037 }
4038
4039 /**
4040 * @deprecated
4041 * @see \Kirki\Framework\Supports\Facades\File::delete()
4042 */
4043 public static function delete_directory($dirname)
4044 {
4045 global $wp_filesystem;
4046 if (empty($wp_filesystem)) {
4047 require_once ABSPATH . 'wp-admin/includes/file.php';
4048 WP_Filesystem();
4049 }
4050
4051 if ($wp_filesystem->exists($dirname)) {
4052 return $wp_filesystem->delete($dirname, true);
4053 }
4054
4055 return false;
4056 }
4057
4058 /**
4059 * @deprecated
4060 * @see \Kirki\App\Supports\FileHandler::get_temp_folder_path()
4061 */
4062 public static function get_temp_folder_path()
4063 {
4064 $upload_dir = wp_upload_dir();
4065 $temp_folder = 'kirki_temp';
4066 $temp_folder_path = $upload_dir['basedir'] . '/' . $temp_folder;
4067
4068 return $temp_folder_path;
4069 }
4070
4071 /**
4072 * @deprecated
4073 * @see \Kirki\App\Managers\GlobalDataManager::get_initial_viewports()
4074 */
4075 public static function get_initial_view_ports()
4076 {
4077 return json_decode('{
4078 "active":"md",
4079 "scale":1,
4080 "zoom":1,
4081 "width":1200,
4082 "mdWidth":"",
4083 "defaults":[
4084 "md",
4085 "tablet",
4086 "mobileLandscape",
4087 "mobile"
4088 ],
4089 "list":{
4090 "md":{
4091 "value":1200,
4092 "scale":1,
4093 "minWidth":1200,
4094 "maxWidth":1200,
4095 "title":"Desktop",
4096 "icon":"desktop",
4097 "activeIcon":"desktop-hover"
4098 },
4099 "tablet":{
4100 "value":991,
4101 "scale":1,
4102 "minWidth":991,
4103 "maxWidth":991,
4104 "title":"Tablet",
4105 "icon":"tablet-default",
4106 "activeIcon":"tablet-hover"
4107 },
4108 "mobileLandscape":{
4109 "value":767,
4110 "scale":1,
4111 "minWidth":767,
4112 "maxWidth":767,
4113 "title":"Landscape",
4114 "icon":"phone-hr-default",
4115 "activeIcon":"phone-hr-hover"
4116 },
4117 "mobile":{
4118 "value":575,
4119 "scale":1,
4120 "minWidth":575,
4121 "maxWidth":575,
4122 "title":"Mobile",
4123 "icon":"phone-vr-default",
4124 "activeIcon":"phone-vr-hover"
4125 }
4126 }
4127 }', true);
4128 }
4129
4130 /**
4131 * @deprecated
4132 * @see \Kirki\App\Supports\FileHandler::download_zip_from_remote()
4133 */
4134 public static function download_zip_from_remote($remote_file, $new_name)
4135 {
4136 return FileHandler::download_zip_from_remote($remote_file, $new_name);
4137 // $file_ext = explode('.', $remote_file); // ['file', 'ext']
4138 // $file_ext = strtolower(end($file_ext)); // 'ext'
4139 // $allowed = ['zip'];
4140 // if (!in_array($file_ext, $allowed)) {
4141 // return false;
4142 // }
4143
4144 // try {
4145 // // error_reporting(E_ALL);
4146 // // ini_set('display_errors', 1);
4147 // // Download the file from the remote server.
4148 // // Create a stream context to disable SSL verification
4149 // $options = [
4150 // "http" => [
4151 // "method" => "GET",
4152 // "header" => "User-Agent: WordPress\r\n"
4153 // ],
4154 // "ssl" => [
4155 // "verify_peer" => false, // Disable verification of the peer's certificate
4156 // "verify_peer_name" => false // Disable verification of the peer's name
4157 // ]
4158 // ];
4159 // $context = stream_context_create($options);
4160 // $file_contents = file_get_contents($remote_file, false, $context);
4161
4162 // // Save the file locally.
4163 // if ($file_contents !== false) {
4164 // // Local path to save the downloaded file.
4165 // $local_file = wp_upload_dir()['basedir'] . '/' . $new_name;
4166 // file_put_contents($local_file, $file_contents);
4167 // return $local_file;
4168 // }
4169 // } catch (\Throwable $th) {
4170 // // throw $th;
4171 // }
4172 // return false;
4173 }
4174
4175 public static function filterZipFile($zip, $zip_file_path)
4176 {
4177 // Temporary filtered ZIP path
4178 $filtered_zip_path = sys_get_temp_dir() . '/filtered.zip';
4179 $filtered_zip = new \ZipArchive;
4180
4181 if ($filtered_zip->open($filtered_zip_path, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) {
4182 // Loop through all files in the archive
4183 for ($i = 0; $i < $zip->numFiles; $i++) {
4184 $filename = $zip->getNameIndex($i);
4185 $file_path = 'zip://' . $zip_file_path . '#' . $filename;
4186
4187 // Get MIME type based on file extensioncm_f
4188 $file_mime = self::getMimeTypeByExtension($filename);
4189
4190 // Additional JSON validation
4191 if ($file_mime === 'application/json' && !self::isJsonFile($file_path)) {
4192 $file_mime = 'text/plain'; // Fallback if not valid JSON
4193 }
4194
4195 // Check if the file MIME type matches supported types
4196 $is_supported = false;
4197 foreach (KIRKI_SUPPORTED_MEDIA_TYPES as $types) {
4198 if (in_array($file_mime, $types)) {
4199 $is_supported = true;
4200 break;
4201 }
4202 }
4203
4204 // Add the file to the filtered archive if supported
4205 if ($is_supported) {
4206 $file_contents = $zip->getFromIndex($i);
4207 $filtered_zip->addFromString($filename, $file_contents);
4208 }
4209 }
4210
4211 $filtered_zip->close();
4212 return $filtered_zip_path;
4213 } else {
4214 return false;
4215 }
4216 }
4217
4218 // Helper function to get MIME type by file extension
4219 private static function getMimeTypeByExtension($filename)
4220 {
4221 $extension_to_mime = [
4222 'json' => 'application/json',
4223 'jpg' => 'image/jpeg',
4224 'jpeg' => 'image/jpeg',
4225 'png' => 'image/png',
4226 'gif' => 'image/gif',
4227 'webp' => 'image/webp',
4228 'svg' => 'image/svg+xml',
4229 'pdf' => 'application/pdf',
4230 'mp4' => 'video/mp4',
4231 'ogg' => 'audio/ogg',
4232 'lottie' => 'text/plain',
4233 'mov' => 'video/quicktime',
4234 'mp3' => 'audio/mpeg',
4235 'wav' => 'audio/wav',
4236
4237 // Add more extensions as needed
4238 ];
4239
4240 $ext = pathinfo($filename, PATHINFO_EXTENSION);
4241 return $extension_to_mime[strtolower($ext)] ?? 'application/octet-stream';
4242 }
4243
4244 // Helper function to validate JSON file content
4245 private static function isJsonFile($file_path)
4246 {
4247 $file_contents = @file_get_contents($file_path);
4248 $trimmed = trim($file_contents);
4249 return $trimmed[0] === '{' || $trimmed[0] === '[';
4250 }
4251
4252 public static function is_remote_url($url)
4253 {
4254 // Parse the URL to get components
4255 $parsed_url = wp_parse_url($url);
4256 return isset($parsed_url['scheme']);
4257 }
4258
4259 public static function is_element_accessible($access)
4260 {
4261 switch ($access) {
4262 case 'all':
4263 return true; // Accessible to everyone
4264
4265 case 'guest':
4266 return !is_user_logged_in();
4267
4268 case 'logged-in':
4269 return is_user_logged_in(); // Accessible to any logged-in user
4270
4271 case 'admin':
4272 // Administrators and Super Admins
4273 return current_user_can('manage_options');
4274
4275 case 'editor':
4276 // Editors, Admins, and Super Admins can see this
4277 return current_user_can('edit_pages');
4278
4279 case 'author':
4280 // Authors, Editors, Admins, etc.
4281 return current_user_can('publish_posts');
4282
4283 case 'subscriber':
4284 // Subscribers and EVERY logged-in user above them
4285 return current_user_can('read');
4286
4287 default:
4288 return false; // Safely hide if the access rule is unrecognized
4289 }
4290 }
4291
4292 /**
4293 * Find symbol for post id using condition
4294 * it will find and return selected symbols html and css;
4295 *
4296 * @param string $type : symbol type.
4297 * @param string $post : post object.
4298 * @return symbol || bool(false)
4299 */
4300 public static function find_symbol_for_this_page($type)
4301 {
4302 $all_symbols = Symbol::fetch_list(true, false);
4303 foreach ($all_symbols as $key => $symbol) {
4304 if (isset($symbol['setAs']) && $symbol['setAs'] === $type) {
4305 return $symbol;
4306 }
4307 }
4308 return false;
4309 }
4310 /**
4311 * Get Custom Header
4312 * it will find and return selected symbols html and css;
4313 *
4314 * @param string $type stymbol type header|footer.
4315 * @param string $html if true the function will return html otherwise return symbol object.
4316 * @return string|object custom section html or stymbol object.
4317 */
4318 public static function get_page_custom_section($type, $html = true)
4319 {
4320 $show = apply_filters('kirki_show_custom_section_' . $type, true);
4321 if (!$show) {
4322 return '';
4323 }
4324
4325
4326 $symbol = self::find_symbol_for_this_page($type);
4327 if (!$html) {
4328 return $symbol;
4329 }
4330
4331 if (isset(self::$custom_sections[$type])) {
4332 return self::$custom_sections[$type];
4333 }
4334
4335 $s = self::isShowWPThemeHeaderFooter() ? '' : ' '; // this is for disableing theme header footer forcefully if is_show_wp_theme_header_footer is false
4336
4337 if ($symbol) {
4338 $action = HelperFunctions::sanitize_text(isset($_GET['action']) ? $_GET['action'] : '');
4339 $symbol_data = $symbol['symbolData'];
4340 $set_as = isset($symbol['setAs']) ? $symbol['setAs'] : '';
4341
4342 $post_id = self::get_post_id_if_possible_from_url();
4343 $template_data = self::get_template_data_if_current_page_is_kirki_template();
4344 if ($template_data)
4345 $post_id = $template_data['template_id'];
4346
4347
4348 $custom_page_data = self::get_custom_data_if_current_page_is_kirki_custom_post();
4349 if ($custom_page_data)
4350 $post_id = $custom_page_data['post_id'];
4351
4352 $is_page_symbol_disabled = get_post_meta($post_id, KIRKI_META_NAME_FOR_PAGE_HF_SYMBOL_DISABLE_STATUS, true);
4353 if (isset($is_page_symbol_disabled) && is_array($is_page_symbol_disabled) && isset($is_page_symbol_disabled[$type])) {
4354 $is_page_symbol_disabled = $is_page_symbol_disabled[$type];
4355 } else {
4356 $is_page_symbol_disabled = false;
4357 }
4358
4359 $params = array(
4360 'blocks' => $symbol_data['data'],
4361 'style_blocks' => $symbol_data['styleBlocks'],
4362 'root' => $symbol_data['root'],
4363 'post_id' => $symbol['id'],
4364 'options' => [],
4365 'get_style' => true,
4366 'get_variable' => false,
4367 'should_take_app_script' => false,
4368 'prefix' => 'kirki-s' . $symbol['id']
4369 );
4370 if ($action === KIRKI_EDITOR_ACTION) {
4371 $extra_attr_for_hf_symbol = '';
4372 if ($is_page_symbol_disabled)
4373 $extra_attr_for_hf_symbol = ' style="display:none;"';
4374 $s = '<' . $type . $extra_attr_for_hf_symbol . ' data-kirki-symbol_set_as="' . $set_as . '" data-kirki-symbol="' . $symbol['id'] . '" data-kirki="' . $type . '">' . self::get_html_using_preview_script($params) . '</' . $type . '>'; //added data-kirki="$type" => We removed theme header and footer using preg_replace in TheFrontendHooks.php file
4375 } else if (!$is_page_symbol_disabled) {
4376 $params['should_take_app_script'] = true;
4377 $params['get_variable'] = true;
4378 $s = self::get_html_using_preview_script($params);
4379 } else if ($is_page_symbol_disabled) {
4380 $s = '<!-- ' . $type . ' is disabled -->';
4381 }
4382 }
4383
4384 $s = do_shortcode($s);
4385
4386 self::$custom_sections[$type] = $s;
4387
4388 return $s;
4389 }
4390
4391 public static function isShowWPThemeHeaderFooter()
4392 {
4393 $common_data = WpAdmin::get_common_data(true);
4394 return $common_data['is_show_wp_theme_header_footer'];
4395 }
4396
4397 /**
4398 * Check if a value is considered true or false.
4399 *
4400 * @param mixed $value The value to check.
4401 * @return bool Returns true if the value is considered "truthy", otherwise false.
4402 *
4403 * @deprecated
4404 * @see function \Kirki\App\is_truthy()
4405 */
4406 public static function isTruthy($value): bool
4407 {
4408 return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false;
4409 }
4410
4411 /**
4412 * Get the upload directory path has upload or write permission.
4413 *
4414 * @deprecated
4415 * @see Kirki\Framework\Supports\Facades\File::is_writable()
4416 * @see function Kirki\App\get_upload_directory()
4417 */
4418 public static function get_upload_dir_has_write_permission()
4419 {
4420 if (!function_exists('request_filesystem_credentials')) {
4421 require_once ABSPATH . 'wp-admin/includes/file.php';
4422 }
4423
4424 if (WP_Filesystem()) {
4425 global $wp_filesystem;
4426 $upload_dir = wp_upload_dir();
4427 return $wp_filesystem->is_writable($upload_dir['basedir']);
4428 }
4429
4430 return false;
4431 }
4432
4433 /**
4434 * $name = [] or string
4435 * @deprecated
4436 * @see Kirki\App\Supports\Template::add_prefix_to_class_name()
4437 */
4438 public static function add_prefix_to_class_name($prefix, $name)
4439 {
4440 if (is_array($name)) {
4441 foreach ($name as $key => $c) {
4442 $c = strtolower($c);
4443 if (in_array($c, KIRKI_PRESERVED_CLASS_LIST)) {
4444 $name[$key] = $c;
4445 } else {
4446 $name[$key] = $prefix ? strtolower($prefix) . '-' . $c : $c;
4447 }
4448 }
4449 } else {
4450 $name = strtolower($name);
4451 if (!in_array($name, KIRKI_PRESERVED_CLASS_LIST)) {
4452 $name = $prefix ? strtolower($prefix) . '-' . $name : $name;
4453 }
4454 }
4455 return $name;
4456 }
4457
4458 public static function checkVisibilityConditions($element, $options)
4459 {
4460 $conditions = $element['properties']['visibilityConditions'] ?? [];
4461 if (!count($conditions))
4462 return true;
4463 foreach ($conditions as $and_group) {
4464 $and_result = true;
4465 foreach ($and_group as $condition) {
4466 $source = (string) ($condition['source'] ?? 'kirki');
4467 $condition_result = apply_filters('kirki_visibility_condition_check_' . $source, false, $condition, $options);
4468 if (!$condition_result) {
4469 $and_result = false;
4470 break; // If any condition fails in AND group
4471 }
4472 }
4473 if ($and_result) {
4474 return true; // If any OR group passes
4475 }
4476 }
4477 return false; // No group passed
4478 }
4479
4480 private static function update_slider_style_blocks($blocks, $styles)
4481 {
4482
4483 $slider_mask_styleIds = [];
4484 $slider_item_styleIds = [];
4485
4486
4487 // slider_item
4488 foreach ($blocks as $id => $block) {
4489 // Check if the block has a 'name' key
4490 if (isset($block['name'])) {
4491 if (isset($block['name']) && $block['name'] === 'slider_mask') {
4492 // Loop through each item in $block['styleIds']
4493 foreach ($block['styleIds'] as $styleId) {
4494 // Check if the styleId is NOT already in the $slider_mask_styleIds array
4495 if (!in_array($styleId, $slider_mask_styleIds)) {
4496 // If not present, push it into the array
4497 $slider_mask_styleIds[] = $styleId;
4498 }
4499 }
4500 }
4501
4502 if (isset($block['name']) && $block['name'] === 'slider_item') {
4503 // Loop through each item in $block['styleIds']
4504 foreach ($block['styleIds'] as $styleId) {
4505 // Check if the styleId is NOT already in the $slider_item_styleIds array
4506 if (!in_array($styleId, $slider_item_styleIds)) {
4507 // If not present, push it into the array
4508 $slider_item_styleIds[] = $styleId;
4509 }
4510 }
4511 }
4512 }
4513 }
4514
4515 if (count($slider_mask_styleIds) > 0) {
4516 foreach ($slider_mask_styleIds as $styleId) {
4517 if ($styles[$styleId]) {
4518 $style = $styles[$styleId];
4519 $style_variants = $style['variant'];
4520
4521 $styleVariants = [];
4522
4523 if ($style_variants && count($style_variants) > 0) {
4524 foreach ($style_variants as $key => $css) {
4525 $css = preg_replace('/overflow\s*:\s*hidden;?/', '', $css);
4526 $css = preg_replace('/pointer-events\s*:\s*none;?/', '', $css);
4527
4528 $css = trim($css);
4529 $styleVariants[$key] = $css;
4530 }
4531 }
4532 $styles[$styleId]['variant'] = $styleVariants;
4533 }
4534 }
4535 }
4536
4537
4538 if (count($slider_item_styleIds) > 0) {
4539 foreach ($slider_item_styleIds as $styleId) {
4540 $style = $styles[$styleId];
4541 $style_variants = $style['variant'];
4542
4543 $styleVariants = [];
4544
4545 if ($style_variants && count($style_variants) > 0) {
4546 foreach ($style_variants as $key => $css) {
4547 $css = preg_replace('/position\s*:\s*absolute;?/', '', $css);
4548 $css = preg_replace('/display\s*:\s*none;?/', '', $css);
4549
4550 $css = trim($css);
4551 $styleVariants[$key] = $css;
4552 }
4553
4554 $styles[$styleId]['variant'] = $styleVariants;
4555 }
4556 }
4557 }
4558
4559 return $styles;
4560 }
4561
4562 public static function handle_legacy_slider_class()
4563 {
4564 $data = Page::get_all_data_by_kirki_meta_key();
4565
4566 foreach ($data as $key => $value) {
4567 $post_id = $value['post_id']; // ID of the post
4568 $meta_key = $value['meta_key']; // Meta key name
4569 $meta_value = $value['meta_value']; // Serialized meta value
4570
4571 $meta_value = unserialize($meta_value, ['allowed_classes' => false]); // Convert serialized data to array
4572
4573 // If the meta value has a 'blocks' key, handle it as a full page data
4574 if (isset($meta_value['blocks'])) {
4575 $blocks = $meta_value['blocks']; // Get the blocks array
4576 $styles = self::get_page_styleblocks($post_id);
4577 $updated_styles = self::update_slider_style_blocks($blocks, $styles); // Update block names
4578
4579 self::update_page_styleblocks($post_id, $updated_styles);
4580 } else {
4581 // If no 'blocks' key, treat entire value as blocks array
4582 $blocks = $meta_value;
4583 $post = get_post($post_id);
4584
4585
4586 if (isset($post->post_type) && $post->post_type === 'kirki_symbol') {
4587 $styles = $blocks['styleBlocks'];
4588 $updated_styles = self::update_slider_style_blocks($blocks['data'], $styles);
4589 $blocks['styleBlocks'] = $updated_styles;
4590
4591 update_post_meta($post_id, $meta_key, $blocks);
4592 }
4593 }
4594 }
4595 }
4596
4597 public static function handle_legacy_slider_default_class()
4598 {
4599 $styles = self::get_global_data_using_key(KIRKI_GLOBAL_STYLE_BLOCK_META_KEY);
4600 if (!$styles) {
4601 $styles = array();
4602 }
4603 if (count($styles) > 0) {
4604 if (isset($styles['kirki_slider_slide'])) {
4605 // Handle kirki_slider_mask
4606 if (isset($styles['kirki_slider_mask'])) {
4607 $variants = $styles['kirki_slider_mask']['variant'];
4608 if (count($variants) > 0) {
4609 $styleVariants = [];
4610 foreach ($variants as $key => $css) {
4611 $css = preg_replace('/overflow\s*:\s*hidden;?/', '', $css);
4612 $css = preg_replace('/pointer-events\s*:\s*none;?/', '', $css);
4613 $css = trim($css);
4614 $styleVariants[$key] = $css;
4615 }
4616 $styles['kirki_slider_mask']['variant'] = $styleVariants;
4617 }
4618 }
4619
4620 // Handle kirki_slider_slide
4621 if (isset($styles['kirki_slider_slide'])) {
4622 $variants = $styles['kirki_slider_slide']['variant'];
4623 if (count($variants) > 0) {
4624 $styleVariants = [];
4625 foreach ($variants as $key => $css) {
4626 $css = preg_replace('/position\s*:\s*absolute;?/', '', $css);
4627 $css = preg_replace('/display\s*:\s*none;?/', '', $css);
4628 $css = trim($css);
4629 $styleVariants[$key] = $css;
4630 }
4631 $styles['kirki_slider_slide']['variant'] = $styleVariants;
4632 }
4633 }
4634 }
4635 }
4636
4637 }
4638
4639 public static function get_current_item_index($index, $options)
4640 {
4641 $pagination = isset($options['pagination']) ? $options['pagination'] : false;
4642 $items_per_page = isset($options['items_per_page']) ? $options['items_per_page'] : 3;
4643 $page_no = isset($options['page_no']) ? $options['page_no'] : 1;
4644
4645 if ($pagination && $items_per_page > 0 && $page_no > 0) {
4646 // Calculate the start index based on the current page and items per page
4647 $index = (($page_no - 1) * $items_per_page) + $index;
4648 }
4649
4650 return $index;
4651 }
4652
4653 public static function convertToBytes($val)
4654 {
4655 $val = trim($val);
4656 $last = strtolower($val[strlen($val) - 1]);
4657 $val = (int) $val;
4658 switch ($last) {
4659 case 'g':
4660 $val *= 1024;
4661 case 'm':
4662 $val *= 1024;
4663 case 'k':
4664 $val *= 1024;
4665 }
4666 return $val;
4667 }
4668
4669 /**
4670 * @deprecated
4671 * @see Kirki\App\Supports\Canvas::get_full_canvas_template_path()
4672 */
4673 public static function get_kirki_full_canvas_template_path()
4674 {
4675 return self::normalize_kirki_full_canvas_template_path(KIRKI_FULL_CANVAS_TEMPLATE_PATH);
4676 }
4677
4678 /**
4679 * @deprecated
4680 * @see Kirki\App\Supports\Canvas::normalize_full_canvas_template_path()
4681 */
4682 public static function normalize_kirki_full_canvas_template_path($path)
4683 {
4684 // replace if has kirki-pro => kirki
4685 if (strpos($path, 'kirki-pro') !== false) {
4686 $path = str_replace('kirki-pro', 'kirki', $path);
4687 }
4688 return $path;
4689 }
4690 public static function normalize_variable_mode($mode)
4691 {
4692 if (!$mode) {
4693 return ['color' => 'inherit', 'size' => 'inherit', 'text-style' => 'inherit', 'font-family' => 'inherit'];
4694 }
4695 // if v is string
4696 if (is_string($mode)) {
4697 return ['color' => $mode, 'size' => $mode, 'text-style' => $mode, 'font-family' => $mode];
4698 }
4699 if (is_array($mode)) {
4700 return $mode;
4701 }
4702 return $mode;
4703 }
4704 }
4705