PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / 3.2.3
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits v3.2.3
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-shortcode-manager.php

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

287 lines 9.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Master Addons Widget Builder - Shortcode Manager
4 * Registers all custom widgets as shortcodes
5 *
6 * @package MasterAddons
7 * @subpackage WidgetBuilder
8 */
9
10 namespace MasterAddons\Inc\Admin\WidgetBuilder;
11
12 if (!defined('ABSPATH')) {
13 exit;
14 }
15
16 class Shortcode_Manager {
17
18 private static $instance = null;
19 private $widgets = [];
20
21 public static function get_instance() {
22 if (self::$instance === null) {
23 self::$instance = new self();
24 }
25 return self::$instance;
26 }
27
28 public function __construct() {
29 add_action('init', [$this, 'register_shortcodes'], 20);
30 }
31
32 /**
33 * Register all widget shortcodes
34 */
35 public function register_shortcodes() {
36 // Get all published widgets
37 $args = [
38 'post_type' => 'jltma_widget',
39 'post_status' => 'publish',
40 'posts_per_page' => -1,
41 'orderby' => 'date',
42 'order' => 'DESC'
43 ];
44
45 $widgets = get_posts($args);
46
47 if (empty($widgets)) {
48 return;
49 }
50
51 foreach ($widgets as $widget_post) {
52 $widget_id = $widget_post->ID;
53 $shortcode_tag = 'jltma_widget_' . $widget_id;
54
55 // Register shortcode with closure that captures widget ID
56 add_shortcode($shortcode_tag, function($atts, $content = null) use ($widget_id) {
57 return $this->render_widget_shortcode($atts, $content, $widget_id);
58 });
59
60 // Store widget info
61 $this->widgets[$shortcode_tag] = [
62 'id' => $widget_id,
63 'title' => $widget_post->post_title,
64 'tag' => $shortcode_tag
65 ];
66 }
67 }
68
69 /**
70 * Render widget shortcode
71 *
72 * @param array $atts Shortcode attributes
73 * @param string $content Shortcode content
74 * @param int $widget_id Widget post ID
75 * @return string Widget output
76 */
77 public function render_widget_shortcode($atts, $content = null, $widget_id = 0) {
78 if (!$widget_id) {
79 return '';
80 }
81
82 // Rendered from post meta through the shared template engine, the same
83 // way Dynamic_Widget renders in Elementor. The old path required a
84 // generated uploads/master_addons/widgets/{id}/widget.php, which
85 // Widget_Generator stopped writing when rendering moved to runtime — so
86 // every shortcode silently returned an empty string.
87 $data = $this->read_widget_meta($widget_id, '_jltma_widget_data');
88 $html = isset($data['html_code']) ? (string) $data['html_code'] : '';
89
90 if ('' === trim($html)) {
91 return '';
92 }
93
94 try {
95 $this->enqueue_widget_assets($widget_id);
96
97 $settings = $this->prepare_settings($widget_id, $atts);
98 $engine = new Widget_Template_Engine($this->collect_control_types($widget_id));
99
100 $output = '<div class="jltma-widget-shortcode jltma-wb-' . esc_attr($widget_id) . '">';
101
102 // Custom CSS/JS output, matching Dynamic_Widget::render(): the stored
103 // bodies are sanitized on save and placeholder substitution is escaped
104 // per output, so only the <style>/<script> wrappers are added here.
105 $css = isset($data['css_code']) ? (string) $data['css_code'] : '';
106 if ('' !== trim($css)) {
107 $css_rendered = $engine->render($css, $settings);
108 if ('' !== trim($css_rendered)) {
109 $output .= '<style>' . $css_rendered . '</style>';
110 }
111 }
112
113 $output .= $engine->render($html, $settings);
114
115 // Custom JS goes to the footer, not into the returned markup: the
116 // shortcode output passes through the_content, where convert_chars()
117 // would turn every `&` inside a <script> into `&#038;`. In the editor
118 // it stays inline so it re-runs when the canvas re-renders — same
119 // split as Dynamic_Widget::render().
120 $js = isset($data['js_code']) ? (string) $data['js_code'] : '';
121 if ('' !== trim($js)) {
122 $js_rendered = $engine->render($js, $settings);
123 if ('' !== trim($js_rendered)) {
124 if (Widget_Builder_Init::is_editor_context()) {
125 $output .= '<script>' . $js_rendered . '</script>';
126 } else {
127 Widget_Builder_Init::enqueue_inline_js($js_rendered, $widget_id);
128 }
129 }
130 }
131
132 return $output . '</div>';
133 } catch (\Exception $e) {
134 if (current_user_can('manage_options')) {
135 return '<div class="jltma-shortcode-error">' . esc_html($e->getMessage()) . '</div>';
136 }
137 return '';
138 }
139 }
140
141 /**
142 * Read widget meta as an array.
143 *
144 * The REST/admin save paths store arrays; the demo importer stores the same
145 * keys JSON-encoded. Accept either.
146 *
147 * @param int $widget_id
148 * @param string $key
149 * @return array
150 */
151 private function read_widget_meta($widget_id, $key) {
152 $value = get_post_meta($widget_id, $key, true);
153
154 if (is_array($value)) {
155 return $value;
156 }
157
158 if (is_string($value) && '' !== $value) {
159 $decoded = json_decode($value, true);
160 if (is_array($decoded)) {
161 return $decoded;
162 }
163 }
164
165 return [];
166 }
167
168 /**
169 * Map control name => type, so the engine escapes each value correctly.
170 *
171 * @param int $widget_id
172 * @return array
173 */
174 private function collect_control_types($widget_id) {
175 $sections = $this->read_widget_meta($widget_id, '_jltma_widget_sections');
176 $types = [];
177
178 foreach ($sections as $section) {
179 if (!is_array($section)) {
180 continue;
181 }
182
183 $controls = !empty($section['controls'])
184 ? $section['controls']
185 : (!empty($section['fields']) ? $section['fields'] : []);
186
187 foreach ((array) $controls as $control) {
188 if (empty($control['name'])) {
189 continue;
190 }
191
192 $types[$control['name']] = $control['type'] ?? 'text';
193
194 if (!empty($control['popover_fields']) && is_array($control['popover_fields'])) {
195 foreach ($control['popover_fields'] as $pf) {
196 if (!empty($pf['name'])) {
197 $types[$pf['name']] = $pf['type'] ?? 'text';
198 }
199 }
200 }
201 }
202 }
203
204 return $types;
205 }
206
207 /**
208 * Enqueue the external libraries a widget declares.
209 *
210 * The widget's own CSS/JS is rendered inline by render_widget_shortcode();
211 * nothing is read from disk, since no per-widget assets are generated.
212 *
213 * @param int $widget_id Widget ID
214 */
215 private function enqueue_widget_assets($widget_id) {
216 // Premium-gated, normalised and URL-validated; empty on free builds.
217 $includes = Widget_Builder_Init::get_widget_includes($widget_id);
218
219 foreach ($includes['css_libraries'] as $css) {
220 wp_enqueue_style($css['handle'], $css['src'], $css['dependencies'], JLTMA_VER);
221 }
222
223 foreach ($includes['js_libraries'] as $js) {
224 wp_enqueue_script($js['handle'], $js['src'], $js['dependencies'], JLTMA_VER, true);
225 }
226 }
227
228 /**
229 * Prepare settings from shortcode attributes
230 *
231 * @param int $widget_id Widget ID
232 * @param array $atts Shortcode attributes
233 * @return array Settings array
234 */
235 private function prepare_settings($widget_id, $atts) {
236 // Get widget sections/controls from meta
237 $sections = $this->read_widget_meta($widget_id, '_jltma_widget_sections');
238 $settings = [];
239 $atts = is_array($atts) ? $atts : [];
240
241 foreach ($sections as $section) {
242 if (!is_array($section)) {
243 continue;
244 }
245
246 $controls = !empty($section['controls'])
247 ? $section['controls']
248 : (!empty($section['fields']) ? $section['fields'] : []);
249
250 foreach ((array) $controls as $control) {
251 if (empty($control['name'])) {
252 continue;
253 }
254
255 $control_name = $control['name'];
256
257 if (isset($atts[$control_name])) {
258 $settings[$control_name] = $atts[$control_name];
259 } elseif (isset($control['default'])) {
260 $settings[$control_name] = $control['default'];
261 }
262 }
263 }
264
265 return $settings;
266 }
267
268 /**
269 * Get all registered shortcodes
270 *
271 * @return array
272 */
273 public function get_registered_shortcodes() {
274 return $this->widgets;
275 }
276
277 /**
278 * Get shortcode tag for widget
279 *
280 * @param int $widget_id Widget ID
281 * @return string Shortcode tag
282 */
283 public static function get_shortcode_tag($widget_id) {
284 return 'jltma_widget_' . $widget_id;
285 }
286 }
287