PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
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 6 months ago AmeliaCatalogBookingElementorWidget.php 2 weeks ago AmeliaCatalogElementorWidget.php 6 months ago AmeliaEventsCalendarBookingElementorWidget.php 2 weeks ago AmeliaEventsElementorWidget.php 2 weeks ago AmeliaEventsListBookingButtonElementorWidget.php 2 weeks ago AmeliaEventsListBookingElementorWidget.php 2 weeks ago AmeliaStepBookingButtonElementorWidget.php 1 month ago AmeliaStepBookingElementorWidget.php 2 weeks ago ElementorBlock.php 2 weeks ago ElementorSharedShortcodeWidget.php 2 weeks ago
AmeliaStepBookingButtonElementorWidget.php
354 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 return is_array($widgetLabel) ? $widgetLabel['title'] : BackendStrings::get('step_booking');
39 }
40
41 public function get_icon()
42 {
43 return 'amelia-logo';
44 }
45
46 public function get_categories()
47 {
48 return ['amelia-elementor'];
49 }
50
51 // -------------------------------------------------------------------------
52 // Controls
53 // -------------------------------------------------------------------------
54
55 protected function register_controls()
56 {
57 // ── All standard Elementor Button widget controls ──────────────────────
58 // Type, Text, Size, Selected Icon, Icon Position, Icon Spacing
59 // + full Style tab (Typography, colours, Border, Radius, Shadow, Padding)
60 parent::register_controls();
61
62 // Set default button text to "Book Appointment"
63 $this->update_control(
64 'text',
65 [
66 'default' => BackendStrings::get('book_appointment'),
67 ]
68 );
69
70 // Remove controls that are irrelevant for the Amelia booking button:
71 // the booking is opened via an Amelia shortcode trigger, not a URL link,
72 // and the trigger ID is managed internally so a manual Button ID is
73 // confusing and unused.
74 $this->remove_control('link');
75 $this->remove_control('button_css_id');
76
77 // ── Amelia booking options ─────────────────────────────────────────────
78 $controls_data = AmeliaStepBookingElementorWidget::amelia_elementor_get_data();
79
80 $this->start_controls_section(
81 'amelia_booking_options',
82 [
83 'label' => esc_html__('Amelia Booking Options', 'wpamelia'),
84 ]
85 );
86
87 // Preselect toggle
88 $this->add_control(
89 'preselect',
90 [
91 'label' => BackendStrings::get('filter'),
92 'type' => Controls_Manager::SWITCHER,
93 'default' => false,
94 'label_on' => BackendStrings::get('yes'),
95 'label_off' => BackendStrings::get('no'),
96 ]
97 );
98
99 // Category
100 if ($controls_data['categories'] && sizeof($controls_data['categories']) > 1) {
101 $this->add_control(
102 'select_category',
103 [
104 'label' => BackendStrings::get('select_category'),
105 'type' => Controls_Manager::SELECT2,
106 'multiple' => true,
107 'options' => $controls_data['categories'],
108 'condition' => ['preselect' => 'yes'],
109 'placeholder' => BackendStrings::get('show_all_categories'),
110 ]
111 );
112 }
113
114 // Service
115 if ($controls_data['services'] && sizeof($controls_data['services']) > 1) {
116 $this->add_control(
117 'select_service',
118 [
119 'label' => BackendStrings::get('select_service'),
120 'type' => Controls_Manager::SELECT2,
121 'multiple' => true,
122 'options' => $controls_data['services'],
123 'condition' => ['preselect' => 'yes'],
124 'placeholder' => BackendStrings::get('show_all_services'),
125 ]
126 );
127 }
128
129 // Employee
130 if ($controls_data['employees'] && sizeof($controls_data['employees']) > 1) {
131 $this->add_control(
132 'select_employee',
133 [
134 'label' => BackendStrings::get('select_employee'),
135 'type' => Controls_Manager::SELECT2,
136 'multiple' => true,
137 'options' => $controls_data['employees'],
138 'condition' => ['preselect' => 'yes'],
139 'placeholder' => BackendStrings::get('show_all_employees'),
140 ]
141 );
142 }
143
144 // Location
145 if ($controls_data['locations'] && sizeof($controls_data['locations']) > 1) {
146 $this->add_control(
147 'select_location',
148 [
149 'label' => BackendStrings::get('select_location'),
150 'type' => Controls_Manager::SELECT2,
151 'multiple' => true,
152 'options' => $controls_data['locations'],
153 'condition' => ['preselect' => 'yes'],
154 'placeholder' => BackendStrings::get('show_all_locations'),
155 ]
156 );
157 }
158
159 // Package
160 if (!empty($controls_data['packages'])) {
161 $this->add_control(
162 'select_package',
163 [
164 'label' => BackendStrings::get('select_package'),
165 'type' => Controls_Manager::SELECT2,
166 'multiple' => true,
167 'options' => $controls_data['packages'],
168 'condition' => ['preselect' => 'yes'],
169 'placeholder' => BackendStrings::get('show_all_packages'),
170 ]
171 );
172 }
173
174 // Show
175 if (!empty($controls_data['show'])) {
176 $this->add_control(
177 'select_show',
178 [
179 'label' => BackendStrings::get('show_all'),
180 'type' => Controls_Manager::SELECT,
181 'options' => $controls_data['show'],
182 'condition' => ['preselect' => 'yes'],
183 'default' => '',
184 ]
185 );
186 }
187
188 // Layout
189 $this->add_control(
190 'layout',
191 [
192 'label' => BackendStrings::get('layout_select_label'),
193 'type' => Controls_Manager::SELECT,
194 'description' => BackendStrings::get('layout_description'),
195 'options' => $controls_data['layout_options'],
196 'default' => '1',
197 ]
198 );
199
200 $this->end_controls_section();
201 }
202
203 // -------------------------------------------------------------------------
204 // Render
205 // -------------------------------------------------------------------------
206
207 protected function render()
208 {
209 $settings = $this->get_settings_for_display();
210
211 // ── Build trigger value ────────────────────────────────────────────────
212 $autoTrigger = 'amelia-step-booking-btn-' . substr(md5($this->get_id()), 0, 8);
213
214 // ── Build shortcode ────────────────────────────────────────────────────
215 $shortcode = '[ameliastepbooking';
216 $shortcode .= ' trigger=' . $autoTrigger;
217 $shortcode .= ' trigger_type=id';
218 $shortcode .= ' in_dialog=1';
219
220 $toCsvIds = static function ($value): string {
221 $vals = is_array($value) ? $value : [$value];
222 $vals = array_filter(array_map('absint', $vals));
223 return implode(',', $vals);
224 };
225
226 if (!empty($settings['preselect'])) {
227 if (!empty($settings['select_show'])) {
228 $shortcode .= ' show=' . sanitize_key((string) $settings['select_show']);
229 }
230
231 if (!empty($settings['select_service'])) {
232 $service = $toCsvIds($settings['select_service']);
233 if ($service !== '') {
234 $shortcode .= ' service=' . $service;
235 }
236 } elseif (!empty($settings['select_category'])) {
237 $category = $toCsvIds($settings['select_category']);
238 if ($category !== '') {
239 $shortcode .= ' category=' . $category;
240 }
241 }
242
243 if (!empty($settings['select_employee'])) {
244 $employee = $toCsvIds($settings['select_employee']);
245 if ($employee !== '') {
246 $shortcode .= ' employee=' . $employee;
247 }
248 }
249
250 if (!empty($settings['select_location'])) {
251 $location = $toCsvIds($settings['select_location']);
252 if ($location !== '') {
253 $shortcode .= ' location=' . $location;
254 }
255 }
256
257 if (!empty($settings['select_package'])) {
258 $package = $toCsvIds($settings['select_package']);
259 if ($package !== '') {
260 $shortcode .= ' package=' . $package;
261 }
262 }
263 }
264
265 if (!empty($settings['layout'])) {
266 $shortcode .= ' layout=' . absint($settings['layout']);
267 }
268
269 $shortcode .= ']';
270
271 // ── Trigger wrapper ────────────────────────────────────────────────────
272 echo '<div class="amelia-step-booking-button-trigger">';
273
274 // ── Capture parent button output and inject the ID ────────────────────
275 ob_start();
276 parent::render();
277 $button_html = ob_get_clean();
278
279 // Remove any existing id attribute before injecting the trigger ID
280 $button_html = preg_replace(
281 '/<a\s+([^>]*?)\s*id="[^"]*"\s*([^>]*?)class="([^"]*elementor-button[^"]*)"/',
282 '<a $1$2class="$3"',
283 $button_html
284 );
285
286 // Inject trigger ID and enforce cursor pointer style on the button link.
287 $button_html = preg_replace_callback(
288 '/<a\s+([^>]*?)class="([^"]*elementor-button[^"]*)"([^>]*)>/',
289 static function ($matches) use ($autoTrigger) {
290 $beforeClass = $matches[1];
291 $afterClass = $matches[3];
292
293 $appendCursor = static function ($styleValue) {
294 if (preg_match('/(^|;)\s*cursor\s*:/i', $styleValue)) {
295 return $styleValue;
296 }
297
298 $styleValue = rtrim($styleValue);
299 if ($styleValue !== '' && substr($styleValue, -1) !== ';') {
300 $styleValue .= ';';
301 }
302
303 return $styleValue . ' cursor: pointer;';
304 };
305
306 $styleUpdated = false;
307
308 $beforeClass = preg_replace_callback(
309 '/\sstyle="([^"]*)"/i',
310 static function ($styleMatch) use (&$styleUpdated, $appendCursor) {
311 $styleUpdated = true;
312 return ' style="' . esc_attr($appendCursor($styleMatch[1])) . '"';
313 },
314 $beforeClass,
315 1
316 );
317
318 if (!$styleUpdated) {
319 $afterClass = preg_replace_callback(
320 '/\sstyle="([^"]*)"/i',
321 static function ($styleMatch) use (&$styleUpdated, $appendCursor) {
322 $styleUpdated = true;
323 return ' style="' . esc_attr($appendCursor($styleMatch[1])) . '"';
324 },
325 $afterClass,
326 1
327 );
328 }
329
330 if (!$styleUpdated) {
331 $afterClass .= ' style="cursor: pointer;"';
332 }
333
334 return '<a ' . $beforeClass
335 . 'class="' . esc_attr($matches[2]) . '"'
336 . ' id="' . esc_attr($autoTrigger) . '"'
337 . $afterClass
338 . '>';
339 },
340 $button_html,
341 1
342 );
343
344 echo $button_html;
345
346 echo '</div>';
347
348 // ── Hidden shortcode consumed by Amelia frontend JS ───────────────────
349 echo '<div class="amelia-step-booking-shortcode" style="display:none">'
350 . esc_html($shortcode)
351 . '</div>';
352 }
353 }
354