PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / 3.1.2
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits v3.1.2
3.2.2 3.2.3 3.2.1 3.2.0 3.1.9 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.9 trunk 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.3 1.1.4 1.1.5 All 174 releases
master-addons / inc / admin / widget-builder / class-control-manager.php

class-control-manager.php in Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits 3.1.2, at inc/admin/widget-builder/class-control-manager.php

244 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace MasterAddons\Inc\Admin\WidgetBuilder;
3
4 defined('ABSPATH') || exit;
5
6 /**
7 * Control Manager
8 * Loads and manages all control type builders
9 */
10 class Control_Manager {
11
12 private static $instance = null;
13 private $controls = [];
14
15 public static function get_instance() {
16 if (self::$instance === null) {
17 self::$instance = new self();
18 }
19 return self::$instance;
20 }
21
22 public function __construct() {
23 $this->load_controls();
24 }
25
26 /**
27 * Load all control classes
28 */
29 private function load_controls() {
30 // Load base class first
31 require_once __DIR__ . '/controls/class-control-base.php';
32
33 // Load FREE control type classes only
34 $control_files = [
35 'color',
36 'heading',
37 'hidden',
38 'text',
39 'textarea',
40 'url',
41 ];
42
43 foreach ($control_files as $file) {
44 $file_path = __DIR__ . '/controls/' . $file . '.php';
45 if (file_exists($file_path)) {
46 require_once $file_path;
47 }
48 }
49
50 // Allow Pro to load additional controls
51 do_action('jltma_widget_builder_load_controls');
52 }
53
54 /**
55 * Get control builder instance by type
56 *
57 * @param string $type Control type (TEXT, TEXTAREA, etc.) - can be uppercase or lowercase
58 * @return Controls\Control_Base|null
59 */
60 public function get_control($type) {
61 // Convert to uppercase for consistency (React UI sends lowercase, but we need uppercase for mapping)
62 $type = strtoupper($type);
63
64 // Return cached instance if exists
65 if (isset($this->controls[$type])) {
66 return $this->controls[$type];
67 }
68
69 // Map control types to classes (FREE controls only)
70 $class_map = [
71 'COLOR' => 'Color',
72 'HEADING' => 'Heading',
73 'HIDDEN' => 'Hidden',
74 'TEXT' => 'Text',
75 'TEXTAREA' => 'Textarea',
76 'URL' => 'Url',
77 ];
78
79 // Allow Pro to add additional control class mappings
80 $class_map = apply_filters('jltma_widget_builder_control_class_map', $class_map);
81
82 if (!isset($class_map[$type])) {
83 return null;
84 }
85
86 // Check if class_map value is a full class name (from Pro) or just class name (free)
87 if (strpos($class_map[$type], '\\') !== false) {
88 $class_name = $class_map[$type];
89 } else {
90 $class_name = '\\MasterAddons\\Inc\\Admin\\WidgetBuilder\\Controls\\' . $class_map[$type];
91 }
92
93 if (!class_exists($class_name)) {
94 return null;
95 }
96
97 // Create and cache instance
98 $this->controls[$type] = new $class_name();
99
100 return $this->controls[$type];
101 }
102
103 /**
104 * Build control output
105 *
106 * @param string $control_key Control key
107 * @param array $field Field configuration
108 * @param string $type Control type
109 * @return string
110 */
111 public function build_control($control_key, $field, $type) {
112 $control = $this->get_control($type);
113
114 if (!$control) {
115 // Fallback for unknown control types
116 return $this->build_fallback_control($control_key, $field, $type);
117 }
118
119 return $control->build($control_key, $field);
120 }
121
122 /**
123 * Build control config ARRAY (runtime equivalent of build_control()).
124 * Consumed by Dynamic_Widget::register_controls() so no PHP file is generated.
125 *
126 * @param string $control_key Control key
127 * @param array $field Field configuration
128 * @param string $type Control type
129 * @return array ['key' => string, 'responsive' => bool, 'args' => array]
130 */
131 public function build_control_config($control_key, $field, $type) {
132 $control = $this->get_control($type);
133
134 if (!$control || !method_exists($control, 'get_config')) {
135 return $this->build_fallback_control_config($control_key, $field, $type);
136 }
137
138 return $control->get_config($control_key, $field);
139 }
140
141 /**
142 * Fallback control config for unknown types (array equivalent of build_fallback_control()).
143 *
144 * @param string $control_key Control key
145 * @param array $field Field configuration
146 * @param string $type Control type
147 * @return array
148 */
149 private function build_fallback_control_config($control_key, $field, $type) {
150 $type_const = strtoupper(preg_replace('/[^A-Za-z0-9_]/', '', (string) $type));
151 if ('' === $type_const) {
152 $type_const = 'TEXT';
153 }
154
155 $const = '\\Elementor\\Controls_Manager::' . $type_const;
156 $args = [
157 // translators: dynamic user-defined control label.
158 'label' => esc_html(!empty($field['label']) ? $field['label'] : 'Control'),
159 'type' => defined($const) ? constant($const) : 'text',
160 ];
161
162 // REPEATER must always carry a 'fields' array.
163 if ('REPEATER' === $type_const) {
164 $args['fields'] = [];
165 }
166
167 if (isset($field['default'])) {
168 $args['default'] = $field['default'];
169 }
170
171 return [
172 'key' => $control_key,
173 'responsive' => !empty($field['responsive']),
174 'args' => $args,
175 ];
176 }
177
178 /**
179 * Build fallback control for unknown types
180 *
181 * @param string $control_key Control key
182 * @param array $field Field configuration
183 * @param string $type Control type
184 * @return string
185 */
186 private function build_fallback_control($control_key, $field, $type) {
187 $label = !empty($field['label']) ? esc_js($field['label']) : 'Control';
188
189 // Elementor control constants are upper-case (e.g. REPEATER, SELECT). Normalise
190 // the type to a safe constant name so the generated PHP is always valid.
191 $type_const = strtoupper(preg_replace('/[^A-Za-z0-9_]/', '', (string) $type));
192 if ('' === $type_const) {
193 $type_const = 'TEXT';
194 }
195
196 $content = "\t\t\$this->add_control(\n";
197 $content .= "\t\t\t'{$control_key}',\n";
198 $content .= "\t\t\t[\n";
199 $content .= "\t\t\t\t'label' => esc_html__('{$label}', 'master-addons'),\n";
200 $content .= "\t\t\t\t'type' => Controls_Manager::{$type_const},\n";
201
202 // REPEATER must always carry a 'fields' array, otherwise Elementor's
203 // sanitize_settings() throws a TypeError when iterating null fields.
204 if ('REPEATER' === $type_const) {
205 $content .= "\t\t\t\t'fields' => array(),\n";
206 }
207
208 if (isset($field['default'])) {
209 $default = $this->export_default_value($field['default']);
210 $content .= "\t\t\t\t'default' => {$default},\n";
211 }
212
213 $content .= "\t\t\t]\n";
214 $content .= "\t\t);\n\n";
215
216 return $content;
217 }
218
219 /**
220 * Convert a control default value into a valid PHP literal for the generated file.
221 * Arrays (e.g. repeater/group-control defaults) must never be interpolated directly,
222 * which would emit the literal token "Array" and produce a fatal parse error.
223 *
224 * @param mixed $value
225 * @return string
226 */
227 private function export_default_value($value) {
228 if (is_array($value)) {
229 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export -- generating PHP source, not debug output
230 return var_export($value, true);
231 }
232 if (is_bool($value)) {
233 return $value ? 'true' : 'false';
234 }
235 if (is_int($value) || is_float($value)) {
236 return (string) $value;
237 }
238 if (null === $value) {
239 return "''";
240 }
241 return "'" . esc_js((string) $value) . "'";
242 }
243 }
244