PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.1.1
Kirki – Freeform Page Builder, Website Builder & Customizer v6.1.1
6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / app / Managers / GlobalDataManager.php
kirki / app / Managers Last commit date
GlobalDataManager.php 3 days ago PageManager.php 3 days ago SymbolManager.php 3 weeks ago
GlobalDataManager.php
708 lines
1 <?php
2
3 namespace Kirki\App\Managers;
4
5 defined('ABSPATH') || exit;
6
7 use Kirki\App\Constants\GlobalDataKeys;
8 use Kirki\App\Constants\OptionKeys;
9 use Kirki\App\Constants\PostTypes;
10 use Kirki\App\Models\Page as PageModel;
11 use Kirki\App\Models\PostMeta;
12 use Kirki\Framework\Supports\Facades\Option;
13
14 class GlobalDataManager
15 {
16 /** @var int */
17 protected $post_id = 0;
18
19 /** @var array */
20 protected $global_style_blocks;
21
22 /** @var array */
23 protected $global_ui_controller;
24
25 /** @var array */
26 protected $global_ui_saved_data;
27
28 /** @var array */
29 protected $global_custom_fonts;
30
31 /**
32 * Get global data post id
33 *
34 * @return int
35 */
36 private function get_post_id()
37 {
38 if (!empty($this->post_id)) {
39 return $this->post_id;
40 }
41
42 $is_option_with_prefix = false;
43 $post_id = null;
44
45 $post_id = Option::get(OptionKeys::GLOBAL_DATA_POST_TYPE_ID, $post_id, $is_option_with_prefix);
46
47 if($post_id) {
48 $this->post_id = (int) $post_id;
49
50 return $this->post_id;
51 }
52
53 $post_id = Option::get(OptionKeys::DROIP_GLOBAL_DATA_POST_TYPE_ID, $post_id, $is_option_with_prefix);
54
55 if($post_id) {
56 $this->post_id = (int) $post_id;
57
58 return $this->post_id;
59 }
60
61 //this block will run only once
62 $post = PageModel::where('post_type', PostTypes::GLOBAL_DATA)->first();
63
64 $post_id = $post->ID ?? null;
65
66 if (empty($post)) {
67 $post = PageModel::create_post([
68 'post_title' => PostTypes::GLOBAL_DATA,
69 'post_type' => PostTypes::GLOBAL_DATA,
70 'post_status' => 'draft'
71 ]);
72
73 $post_id = $post->ID ?? null;
74 }
75
76 if($post_id) {
77 $autoload = true;
78 Option::set(OptionKeys::GLOBAL_DATA_POST_TYPE_ID, $post_id, $autoload, $is_option_with_prefix);
79 }
80
81 return $this->post_id = (int) $post_id;
82 }
83
84 /**
85 * Get global data using key
86 *
87 * @param string $key
88 * @return mixed
89 *
90 */
91 public function get(string $key)
92 {
93 //first get post using KIRKI_GLOBAL_DATA_POST_TYPE_NAME post_type name. if not found then create new one.
94 $post_id = $this->get_post_id();
95
96 $meta_value = PostMeta::get_meta_value($post_id, $key, null);
97
98 if ($meta_value !== null) {
99 return $meta_value;
100 }
101
102 $with_prefix = false;
103 $default_value = null;
104
105 // this block will run only once for a legacy option key.
106 $value = Option::get($key, $default_value, $with_prefix);
107
108 if (!is_null($value)) {
109 PostMeta::update_meta_value($post_id, $key, $value);
110 Option::delete($key, $with_prefix);
111 }
112
113 return $value;
114 }
115
116 /**
117 * Get global data using key
118 *
119 * @param string $key
120 * @param mixed $value
121 */
122 public function update(string $key, $value)
123 {
124 $post_id = $this->get_post_id();
125
126 PostMeta::update_meta_value($post_id, $key, $value);
127 }
128
129 /**
130 * Get deprecated global style blocks
131 *
132 * @return array
133 */
134 public function get_deprecated_global_style_blocks()
135 {
136 return $this->get(GlobalDataKeys::STYLE_BLOCK_DEPRECATED) ?? [];
137 }
138
139 /**
140 * Update global style blocks
141 *
142 * @param array $styles
143 */
144 public function update_style_blocks($styles)
145 {
146 if (!is_array($styles)) {
147 $styles = [];
148 }
149
150 $this->update(GlobalDataKeys::STYLE_BLOCKS, $styles);
151
152 $this->global_style_blocks = null;
153 }
154
155 /**
156 * Get global style blocks
157 *
158 * @return array
159 */
160 public function get_style_blocks()
161 {
162 if (!is_null($this->global_style_blocks)) {
163 return $this->global_style_blocks;
164 }
165
166 return $this->global_style_blocks = $this->get(GlobalDataKeys::STYLE_BLOCKS) ?? [];
167 }
168
169 /**
170 * Get global UI controller
171 *
172 * @return array
173 */
174 public function get_global_ui_controller()
175 {
176 if (!is_null($this->global_ui_controller)) {
177 return $this->global_ui_controller;
178 }
179
180 return $this->global_ui_controller = $this->get(GlobalDataKeys::UI_CONTROLLER) ?? [];
181 }
182
183 /**
184 * Update global UI controller
185 *
186 * @param array $controller
187 */
188 public function update_global_ui_controller($controller)
189 {
190 if (!is_array($controller)) {
191 return;
192 }
193
194 $this->update(GlobalDataKeys::UI_CONTROLLER, $controller);
195
196 $this->global_ui_controller = null;
197 }
198
199 /**
200 * Get global UI saved data
201 *
202 * @return array
203 */
204 public function get_global_ui_saved_data()
205 {
206 if (!is_null($this->global_ui_saved_data)) {
207 return $this->global_ui_saved_data;
208 }
209
210 $saved_data = $this->get(GlobalDataKeys::UI_SAVED_DATA) ?? [];
211
212 $saved_data['variableData'] = $this->get_variable_data($saved_data);
213
214 return $this->global_ui_saved_data = $saved_data;
215 }
216
217 /**
218 * Update global UI saved data
219 *
220 * @param array $fonts
221 */
222 public function update_global_ui_saved_data($fonts)
223 {
224 if (!is_array($fonts)) {
225 return;
226 }
227
228 $this->update(GlobalDataKeys::UI_SAVED_DATA, $fonts);
229
230 $this->global_ui_saved_data = null;
231 }
232
233 /**
234 * Get global custom fonts data
235 *
236 * @return array
237 */
238 public function get_global_custom_fonts()
239 {
240 if (!is_null($this->global_custom_fonts)) {
241 return $this->global_custom_fonts;
242 }
243
244 return $this->global_custom_fonts = $this->get(GlobalDataKeys::CUSTOM_FONTS) ?? [];
245 }
246
247 /**
248 * Update global custom fonts data
249 *
250 * @param array $data
251 */
252 public function update_global_custom_fonts($data)
253 {
254 if (!is_array($data)) {
255 return;
256 }
257
258 $this->update(GlobalDataKeys::CUSTOM_FONTS, $data);
259
260 $this->global_custom_fonts = null;
261 }
262
263 /**
264 * Get variable data
265 *
266 * @param array|null $saved_data, if pass null will get $saved_data otherwise will use $saved_data
267 *
268 * @return array
269 */
270 public function get_variable_data($saved_data = null) {
271 $saved_data = is_array($saved_data) ? $saved_data : ($this->get(GlobalDataKeys::UI_SAVED_DATA) ?? []);
272
273 $variables = $saved_data && !empty($saved_data['variableData']['data'])
274 ? $this->normalize_variable_data($saved_data['variableData'])
275 : $this->initial_variable_data();
276
277 $variables = $this->normalize_old_variable_data($variables);
278
279 $variables = $this->sort_variable_data($variables);
280
281 return $variables;
282 }
283
284 /**
285 * Normalize old variable data
286 *
287 * @param array $variables
288 *
289 * @return array
290 */
291 protected function normalize_old_variable_data($variables) {
292 // Check if data needs normalization
293 if (!isset($variables['data']) || !is_array($variables['data'])) {
294 return $variables;
295 }
296
297 // Check if already normalized (has the 4 standard groups with correct keys)
298 $expected_keys = ['color', 'size', 'text-style', 'font-family'];
299 $actual_keys = array_column($variables['data'], 'key');
300
301 if (count($actual_keys) === 4 && empty(array_diff($expected_keys, $actual_keys))) {
302 // Already normalized, return as-is
303 return $variables;
304 }
305
306 // Create fresh groups
307 $normalized = [
308 'data' => [
309 [
310 'title' => 'Colors',
311 'key' => 'color',
312 'modes' => [],
313 'variables' => [],
314 ],
315 [
316 'title' => 'Numbers',
317 'key' => 'size',
318 'modes' => [],
319 'variables' => [],
320 ],
321 [
322 'title' => 'Text Styles',
323 'key' => 'text-style',
324 'modes' => [],
325 'variables' => [],
326 ],
327 [
328 'title' => 'Font Family',
329 'key' => 'font-family',
330 'modes' => [],
331 'variables' => [],
332 ],
333 ],
334 ];
335
336 // Index for quick access
337 $groups = [];
338
339 foreach ($normalized['data'] as $index => &$group) {
340 $groups[$group['key']] = &$normalized['data'][$index];
341 }
342
343 unset($group);
344
345 // Track modes per type
346 $modes_per_type = [
347 'color' => [],
348 'size' => [],
349 'text-style' => [],
350 'font-family' => [],
351 ];
352
353 // Single pass: process all variables
354 foreach ($variables['data'] as $section) {
355 if (empty($section['variables'])) {
356 continue;
357 }
358
359 foreach ($section['variables'] as $variable) {
360 // Determine correct group based on variable's type
361 $var_type = isset($variable['type']) ? $variable['type'] : null;
362
363 if (!$var_type || !isset($groups[$var_type])) {
364 continue;
365 }
366
367 // Add variable to correct group (move if in wrong section)
368 $groups[$var_type]['variables'][] = $variable;
369
370 // Collect modes for this type
371 if (isset($variable['value']) && is_array($variable['value'])) {
372 foreach (array_keys($variable['value']) as $mode_key) {
373 if (!isset($modes_per_type[$var_type][$mode_key])) {
374 $modes_per_type[ $var_type ][ $mode_key ] = [
375 'key' => $mode_key,
376 'title' => ucfirst($mode_key),
377 ];
378 }
379 }
380 }
381 }
382 }
383
384 // Assign collected modes to groups
385 foreach ($modes_per_type as $type => $modes) {
386 if (isset($groups[$type])) {
387 $groups[$type]['modes'] = array_values($modes);
388
389 // Ensure default mode exists
390 if (empty($groups[$type]['modes'])) {
391 $groups[$type]['modes'][] = [
392 'key' => 'default',
393 'title' => 'Default',
394 ];
395 }
396 }
397 }
398
399 return $normalized;
400 }
401
402 /**
403 * Normalize variable data
404 *
405 * @param array $raw_data
406 *
407 * @return array
408 *
409 * @todo: need to refactor this method, copied from \Kirki\Ajax\UserData::normalize_variable_data()
410 */
411 public function normalize_variable_data( $raw_data ) {
412 if(
413 isset($raw_data['data'])
414 && is_array($raw_data['data'])
415 && count($raw_data['data']) > 0
416 && isset($raw_data['data'][0]['key'])
417 ) {
418 return $raw_data; // thats means already normalized.
419 }
420
421 // Start from clean base
422 $organized = self::initial_variable_data();
423
424 // Index groups by key for fast access
425 $groups = [];
426
427 foreach ($organized['data'] as $index => $group) {
428 $groups[$group['key'] ] = &$organized['data'][$index];
429 }
430
431 foreach ($raw_data['data'] as $section) {
432
433 /* ---------------------------------
434 * Merge modes (unique by key)
435 * --------------------------------- */
436 if (!empty($section['modes'])) {
437 foreach ($groups as &$group) {
438 $mode_index = [];
439
440 foreach ($group['modes'] as $existing_mode) {
441 if (isset($existing_mode['key'])) {
442 $mode_index[$existing_mode['key']] = true;
443 }
444 }
445
446 foreach ($section['modes'] as $mode) {
447 if (isset($mode['key']) && !isset($mode_index[$mode['key']])) {
448 $group['modes'][] = $mode;
449 $mode_index[$mode['key']] = true;
450 }
451 }
452 }
453 }
454
455 /* ---------------------------------
456 * Organize variables
457 * --------------------------------- */
458 if (empty($section['variables'])) {
459 continue;
460 }
461
462 foreach ($section['variables'] as $variable) {
463
464 if (empty($variable['type'])) {
465 continue;
466 }
467
468 $group_key = $variable['type'];
469
470 if (!isset($groups[$group_key])) {
471 continue;
472 }
473
474 $found = false;
475
476 foreach ($groups[$group_key]['variables'] as &$existing) {
477
478 if (isset($existing['id'], $variable['id']) && $existing['id'] === $variable['id']) {
479
480 // Merge values by mode
481 $existing['value'] = array_merge(
482 $existing['value'] ?? [],
483 $variable['value'] ?? []
484 );
485
486 $found = true;
487 break;
488 }
489 }
490
491 if (!$found) {
492 $groups[$group_key]['variables'][] = $variable;
493 }
494 }
495 }
496
497 return $organized;
498 }
499
500 /**
501 * Initial variable data
502 *
503 * @return array
504 */
505 protected function initial_variable_data() {
506 return [
507 'data' => [
508 [
509 'title' => 'Colors',
510 'key' => 'color',
511 'modes' => [
512 [
513 'title' => 'Default',
514 'key' => 'default',
515 ],
516 ],
517 'variables' => [],
518 ],
519 [
520 'title' => 'Numbers',
521 'key' => 'size',
522 'modes' => [
523 [
524 'title' => 'Default',
525 'key' => 'default',
526 ],
527 ],
528 'variables' => [],
529 ],
530 [
531 'title' => 'Text Styles',
532 'key' => 'text-style',
533 'modes' => [
534 [
535 'title' => 'Default',
536 'key' => 'default',
537 ],
538 ],
539 'variables' => [],
540 ],
541 [
542 'title' => 'Font Family',
543 'key' => 'font-family',
544 'modes' => [
545 [
546 'title' => 'Default',
547 'key' => 'default',
548 ],
549 ],
550 'variables' => [],
551 ],
552 ],
553
554 ];
555 }
556
557 /**
558 * Sort variable data
559 *
560 * @param array $variable_data
561 *
562 * @return array
563 */
564 public function sort_variable_data($variable_data) {
565 if (empty($variable_data) || !is_array($variable_data)) {
566 return $variable_data;
567 }
568
569 // Enforce deterministic group order: Color, Number, Text style, Font family
570 $order_map = [
571 'color' => 0,
572 'size' => 1,
573 'text-style' => 2,
574 'font-family' => 3,
575 ];
576
577 usort(
578 $variable_data['data'],
579 function ($first, $second) use ($order_map) {
580 $first_order = isset($order_map[$first['key']]) ? $order_map[$first['key']] : 99;
581 $second_order = isset($order_map[$second['key']]) ? $order_map[$second['key']] : 99;
582 return $first_order - $second_order;
583 }
584 );
585
586 return $variable_data;
587 }
588
589 /**
590 * Get View port list.
591 * this method only for front end. not for editor. cause list data not completed data.
592 *
593 * @return array
594 */
595 public function get_view_port_list() {
596 $control = $this->get_global_ui_controller();
597
598 if (empty($control) || !isset($control['viewport'], $control['viewport']['list'])) {
599 return $this->sort_viewport_list($this->get_initial_viewports()['list']);
600 }
601
602 return $this->sort_viewport_list((array) $control['viewport']['list']);
603 }
604
605 /**
606 * Sort view port list
607 *
608 * @param array $viewport_list list of view port list.
609 * @return array
610 */
611 protected function sort_viewport_list($viewport_list) {
612 $viewport_list = $viewport_list;
613 $list = [];
614 uasort($viewport_list, static function ($first, $second) {
615 return ($first['minWidth'] ?? 0) <=> ($second['minWidth'] ?? 0);
616 });
617 $passed_md = false;
618
619 foreach ($viewport_list as $key => $value) {
620 if ($passed_md) {
621 $list[$key] = $value;
622 $list[$key]['type'] = 'min';
623 $list[$key]['id'] = $key;
624 }
625
626 if ($key === 'md') {
627 $passed_md = true;
628 $list[$key] = $value;
629 $list[$key]['id'] = $key;
630 $list[$key]['type'] = 'max';
631 }
632 }
633 $passed_md = false;
634
635 foreach ( array_reverse( $viewport_list ) as $key => $value ) {
636 if ($passed_md) {
637 $list[$key] = $value;
638 $list[$key]['type'] = 'max';
639 $list[$key]['id'] = $key;
640 }
641
642 if ($key === 'md') {
643 $passed_md = true;
644 }
645 }
646 return $list;
647 }
648
649 /**
650 * Get initial view port list.
651 *
652 * @return array
653 */
654 public function get_initial_viewports()
655 {
656 return [
657 'active' => 'md',
658 'scale' => 1,
659 'zoom' => 1,
660 'width' => 1200,
661 'mdWidth' => '',
662 'defaults' => [
663 'md',
664 'tablet',
665 'mobileLandscape',
666 'mobile',
667 ],
668 'list' => [
669 'md' => [
670 'value' => 1200,
671 'scale' => 1,
672 'minWidth' => 1200,
673 'maxWidth' => 1200,
674 'title' => 'Desktop',
675 'icon' => 'desktop',
676 'activeIcon' => 'desktop-hover',
677 ],
678 'tablet' => [
679 'value' => 991,
680 'scale' => 1,
681 'minWidth' => 991,
682 'maxWidth' => 991,
683 'title' => 'Tablet',
684 'icon' => 'tablet-default',
685 'activeIcon' => 'tablet-hover',
686 ],
687 'mobileLandscape' => [
688 'value' => 767,
689 'scale' => 1,
690 'minWidth' => 767,
691 'maxWidth' => 767,
692 'title' => 'Landscape',
693 'icon' => 'phone-hr-default',
694 'activeIcon' => 'phone-hr-hover',
695 ],
696 'mobile' => [
697 'value' => 575,
698 'scale' => 1,
699 'minWidth' => 575,
700 'maxWidth' => 575,
701 'title' => 'Mobile',
702 'icon' => 'phone-vr-default',
703 'activeIcon' => 'phone-vr-hover',
704 ],
705 ],
706 ];
707 }
708 }