PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.6
Booking for Appointments and Events Calendar – Amelia v2.4.6
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 / extensions / divi_5_amelia / server / AmeliaBookingButtonRendererTrait.php
ameliabooking / extensions / divi_5_amelia / server Last commit date
AmeliaBookingButtonRendererTrait.php 2 months ago AmeliaBookingModule.php 7 months ago AmeliaCatalogBookingModule.php 2 months ago AmeliaCatalogModule.php 7 months ago AmeliaCustomerPanelModule.php 7 months ago AmeliaEmployeePanelModule.php 7 months ago AmeliaEventsCalendarModule.php 2 months ago AmeliaEventsListBookingButtonModule.php 2 months ago AmeliaEventsListModule.php 2 months ago AmeliaEventsModule.php 7 months ago AmeliaSearchModule.php 7 months ago AmeliaStepBookingButtonModule.php 2 months ago AmeliaStepBookingModule.php 2 months ago ModuleMetadata.php 2 months ago SharedShortcodeModule.php 2 months ago index.php 2 months ago
AmeliaBookingButtonRendererTrait.php
225 lines
1 <?php
2
3 namespace Divi5Amelia;
4
5 use ET\Builder\FrontEnd\BlockParser\BlockParserStore;
6 use ET\Builder\Packages\Module\Module;
7 use ET\Builder\Packages\Module\Layout\Components\ModuleElements\ModuleElements;
8 use WP_Block;
9
10 /**
11 * Shared helpers for rendering Divi-trigger buttons used by Amelia modules.
12 */
13 trait AmeliaBookingButtonRendererTrait
14 {
15 /**
16 * Normalize button innerContent.desktop.value to object-like array.
17 * Supports both persisted string and structured array formats.
18 */
19 private static function normalizeButtonDesktopValue(array &$button_attr): array
20 {
21 $button_attr['innerContent'] = $button_attr['innerContent'] ?? [];
22 $button_attr['innerContent']['desktop'] = $button_attr['innerContent']['desktop'] ?? [];
23
24 $raw = $button_attr['innerContent']['desktop']['value'] ?? null;
25
26 if (is_string($raw)) {
27 $button_attr['innerContent']['desktop']['value'] = [
28 'text' => $raw,
29 ];
30 } elseif (!is_array($raw)) {
31 $button_attr['innerContent']['desktop']['value'] = [];
32 }
33
34 return $button_attr['innerContent']['desktop']['value'];
35 }
36
37 /**
38 * Merge CSS class names while preserving uniqueness.
39 */
40 private static function mergeClassNames(string ...$class_names): string
41 {
42 $all = implode(' ', $class_names);
43 $parts = preg_split('/\s+/', trim($all)) ?: [];
44 $parts = array_values(array_unique(array_filter($parts, static function ($part) {
45 return $part !== '';
46 })));
47
48 return implode(' ', $parts);
49 }
50
51 /**
52 * Ensure the first rendered button/anchor has display:inline-block in inline style.
53 */
54 private static function ensureInlineBlockDisplayInHtml(string $html): string
55 {
56 if ($html === '') {
57 return $html;
58 }
59
60 // Case 1: Existing style="..." on first clickable tag -> normalize display to inline-block.
61 $updated = preg_replace_callback(
62 '/<(a|button)\b([^>]*?)\sstyle\s*=\s*(["\'])(.*?)\3([^>]*)>/i',
63 static function (array $matches): string {
64 $style_value = trim((string) $matches[4]);
65 if (preg_match('/(?:^|;)\s*display\s*:[^;]*/i', $style_value)) {
66 $style_value = preg_replace(
67 '/(^|;)\s*display\s*:[^;]*(?:;|$)/i',
68 '$1 display: inline-block;',
69 $style_value,
70 1
71 ) ?? $style_value;
72 } else {
73 if ($style_value !== '' && substr($style_value, -1) !== ';') {
74 $style_value .= ';';
75 }
76 $style_value .= ' display: inline-block;';
77 }
78
79 if ($style_value !== '' && substr($style_value, -1) !== ';') {
80 $style_value .= ';';
81 }
82
83 return '<' . $matches[1] . $matches[2] . ' style=' . $matches[3] . trim($style_value) . $matches[3] . $matches[5] . '>';
84 },
85 $html,
86 1
87 );
88
89 if ($updated !== null && $updated !== $html) {
90 return $updated;
91 }
92
93 // Case 2: Bare style attribute token (style without value) on first clickable tag.
94 $updated = preg_replace(
95 '/(<(?:a|button)\b[^>]*?)\sstyle(?=[\s>])/i',
96 '$1 style="display: inline-block;"',
97 $html,
98 1
99 );
100
101 if ($updated !== null && $updated !== $html) {
102 return $updated;
103 }
104
105 // Case 3: No style attribute on first clickable tag -> inject one.
106 $updated = preg_replace(
107 '/(<(?:a|button)\b)([\s>])/i',
108 '$1 style="display: inline-block;"$2',
109 $html,
110 1
111 );
112
113 return $updated ?? $html;
114 }
115
116 /**
117 * Build and render trigger button HTML while keeping Divi button markup/styles.
118 */
119 private static function renderTriggerButtonHtml(
120 array $attrs,
121 WP_Block $block,
122 ModuleElements $elements,
123 string $auto_trigger,
124 string $button_base_class,
125 string $fallback_button_text
126 ): string {
127 $button_attr = $attrs['button'] ?? [];
128 $desktop_value = self::normalizeButtonDesktopValue($button_attr);
129 $button_text = trim((string) ($desktop_value['text'] ?? ''));
130 if ($button_text === '') {
131 $button_text = $fallback_button_text;
132 }
133
134 $module_id = isset($block->parsed_block['id']) ? (string) $block->parsed_block['id'] : '';
135 $divi_button_instance_class = 'et_pb_button_' . sanitize_html_class($module_id !== '' ? $module_id : wp_unique_id());
136
137 $button_attr['attributes'] = $button_attr['attributes'] ?? [];
138 $existing_button_class = isset($button_attr['attributes']['class']) ? trim((string) $button_attr['attributes']['class']) : '';
139 $button_attr['attributes']['class'] = self::mergeClassNames(
140 $button_base_class,
141 'et_block_module',
142 $divi_button_instance_class,
143 $existing_button_class
144 );
145
146 $button_attr['attributes']['id'] = trim($auto_trigger);
147 if ($module_id !== '' && !isset($button_attr['attributes']['data-id'])) {
148 $button_attr['attributes']['data-id'] = $module_id;
149 }
150
151 $button_attr['innerContent']['desktop']['value']['text'] = $button_text;
152 if (!isset($button_attr['innerContent']['desktop']['value']['linkUrl'])) {
153 $button_attr['innerContent']['desktop']['value']['linkUrl'] = '';
154 }
155
156 $button_html = $elements->render([
157 'attrName' => 'button',
158 'elementAttr' => $button_attr,
159 'elementProps' => [
160 'hasWrapper' => false,
161 'allowEmptyUrl' => true,
162 ],
163 ]);
164
165 $button_html = self::ensureInlineBlockDisplayInHtml((string) $button_html);
166
167 if (!empty($button_html) && !empty($auto_trigger)) {
168 $button_html = preg_replace(
169 '/(<(?:a|button)\b)([\s>])/i',
170 '$1 id="' . esc_attr($auto_trigger) . '"$2',
171 $button_html,
172 1
173 );
174 }
175
176 return (string) $button_html;
177 }
178
179 /**
180 * Render the common module shell around the booking trigger button.
181 */
182 private static function renderBookingButtonModuleShell(
183 array $attrs,
184 WP_Block $block,
185 ModuleElements $elements,
186 string $button_html,
187 string $wrapper_class,
188 string $shortcode_hidden_content
189 ): string {
190 $parent = BlockParserStore::get_parent(
191 $block->parsed_block['id'],
192 $block->parsed_block['storeInstance']
193 );
194
195 $inner_content =
196 $elements->style_components(['attrName' => 'module'])
197 . $elements->style_components(['attrName' => 'button'])
198 . '<div class="' . esc_attr($wrapper_class) . '">'
199 . $button_html
200 . '</div>'
201 . '<div class="amelia-shortcode" style="display:none">' . $shortcode_hidden_content . '</div>';
202
203 return Module::render([
204 // FE only.
205 'orderIndex' => $block->parsed_block['orderIndex'],
206 'storeInstance' => $block->parsed_block['storeInstance'],
207
208 // VB equivalent.
209 'attrs' => $attrs,
210 'elements' => $elements,
211 'id' => $block->parsed_block['id'],
212 'name' => $block->block_type->name,
213 'classnamesFunction' => [self::class, 'module_classnames'],
214 'moduleCategory' => $block->block_type->category,
215 'stylesComponent' => [self::class, 'module_styles'],
216 'scriptDataComponent' => [self::class, 'module_script_data'],
217 'parentAttrs' => $parent->attrs ?? [],
218 'parentId' => $parent->id ?? '',
219 'parentName' => $parent->blockName ?? '',
220 'children' => $inner_content,
221 ]);
222 }
223 }
224
225