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