PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.0.13
Kirki – Freeform Page Builder, Website Builder & Customizer v6.0.13
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 2 weeks ago PageManager.php 2 weeks ago SymbolManager.php 2 weeks ago
GlobalDataManager.php
698 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 global style blocks
131 *
132 * @return array
133 */
134 public function get_global_style_blocks()
135 {
136 if (!is_null($this->global_style_blocks)) {
137 return $this->global_style_blocks;
138 }
139
140 return $this->global_style_blocks = $this->get(GlobalDataKeys::STYLE_BLOCK) ?? [];
141 }
142
143 /**
144 * Update global style blocks
145 *
146 * @param array $styles
147 */
148 public function update_global_style_blocks($styles)
149 {
150 if (!is_array($styles)) {
151 return;
152 }
153
154 $this->update(GlobalDataKeys::STYLE_BLOCK, $styles);
155
156 $this->global_style_blocks = null;
157 }
158
159 /**
160 * Get global UI controller
161 *
162 * @return array
163 */
164 public function get_global_ui_controller()
165 {
166 if (!is_null($this->global_ui_controller)) {
167 return $this->global_ui_controller;
168 }
169
170 return $this->global_ui_controller = $this->get(GlobalDataKeys::UI_CONTROLLER) ?? [];
171 }
172
173 /**
174 * Update global UI controller
175 *
176 * @param array $controller
177 */
178 public function update_global_ui_controller($controller)
179 {
180 if (!is_array($controller)) {
181 return;
182 }
183
184 $this->update(GlobalDataKeys::UI_CONTROLLER, $controller);
185
186 $this->global_ui_controller = null;
187 }
188
189 /**
190 * Get global UI saved data
191 *
192 * @return array
193 */
194 public function get_global_ui_saved_data()
195 {
196 if (!is_null($this->global_ui_saved_data)) {
197 return $this->global_ui_saved_data;
198 }
199
200 $saved_data = $this->get(GlobalDataKeys::UI_SAVED_DATA) ?? [];
201
202 $saved_data['variableData'] = $this->get_variable_data($saved_data);
203
204 return $this->global_ui_saved_data = $saved_data;
205 }
206
207 /**
208 * Update global UI saved data
209 *
210 * @param array $fonts
211 */
212 public function update_global_ui_saved_data($fonts)
213 {
214 if (!is_array($fonts)) {
215 return;
216 }
217
218 $this->update(GlobalDataKeys::UI_SAVED_DATA, $fonts);
219
220 $this->global_ui_saved_data = null;
221 }
222
223 /**
224 * Get global custom fonts data
225 *
226 * @return array
227 */
228 public function get_global_custom_fonts()
229 {
230 if (!is_null($this->global_custom_fonts)) {
231 return $this->global_custom_fonts;
232 }
233
234 return $this->global_custom_fonts = $this->get(GlobalDataKeys::CUSTOM_FONTS) ?? [];
235 }
236
237 /**
238 * Update global custom fonts data
239 *
240 * @param array $data
241 */
242 public function update_global_custom_fonts($data)
243 {
244 if (!is_array($data)) {
245 return;
246 }
247
248 $this->update(GlobalDataKeys::CUSTOM_FONTS, $data);
249
250 $this->global_custom_fonts = null;
251 }
252
253 /**
254 * Get variable data
255 *
256 * @param array|null $saved_data, if pass null will get $saved_data otherwise will use $saved_data
257 *
258 * @return array
259 */
260 public function get_variable_data($saved_data = null) {
261 $saved_data = is_array($saved_data) ? $saved_data : ($this->get(GlobalDataKeys::UI_SAVED_DATA) ?? []);
262
263 $variables = $saved_data && !empty($saved_data['variableData']['data'])
264 ? $this->normalize_variable_data($saved_data['variableData'])
265 : $this->initial_variable_data();
266
267 $variables = $this->normalize_old_variable_data($variables);
268
269 $variables = $this->sort_variable_data($variables);
270
271 return $variables;
272 }
273
274 /**
275 * Normalize old variable data
276 *
277 * @param array $variables
278 *
279 * @return array
280 */
281 protected function normalize_old_variable_data($variables) {
282 // Check if data needs normalization
283 if (!isset($variables['data']) || !is_array($variables['data'])) {
284 return $variables;
285 }
286
287 // Check if already normalized (has the 4 standard groups with correct keys)
288 $expected_keys = ['color', 'size', 'text-style', 'font-family'];
289 $actual_keys = array_column($variables['data'], 'key');
290
291 if (count($actual_keys) === 4 && empty(array_diff($expected_keys, $actual_keys))) {
292 // Already normalized, return as-is
293 return $variables;
294 }
295
296 // Create fresh groups
297 $normalized = [
298 'data' => [
299 [
300 'title' => 'Colors',
301 'key' => 'color',
302 'modes' => [],
303 'variables' => [],
304 ],
305 [
306 'title' => 'Numbers',
307 'key' => 'size',
308 'modes' => [],
309 'variables' => [],
310 ],
311 [
312 'title' => 'Text Styles',
313 'key' => 'text-style',
314 'modes' => [],
315 'variables' => [],
316 ],
317 [
318 'title' => 'Font Family',
319 'key' => 'font-family',
320 'modes' => [],
321 'variables' => [],
322 ],
323 ],
324 ];
325
326 // Index for quick access
327 $groups = [];
328
329 foreach ($normalized['data'] as $index => &$group) {
330 $groups[$group['key']] = &$normalized['data'][$index];
331 }
332
333 unset($group);
334
335 // Track modes per type
336 $modes_per_type = [
337 'color' => [],
338 'size' => [],
339 'text-style' => [],
340 'font-family' => [],
341 ];
342
343 // Single pass: process all variables
344 foreach ($variables['data'] as $section) {
345 if (empty($section['variables'])) {
346 continue;
347 }
348
349 foreach ($section['variables'] as $variable) {
350 // Determine correct group based on variable's type
351 $var_type = isset($variable['type']) ? $variable['type'] : null;
352
353 if (!$var_type || !isset($groups[$var_type])) {
354 continue;
355 }
356
357 // Add variable to correct group (move if in wrong section)
358 $groups[$var_type]['variables'][] = $variable;
359
360 // Collect modes for this type
361 if (isset($variable['value']) && is_array($variable['value'])) {
362 foreach (array_keys($variable['value']) as $mode_key) {
363 if (!isset($modes_per_type[$var_type][$mode_key])) {
364 $modes_per_type[ $var_type ][ $mode_key ] = [
365 'key' => $mode_key,
366 'title' => ucfirst($mode_key),
367 ];
368 }
369 }
370 }
371 }
372 }
373
374 // Assign collected modes to groups
375 foreach ($modes_per_type as $type => $modes) {
376 if (isset($groups[$type])) {
377 $groups[$type]['modes'] = array_values($modes);
378
379 // Ensure default mode exists
380 if (empty($groups[$type]['modes'])) {
381 $groups[$type]['modes'][] = [
382 'key' => 'default',
383 'title' => 'Default',
384 ];
385 }
386 }
387 }
388
389 return $normalized;
390 }
391
392 /**
393 * Normalize variable data
394 *
395 * @param array $raw_data
396 *
397 * @return array
398 *
399 * @todo: need to refactor this method, copied from \Kirki\Ajax\UserData::normalize_variable_data()
400 */
401 public function normalize_variable_data( $raw_data ) {
402 if(
403 isset($raw_data['data'])
404 && is_array($raw_data['data'])
405 && count($raw_data['data']) > 0
406 && isset($raw_data['data'][0]['key'])
407 ) {
408 return $raw_data; // thats means already normalized.
409 }
410
411 // Start from clean base
412 $organized = self::initial_variable_data();
413
414 // Index groups by key for fast access
415 $groups = [];
416
417 foreach ($organized['data'] as $index => $group) {
418 $groups[$group['key'] ] = &$organized['data'][$index];
419 }
420
421 foreach ($raw_data['data'] as $section) {
422
423 /* ---------------------------------
424 * Merge modes (unique by key)
425 * --------------------------------- */
426 if (!empty($section['modes'])) {
427 foreach ($groups as &$group) {
428 $mode_index = [];
429
430 foreach ($group['modes'] as $existing_mode) {
431 if (isset($existing_mode['key'])) {
432 $mode_index[$existing_mode['key']] = true;
433 }
434 }
435
436 foreach ($section['modes'] as $mode) {
437 if (isset($mode['key']) && !isset($mode_index[$mode['key']])) {
438 $group['modes'][] = $mode;
439 $mode_index[$mode['key']] = true;
440 }
441 }
442 }
443 }
444
445 /* ---------------------------------
446 * Organize variables
447 * --------------------------------- */
448 if (empty($section['variables'])) {
449 continue;
450 }
451
452 foreach ($section['variables'] as $variable) {
453
454 if (empty($variable['type'])) {
455 continue;
456 }
457
458 $group_key = $variable['type'];
459
460 if (!isset($groups[$group_key])) {
461 continue;
462 }
463
464 $found = false;
465
466 foreach ($groups[$group_key]['variables'] as &$existing) {
467
468 if (isset($existing['id'], $variable['id']) && $existing['id'] === $variable['id']) {
469
470 // Merge values by mode
471 $existing['value'] = array_merge(
472 $existing['value'] ?? [],
473 $variable['value'] ?? []
474 );
475
476 $found = true;
477 break;
478 }
479 }
480
481 if (!$found) {
482 $groups[$group_key]['variables'][] = $variable;
483 }
484 }
485 }
486
487 return $organized;
488 }
489
490 /**
491 * Initial variable data
492 *
493 * @return array
494 */
495 protected function initial_variable_data() {
496 return [
497 'data' => [
498 [
499 'title' => 'Colors',
500 'key' => 'color',
501 'modes' => [
502 [
503 'title' => 'Default',
504 'key' => 'default',
505 ],
506 ],
507 'variables' => [],
508 ],
509 [
510 'title' => 'Numbers',
511 'key' => 'size',
512 'modes' => [
513 [
514 'title' => 'Default',
515 'key' => 'default',
516 ],
517 ],
518 'variables' => [],
519 ],
520 [
521 'title' => 'Text Styles',
522 'key' => 'text-style',
523 'modes' => [
524 [
525 'title' => 'Default',
526 'key' => 'default',
527 ],
528 ],
529 'variables' => [],
530 ],
531 [
532 'title' => 'Font Family',
533 'key' => 'font-family',
534 'modes' => [
535 [
536 'title' => 'Default',
537 'key' => 'default',
538 ],
539 ],
540 'variables' => [],
541 ],
542 ],
543
544 ];
545 }
546
547 /**
548 * Sort variable data
549 *
550 * @param array $variable_data
551 *
552 * @return array
553 */
554 public function sort_variable_data($variable_data) {
555 if (empty($variable_data) || !is_array($variable_data)) {
556 return $variable_data;
557 }
558
559 // Enforce deterministic group order: Color, Number, Text style, Font family
560 $order_map = [
561 'color' => 0,
562 'size' => 1,
563 'text-style' => 2,
564 'font-family' => 3,
565 ];
566
567 usort(
568 $variable_data['data'],
569 function ($first, $second) use ($order_map) {
570 $first_order = isset($order_map[$first['key']]) ? $order_map[$first['key']] : 99;
571 $second_order = isset($order_map[$second['key']]) ? $order_map[$second['key']] : 99;
572 return $first_order - $second_order;
573 }
574 );
575
576 return $variable_data;
577 }
578
579 /**
580 * Get View port list.
581 * this method only for front end. not for editor. cause list data not completed data.
582 *
583 * @return array
584 */
585 public function get_view_port_list() {
586 $control = $this->get_global_ui_controller();
587
588 if (empty($control) || !isset($control['viewport'], $control['viewport']['list'])) {
589 return $this->sort_viewport_list($this->get_initial_viewports()['list']);
590 }
591
592 return $this->sort_viewport_list((array) $control['viewport']['list']);
593 }
594
595 /**
596 * Sort view port list
597 *
598 * @param array $viewport_list list of view port list.
599 * @return array
600 */
601 protected function sort_viewport_list($viewport_list) {
602 $viewport_list = $viewport_list;
603 $list = [];
604 uasort($viewport_list, static function ($first, $second) {
605 return ($first['minWidth'] ?? 0) <=> ($second['minWidth'] ?? 0);
606 });
607 $passed_md = false;
608
609 foreach ($viewport_list as $key => $value) {
610 if ($passed_md) {
611 $list[$key] = $value;
612 $list[$key]['type'] = 'min';
613 $list[$key]['id'] = $key;
614 }
615
616 if ($key === 'md') {
617 $passed_md = true;
618 $list[$key] = $value;
619 $list[$key]['id'] = $key;
620 $list[$key]['type'] = 'max';
621 }
622 }
623 $passed_md = false;
624
625 foreach ( array_reverse( $viewport_list ) as $key => $value ) {
626 if ($passed_md) {
627 $list[$key] = $value;
628 $list[$key]['type'] = 'max';
629 $list[$key]['id'] = $key;
630 }
631
632 if ($key === 'md') {
633 $passed_md = true;
634 }
635 }
636 return $list;
637 }
638
639 /**
640 * Get initial view port list.
641 *
642 * @return array
643 */
644 public function get_initial_viewports()
645 {
646 return [
647 'active' => 'md',
648 'scale' => 1,
649 'zoom' => 1,
650 'width' => 1200,
651 'mdWidth' => '',
652 'defaults' => [
653 'md',
654 'tablet',
655 'mobileLandscape',
656 'mobile',
657 ],
658 'list' => [
659 'md' => [
660 'value' => 1200,
661 'scale' => 1,
662 'minWidth' => 1200,
663 'maxWidth' => 1200,
664 'title' => 'Desktop',
665 'icon' => 'desktop',
666 'activeIcon' => 'desktop-hover',
667 ],
668 'tablet' => [
669 'value' => 991,
670 'scale' => 1,
671 'minWidth' => 991,
672 'maxWidth' => 991,
673 'title' => 'Tablet',
674 'icon' => 'tablet-default',
675 'activeIcon' => 'tablet-hover',
676 ],
677 'mobileLandscape' => [
678 'value' => 767,
679 'scale' => 1,
680 'minWidth' => 767,
681 'maxWidth' => 767,
682 'title' => 'Landscape',
683 'icon' => 'phone-hr-default',
684 'activeIcon' => 'phone-hr-hover',
685 ],
686 'mobile' => [
687 'value' => 575,
688 'scale' => 1,
689 'minWidth' => 575,
690 'maxWidth' => 575,
691 'title' => 'Mobile',
692 'icon' => 'phone-vr-default',
693 'activeIcon' => 'phone-vr-hover',
694 ],
695 ],
696 ];
697 }
698 }