PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.8
Booking for Appointments and Events Calendar – Amelia v2.4.8
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Infrastructure / WP / Elementor / AmeliaStepBookingButtonElementorWidget.php
ameliabooking / src / Infrastructure / WP / Elementor Last commit date
AmeliaBookingElementorWidget.php 1 week ago AmeliaCatalogBookingElementorWidget.php 1 week ago AmeliaCatalogElementorWidget.php 1 week ago AmeliaElementorWhiteLabelHelper.php 1 week ago AmeliaEventsCalendarBookingElementorWidget.php 1 week ago AmeliaEventsElementorWidget.php 1 week ago AmeliaEventsListBookingButtonElementorWidget.php 1 week ago AmeliaEventsListBookingElementorWidget.php 1 week ago AmeliaStepBookingButtonElementorWidget.php 1 week ago AmeliaStepBookingElementorWidget.php 1 week ago ElementorBlock.php 1 week ago ElementorSharedShortcodeWidget.php 2 months ago
AmeliaStepBookingButtonElementorWidget.php
356 lines
1 <?php
2
3 /**
4 * @copyright © Melograno Ventures. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace Elementor;
9
10 use AmeliaBooking\Infrastructure\WP\Translations\BackendStrings;
11
12 /**
13 * Class AmeliaStepBookingButtonElementorWidget
14 *
15 * Extends the native Elementor Button widget so all standard button controls
16 * (Type, Text, Link, Size, Icon, Icon Position, Icon Spacing and the full
17 * Style tab) are inherited for free. The Amelia booking options (preselect
18 * parameters, layout, manual trigger, in-dialog) are added in a dedicated
19 * section after the parent controls.
20 *
21 * @package AmeliaBooking\Infrastructure\WP\Elementor
22 */
23 class AmeliaStepBookingButtonElementorWidget extends Widget_Button
24 {
25 // -------------------------------------------------------------------------
26 // Identity
27 // -------------------------------------------------------------------------
28
29 public function get_name()
30 {
31 return 'stepbookingbutton';
32 }
33
34 public function get_title()
35 {
36 $widgetLabel = BackendStrings::get('step_booking_button_gutenberg_block');
37
38 $title = is_array($widgetLabel) ? $widgetLabel['title'] : BackendStrings::get('step_booking');
39
40 return AmeliaElementorWhiteLabelHelper::label($title);
41 }
42
43 public function get_icon()
44 {
45 return AmeliaElementorWhiteLabelHelper::icon();
46 }
47
48 public function get_categories()
49 {
50 return ['amelia-elementor'];
51 }
52
53 // -------------------------------------------------------------------------
54 // Controls
55 // -------------------------------------------------------------------------
56
57 protected function register_controls()
58 {
59 // ── All standard Elementor Button widget controls ──────────────────────
60 // Type, Text, Size, Selected Icon, Icon Position, Icon Spacing
61 // + full Style tab (Typography, colours, Border, Radius, Shadow, Padding)
62 parent::register_controls();
63
64 // Set default button text to "Book Appointment"
65 $this->update_control(
66 'text',
67 [
68 'default' => BackendStrings::get('book_appointment'),
69 ]
70 );
71
72 // Remove controls that are irrelevant for the Amelia booking button:
73 // the booking is opened via an Amelia shortcode trigger, not a URL link,
74 // and the trigger ID is managed internally so a manual Button ID is
75 // confusing and unused.
76 $this->remove_control('link');
77 $this->remove_control('button_css_id');
78
79 // ── Amelia booking options ─────────────────────────────────────────────
80 $controls_data = AmeliaStepBookingElementorWidget::amelia_elementor_get_data();
81
82 $this->start_controls_section(
83 'amelia_booking_options',
84 [
85 'label' => AmeliaElementorWhiteLabelHelper::label(esc_html__('Amelia Booking Options', 'wpamelia')),
86 ]
87 );
88
89 // Preselect toggle
90 $this->add_control(
91 'preselect',
92 [
93 'label' => BackendStrings::get('filter'),
94 'type' => Controls_Manager::SWITCHER,
95 'default' => false,
96 'label_on' => BackendStrings::get('yes'),
97 'label_off' => BackendStrings::get('no'),
98 ]
99 );
100
101 // Category
102 if ($controls_data['categories'] && sizeof($controls_data['categories']) > 1) {
103 $this->add_control(
104 'select_category',
105 [
106 'label' => BackendStrings::get('select_category'),
107 'type' => Controls_Manager::SELECT2,
108 'multiple' => true,
109 'options' => $controls_data['categories'],
110 'condition' => ['preselect' => 'yes'],
111 'placeholder' => BackendStrings::get('show_all_categories'),
112 ]
113 );
114 }
115
116 // Service
117 if ($controls_data['services'] && sizeof($controls_data['services']) > 1) {
118 $this->add_control(
119 'select_service',
120 [
121 'label' => BackendStrings::get('select_service'),
122 'type' => Controls_Manager::SELECT2,
123 'multiple' => true,
124 'options' => $controls_data['services'],
125 'condition' => ['preselect' => 'yes'],
126 'placeholder' => BackendStrings::get('show_all_services'),
127 ]
128 );
129 }
130
131 // Employee
132 if ($controls_data['employees'] && sizeof($controls_data['employees']) > 1) {
133 $this->add_control(
134 'select_employee',
135 [
136 'label' => BackendStrings::get('select_employee'),
137 'type' => Controls_Manager::SELECT2,
138 'multiple' => true,
139 'options' => $controls_data['employees'],
140 'condition' => ['preselect' => 'yes'],
141 'placeholder' => BackendStrings::get('show_all_employees'),
142 ]
143 );
144 }
145
146 // Location
147 if ($controls_data['locations'] && sizeof($controls_data['locations']) > 1) {
148 $this->add_control(
149 'select_location',
150 [
151 'label' => BackendStrings::get('select_location'),
152 'type' => Controls_Manager::SELECT2,
153 'multiple' => true,
154 'options' => $controls_data['locations'],
155 'condition' => ['preselect' => 'yes'],
156 'placeholder' => BackendStrings::get('show_all_locations'),
157 ]
158 );
159 }
160
161 // Package
162 if (!empty($controls_data['packages'])) {
163 $this->add_control(
164 'select_package',
165 [
166 'label' => BackendStrings::get('select_package'),
167 'type' => Controls_Manager::SELECT2,
168 'multiple' => true,
169 'options' => $controls_data['packages'],
170 'condition' => ['preselect' => 'yes'],
171 'placeholder' => BackendStrings::get('show_all_packages'),
172 ]
173 );
174 }
175
176 // Show
177 if (!empty($controls_data['show'])) {
178 $this->add_control(
179 'select_show',
180 [
181 'label' => BackendStrings::get('show_all'),
182 'type' => Controls_Manager::SELECT,
183 'options' => $controls_data['show'],
184 'condition' => ['preselect' => 'yes'],
185 'default' => '',
186 ]
187 );
188 }
189
190 // Layout
191 $this->add_control(
192 'layout',
193 [
194 'label' => BackendStrings::get('layout_select_label'),
195 'type' => Controls_Manager::SELECT,
196 'description' => BackendStrings::get('layout_description'),
197 'options' => $controls_data['layout_options'],
198 'default' => '1',
199 ]
200 );
201
202 $this->end_controls_section();
203 }
204
205 // -------------------------------------------------------------------------
206 // Render
207 // -------------------------------------------------------------------------
208
209 protected function render()
210 {
211 $settings = $this->get_settings_for_display();
212
213 // ── Build trigger value ────────────────────────────────────────────────
214 $autoTrigger = 'amelia-step-booking-btn-' . substr(md5($this->get_id()), 0, 8);
215
216 // ── Build shortcode ────────────────────────────────────────────────────
217 $shortcode = '[' . AmeliaElementorWhiteLabelHelper::shortcodeTag('stepbooking', 'ameliastepbooking');
218 $shortcode .= ' trigger=' . $autoTrigger;
219 $shortcode .= ' trigger_type=id';
220 $shortcode .= ' in_dialog=1';
221
222 $toCsvIds = static function ($value): string {
223 $vals = is_array($value) ? $value : [$value];
224 $vals = array_filter(array_map('absint', $vals));
225 return implode(',', $vals);
226 };
227
228 if (!empty($settings['preselect'])) {
229 if (!empty($settings['select_show'])) {
230 $shortcode .= ' show=' . sanitize_key((string) $settings['select_show']);
231 }
232
233 if (!empty($settings['select_service'])) {
234 $service = $toCsvIds($settings['select_service']);
235 if ($service !== '') {
236 $shortcode .= ' service=' . $service;
237 }
238 } elseif (!empty($settings['select_category'])) {
239 $category = $toCsvIds($settings['select_category']);
240 if ($category !== '') {
241 $shortcode .= ' category=' . $category;
242 }
243 }
244
245 if (!empty($settings['select_employee'])) {
246 $employee = $toCsvIds($settings['select_employee']);
247 if ($employee !== '') {
248 $shortcode .= ' employee=' . $employee;
249 }
250 }
251
252 if (!empty($settings['select_location'])) {
253 $location = $toCsvIds($settings['select_location']);
254 if ($location !== '') {
255 $shortcode .= ' location=' . $location;
256 }
257 }
258
259 if (!empty($settings['select_package'])) {
260 $package = $toCsvIds($settings['select_package']);
261 if ($package !== '') {
262 $shortcode .= ' package=' . $package;
263 }
264 }
265 }
266
267 if (!empty($settings['layout'])) {
268 $shortcode .= ' layout=' . absint($settings['layout']);
269 }
270
271 $shortcode .= ']';
272
273 // ── Trigger wrapper ────────────────────────────────────────────────────
274 echo '<div class="amelia-step-booking-button-trigger">';
275
276 // ── Capture parent button output and inject the ID ────────────────────
277 ob_start();
278 parent::render();
279 $button_html = ob_get_clean();
280
281 // Remove any existing id attribute before injecting the trigger ID
282 $button_html = preg_replace(
283 '/<a\s+([^>]*?)\s*id="[^"]*"\s*([^>]*?)class="([^"]*elementor-button[^"]*)"/',
284 '<a $1$2class="$3"',
285 $button_html
286 );
287
288 // Inject trigger ID and enforce cursor pointer style on the button link.
289 $button_html = preg_replace_callback(
290 '/<a\s+([^>]*?)class="([^"]*elementor-button[^"]*)"([^>]*)>/',
291 static function ($matches) use ($autoTrigger) {
292 $beforeClass = $matches[1];
293 $afterClass = $matches[3];
294
295 $appendCursor = static function ($styleValue) {
296 if (preg_match('/(^|;)\s*cursor\s*:/i', $styleValue)) {
297 return $styleValue;
298 }
299
300 $styleValue = rtrim($styleValue);
301 if ($styleValue !== '' && substr($styleValue, -1) !== ';') {
302 $styleValue .= ';';
303 }
304
305 return $styleValue . ' cursor: pointer;';
306 };
307
308 $styleUpdated = false;
309
310 $beforeClass = preg_replace_callback(
311 '/\sstyle="([^"]*)"/i',
312 static function ($styleMatch) use (&$styleUpdated, $appendCursor) {
313 $styleUpdated = true;
314 return ' style="' . esc_attr($appendCursor($styleMatch[1])) . '"';
315 },
316 $beforeClass,
317 1
318 );
319
320 if (!$styleUpdated) {
321 $afterClass = preg_replace_callback(
322 '/\sstyle="([^"]*)"/i',
323 static function ($styleMatch) use (&$styleUpdated, $appendCursor) {
324 $styleUpdated = true;
325 return ' style="' . esc_attr($appendCursor($styleMatch[1])) . '"';
326 },
327 $afterClass,
328 1
329 );
330 }
331
332 if (!$styleUpdated) {
333 $afterClass .= ' style="cursor: pointer;"';
334 }
335
336 return '<a ' . $beforeClass
337 . 'class="' . esc_attr($matches[2]) . '"'
338 . ' id="' . esc_attr($autoTrigger) . '"'
339 . $afterClass
340 . '>';
341 },
342 $button_html,
343 1
344 );
345
346 echo $button_html;
347
348 echo '</div>';
349
350 // ── Hidden shortcode consumed by Amelia frontend JS ───────────────────
351 echo '<div class="amelia-step-booking-shortcode" style="display:none">'
352 . esc_html($shortcode)
353 . '</div>';
354 }
355 }
356