PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.21
Booktics – Appointment Booking Calendar for Service Businesses v1.0.21
1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 All 27 releases
booktics / core / assets / base-assets.php

base-assets.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.21, at core/assets/base-assets.php

364 lines 11.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Booktics\Assets;
3
4 if ( ! defined( 'ABSPATH' ) ) exit;
5
6 use Booktics\Contracts\Hookable_Service_Contract;
7
8 /**
9 * Main assets management class
10 */
11 abstract class Base_Assets implements Hookable_Service_Contract {
12 /**
13 * Get all scripts
14 *
15 * @return array list of script to register
16 */
17 abstract public function get_scripts(): array;
18
19 /**
20 * Get all styles
21 *
22 * @return array list of styles to regiser
23 */
24 abstract public function get_styles(): array;
25
26 /**
27 * Register scripts and styles
28 *
29 * @return void
30 */
31 public function register_styles_scripts() {
32 // Register i18n loader early so other scripts can depend on it.
33 $this->register_global_scripts();
34 $this->register_scripts();
35 $this->register_styles();
36 }
37
38 /**
39 * Register scripts
40 *
41 * @param array $scripts
42 *
43 * @return void
44 */
45 public function register_scripts() {
46 $scripts = $this->get_scripts();
47
48 foreach ( $scripts as $handle => $script ) {
49 $deps = isset( $script['deps'] ) ? $script['deps'] : [];
50 $in_footer = isset( $script['in_footer'] ) ? $script['in_footer'] : true;
51 $version = isset( $script['version'] ) ? $script['version'] : $this->get_version( $script['src'] );
52
53 $deps = $this->get_dependencies( $script['src'], $deps );
54
55 // Mirror wp-cafe: if script uses wp-i18n, ensure our i18n loader runs first.
56 if ( in_array( 'wp-i18n', $deps, true ) ) {
57 $deps[] = 'booktics-i18n-loader';
58 }
59
60 wp_register_script( $handle, $script['src'], $deps, $version, $in_footer );
61
62 // Enable JS translations for bundles that use wp.i18n.
63 if ( function_exists( 'wp_set_script_translations' ) ) {
64 // Don't force a path: translations are commonly in WP_LANG_DIR/plugins.
65 wp_set_script_translations( $handle, 'booktics' );
66 }
67
68 // Ensure preloaded locale data is actually registered in wp.i18n for
69 // pages that never trigger per-chunk JSON downloads.
70 if ( 'booktics-packages' === $handle ) {
71 wp_add_inline_script(
72 'booktics-packages',
73 '(function(){try{if(window.bookticsPreloadedLocaleData && window.wp && window.wp.i18n && window.wp.i18n.setLocaleData){window.wp.i18n.setLocaleData(window.bookticsPreloadedLocaleData,"booktics");}}catch(e){}})();',
74 'before'
75 );
76 }
77 }
78 }
79
80 /**
81 * Register global scripts.
82 *
83 * @return void
84 */
85 private function register_global_scripts() {
86 $scripts = [
87 'booktics-i18n-loader' => [
88 'src' => booktics()->assets_url . '/build/js/i18n-loader.js',
89 'deps' => [],
90 'in_footer' => true,
91 ],
92 ];
93
94 foreach ( $scripts as $handle => $script ) {
95 $deps = isset( $script['deps'] ) ? $script['deps'] : [];
96 $in_footer = isset( $script['in_footer'] ) ? $script['in_footer'] : true;
97 $version = isset( $script['version'] ) ? $script['version'] : $this->get_version( $script['src'] );
98
99 $deps = $this->get_dependencies( $script['src'], $deps );
100
101 wp_register_script( $handle, $script['src'], $deps, $version, $in_footer );
102 }
103 }
104
105 /**
106 * Enqueue i18n loader and set its state.
107 *
108 * @return void
109 */
110 protected function enqueue_i18n_loader() {
111 $data = [
112 'baseUrl' => false,
113 'locale' => determine_locale(),
114 'domainMap' => [],
115 'domainPaths' => [],
116 'translationMap' => (object) [],
117 ];
118
119 $lang_dir = WP_LANG_DIR;
120 $content_dir = WP_CONTENT_DIR;
121 $abspath = ABSPATH;
122
123 if ( strpos( $lang_dir, $content_dir ) === 0 ) {
124 $data['baseUrl'] = content_url(
125 substr( trailingslashit( $lang_dir ), strlen( trailingslashit( $content_dir ) ) )
126 );
127 } elseif ( strpos( $lang_dir, $abspath ) === 0 ) {
128 $data['baseUrl'] = site_url(
129 substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) )
130 );
131 }
132
133 $data['translationMap'] = (object) $this->get_translation_map();
134 $data['domainPaths'] = (object) $this->get_translation_directories();
135
136 wp_enqueue_script( 'booktics-i18n-loader' );
137
138 $data['domainMap'] = (object) $data['domainMap'];
139 $data['domainPaths'] = (object) $data['domainPaths'];
140
141 // If a consolidated .l10n.php file exists, preload all messages to avoid
142 // relying on per-chunk JSON requests (which may be missing).
143 $preload = null;
144 $l10n_file = trailingslashit( WP_LANG_DIR ) . 'plugins/booktics-' . $data['locale'] . '.l10n.php';
145 $l10n_file_alt = trailingslashit( WP_LANG_DIR ) . 'plugins/booktics-' . str_replace( '-', '_', $data['locale'] ) . '.l10n.php';
146 $l10n_file_legacy = trailingslashit( WP_LANG_DIR ) . 'plugins/booktics-' . str_replace( '_', '-', $data['locale'] ) . '.l10n.php';
147 $l10n_candidates = array_unique( array_filter( [ $l10n_file, $l10n_file_alt, $l10n_file_legacy ] ) );
148
149 foreach ( $l10n_candidates as $candidate ) {
150 if ( file_exists( $candidate ) ) {
151 $preload = include $candidate;
152 break;
153 }
154 }
155
156 $inline = '(function(){var k="bookticsI18nLoader"; if (typeof window[k] === "undefined") { window[k] = {}; } window[k].state = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES ) . ';';
157
158 if ( is_array( $preload ) && ! empty( $preload['messages'] ) && is_array( $preload['messages'] ) ) {
159 $messages = [];
160 foreach ( $preload['messages'] as $msgid => $msgstr ) {
161 if ( is_string( $msgid ) && is_string( $msgstr ) ) {
162 $messages[ $msgid ] = [ $msgstr ];
163 }
164 }
165
166 $locale_data = [
167 '' => [
168 'domain' => 'booktics',
169 'lang' => $data['locale'],
170 'plural-forms' => isset( $preload['plural-forms'] ) ? $preload['plural-forms'] : 'nplurals=2; plural=n != 1;',
171 ],
172 ] + $messages;
173
174 $encoded_locale_data = wp_json_encode( $locale_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
175 $inline .= ' window[k].preloadedLocaleData = ' . $encoded_locale_data . '; window.bookticsPreloadedLocaleData = ' . $encoded_locale_data . ';';
176 }
177
178 $inline .= '})();';
179
180 wp_add_inline_script(
181 'booktics-i18n-loader',
182 $inline,
183 'before'
184 );
185 }
186
187 /**
188 * Register styles
189 *
190 * @param array $styles
191 *
192 * @return void
193 */
194 public function register_styles() {
195 $styles = $this->get_styles();
196
197 foreach ( $styles as $handle => $style ) {
198 $deps = isset( $style['deps'] ) ? $style['deps'] : false;
199 $version = booktics()->version;
200
201 wp_register_style( $handle, $style['src'], $deps, $version );
202 }
203 }
204
205 /**
206 * Get script and style file dependencies
207 *
208 * @param string $file_name
209 * @param array $deps
210 *
211 * @return array
212 */
213 private function get_dependencies( $file_name, $deps = [] ) {
214 $assets = $this->get_file_assets( $file_name );
215
216 $assets_deps = ! empty( $assets['dependencies'] ) ? $assets['dependencies'] : [];
217
218 $merged_deps = array_merge( $assets_deps, $deps );
219
220 return $merged_deps;
221 }
222
223 /**
224 * Get file assets
225 *
226 * @param string $file_name
227 *
228 * @return array
229 */
230 private function get_file_assets( $file_url ) {
231 $file = $this->get_file_path( $file_url );
232 $assets = [];
233
234 if ( file_exists( $file ) ) {
235 $assets = include $file;
236 }
237
238 return $assets;
239 }
240
241 /**
242 * Get script file version
243 *
244 * @param string $file_name
245 *
246 * @return string
247 */
248 private function get_version( $file_name ) {
249 $assets = $this->get_file_assets( $file_name );
250 $assets_vers = ! empty( $assets['version'] ) ? $assets['version'] : booktics()->version;
251 return $assets_vers;
252 }
253
254 /**
255 * Get file path from url
256 *
257 * @param string $url
258 *
259 * @return string
260 */
261 private function get_file_path( $url ) {
262 // Check if the URL is valid
263 if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
264 return false;
265 }
266
267 // Parse the URL
268 $url_parts = wp_parse_url( $url );
269
270 // Check if the URL has a path component
271 if ( ! isset( $url_parts['path'] ) ) {
272 return false; // URL does not contain a path
273 }
274
275 $clean_path = str_replace( '.js', '.asset.php', $url_parts['path'] );
276
277 // Get the file path from the URL path.
278 if ( isset( $_SERVER['DOCUMENT_ROOT'] ) ) {
279 $file_path = sanitize_text_field( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ) . $clean_path;
280 } else {
281 $file_path = ''; // Or another appropriate fallback.
282 }
283
284 // Check if the file exists
285 if ( ! file_exists( $file_path ) ) {
286 return false; // File does not exist.
287 }
288
289 return $file_path;
290 }
291
292 /**
293 * Get map of translation file hashes to their URLs.
294 *
295 * Scans all translation directories for JSON files matching
296 * the current locale and domain, building a hash => URL map.
297 *
298 * @return array Hash => URL map.
299 */
300 private function get_translation_map() {
301 $map = [];
302 $locale = determine_locale();
303 $domain = 'booktics';
304
305 $locale_variants = array_unique( array_filter( [
306 $locale,
307 str_replace( '-', '_', $locale ),
308 str_replace( '_', '-', $locale ),
309 ] ) );
310
311 foreach ( $this->get_translation_directories() as $dir ) {
312 if ( ! is_dir( $dir['path'] ) || ! is_readable( $dir['path'] ) ) {
313 continue;
314 }
315
316 foreach ( $locale_variants as $variant ) {
317 $files = glob( $dir['path'] . $domain . '-' . $variant . '-*.json' );
318 if ( ! $files ) {
319 continue;
320 }
321
322 foreach ( $files as $file ) {
323 $filename = basename( $file );
324 // Extract hash: everything after "{domain}-{locale}-".
325 $variant_prefix = $domain . '-' . $variant . '-';
326 if ( strpos( $filename, $variant_prefix ) === 0 ) {
327 $hash = substr( $filename, strlen( $variant_prefix ), -5 ); // strip .json
328 if ( $hash ) {
329 $map[ $hash ] = trailingslashit( $dir['url'] ) . $filename;
330 }
331 }
332 }
333 }
334 }
335
336 return $map;
337 }
338
339 /**
340 * Return translation directory paths and their corresponding URLs.
341 *
342 * Covers the standard WP languages/plugins/ directory, Loco Translate's
343 * directory, and the plugin's own bundled languages/ folder.
344 *
345 * @return array<int, array{path: string, url: string}>
346 */
347 private function get_translation_directories() {
348 return [
349 [
350 'path' => WP_CONTENT_DIR . '/languages/plugins/',
351 'url' => content_url( 'languages/plugins/' ),
352 ],
353 [
354 'path' => WP_CONTENT_DIR . '/languages/loco/plugins/',
355 'url' => content_url( 'languages/loco/plugins/' ),
356 ],
357 [
358 'path' => booktics()->plugin_directory . '/languages/',
359 'url' => booktics()->plugin_url . '/languages/',
360 ],
361 ];
362 }
363 }
364