PluginProbe
Adminify – White Label, Admin Menu Editor, Login Customizer / 4.3.0
Adminify – White Label, Admin Menu Editor, Login Customizer v4.3.0
4.3.1 4.3.0 4.2.26 4.2.25 4.2.24 4.2.23 4.2.22 4.2.21 4.2.20 4.2.19 4.2.18 4.2.17 4.2.16 4.2.15 4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.1.17 All 164 releases
adminify / Inc / Classes / GoogleFontsLocal.php

GoogleFontsLocal.php in Adminify – White Label, Admin Menu Editor, Login Customizer 4.3.0, at Inc/Classes/GoogleFontsLocal.php

361 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace PXLBSAdminify\Inc\Classes;
4
5 // no direct access allowed
6 if (!defined('ABSPATH')) {
7 exit;
8 }
9
10 /**
11 * Google Fonts Local
12 *
13 * Downloads Google Fonts locally and serves them from wp-content/uploads/adminify-google-fonts/
14 *
15 * @since 4.0.0
16 */
17 class GoogleFontsLocal
18 {
19 /**
20 * Singleton instance
21 */
22 private static $instance = null;
23
24 /**
25 * User agent for fetching woff2 fonts
26 */
27 private $user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36';
28
29 /**
30 * Base folder name for storing fonts
31 */
32 private $folder_name = 'adminify-google-fonts';
33
34 /**
35 * Get singleton instance
36 */
37 public static function get_instance()
38 {
39 if (self::$instance === null) {
40 self::$instance = new self();
41 }
42 return self::$instance;
43 }
44
45 /**
46 * Constructor
47 */
48 private function __construct()
49 {
50 // Hook into framework's save filter (most reliable)
51 add_filter('pxlbsadminify_adminify_saved_before', [$this, 'on_settings_save'], 10, 1);
52
53 // Enqueue local fonts
54 add_action('admin_enqueue_scripts', [$this, 'enqueue_local_fonts'], 99);
55 }
56
57 /**
58 * Handle settings save via framework filter
59 */
60 public function on_settings_save($data)
61 {
62 $this->process_fonts($data);
63 return $data;
64 }
65
66 /**
67 * Process fonts from settings data
68 */
69 public function process_fonts($data)
70 {
71 if (!is_array($data)) {
72 return;
73 }
74
75 // Body Font (admin_general_google_font) is a Pro-only feature.
76 // Pro/Classes/Assets_Pro.php hooks
77 // pxlbsadminify_adminify_saved_before to download that font on
78 // save. Free leaves it alone.
79
80 // Process Light Mode Logo Typography
81 if (!empty($data['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo_text_typo']['font-family'])) {
82 $typo = $data['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo_text_typo'];
83 $font_family = $typo['font-family'];
84 $font_weights = !empty($typo['font-weight']) ? $typo['font-weight'] : '400';
85 $this->download_font($font_family, $font_weights);
86 }
87
88 // Process Dark Mode Logo Typography
89 if (!empty($data['light_dark_mode']['admin_ui_dark_mode']['admin_ui_dark_logo_text_typo']['font-family'])) {
90 $typo = $data['light_dark_mode']['admin_ui_dark_mode']['admin_ui_dark_logo_text_typo'];
91 $font_family = $typo['font-family'];
92 $font_weights = !empty($typo['font-weight']) ? $typo['font-weight'] : '400';
93 $this->download_font($font_family, $font_weights);
94 }
95 }
96
97 /**
98 * Get the local fonts folder path
99 */
100 public function get_folder()
101 {
102 $upload_dir = wp_get_upload_dir();
103 return $upload_dir['basedir'] . '/' . $this->folder_name;
104 }
105
106 /**
107 * Get the local fonts folder URL
108 */
109 public function get_folder_url()
110 {
111 $upload_dir = wp_get_upload_dir();
112 $folder_url = $upload_dir['baseurl'] . '/' . $this->folder_name;
113
114 if (is_ssl()) {
115 $folder_url = set_url_scheme($folder_url, 'https');
116 }
117
118 return $folder_url;
119 }
120
121 /**
122 * Download a Google Font locally
123 */
124 public function download_font($font_family, $font_weights = '400')
125 {
126 if (empty($font_family)) {
127 return false;
128 }
129
130 // Sanitize font family for folder name
131 $font_slug = sanitize_title($font_family);
132 $folder = $this->get_folder() . '/' . $font_slug;
133
134 // Create base folder
135 if (!file_exists($this->get_folder())) {
136 wp_mkdir_p($this->get_folder());
137 }
138
139 // Create font folder
140 if (!file_exists($folder)) {
141 wp_mkdir_p($folder);
142 }
143
144 // Normalize font weights - convert 'normal' to '400', 'bold' to '700'
145 $weight_map = [
146 'normal' => '400',
147 'bold' => '700',
148 'lighter' => '300',
149 'bolder' => '700',
150 ];
151
152 if (is_array($font_weights)) {
153 $font_weights = array_map(function($w) use ($weight_map) {
154 return isset($weight_map[$w]) ? $weight_map[$w] : $w;
155 }, $font_weights);
156 $weights_string = implode(',', $font_weights);
157 $weights_array = $font_weights;
158 } else {
159 $font_weights = isset($weight_map[$font_weights]) ? $weight_map[$font_weights] : $font_weights;
160 $weights_string = $font_weights;
161 $weights_array = [$font_weights];
162 }
163
164 // Try the newer css2 API first
165 $font_string = urlencode($font_family);
166 $wght_string = implode(';', $weights_array);
167 $google_url = 'https://fonts.googleapis.com/css2?family=' . $font_string . ':wght@' . $wght_string . '&display=swap';
168
169 // Fetch CSS from Google
170 $response = wp_remote_get($google_url, [
171 'user-agent' => $this->user_agent,
172 'timeout' => 30,
173 ]);
174
175 $css_content = '';
176 if (!is_wp_error($response)) {
177 $css_content = wp_remote_retrieve_body($response);
178 }
179
180 // Fallback: Use the older Google Fonts API (css) which is more forgiving
181 if (empty($css_content) || strpos($css_content, '@font-face') === false) {
182 $font_string = urlencode($font_family);
183 if (!empty($weights_string)) {
184 $font_string .= ':' . $weights_string;
185 }
186 $google_url = 'https://fonts.googleapis.com/css?family=' . $font_string . '&display=swap';
187
188 // Fetch CSS from Google using older API
189 $response = wp_remote_get($google_url, [
190 'user-agent' => $this->user_agent,
191 'timeout' => 30,
192 ]);
193
194 if (!is_wp_error($response)) {
195 $css_content = wp_remote_retrieve_body($response);
196 }
197 }
198
199 // Final fallback: Generate system font fallback CSS if Google Font not found
200 if (empty($css_content) || strpos($css_content, '@font-face') === false) {
201 $css_content = $this->generate_system_font_fallback($font_family, $weights_array);
202 }
203
204 // Find all font file URLs
205 preg_match_all('/url\(([^)]+)\)/i', $css_content, $matches);
206
207 if (!empty($matches[1])) {
208 foreach ($matches[1] as $font_url) {
209 $font_url = trim($font_url, '"\'');
210
211 // Only process Google font URLs
212 if (strpos($font_url, 'fonts.gstatic.com') === false) {
213 continue;
214 }
215
216 // Generate local filename
217 $url_hash = substr(md5($font_url), 0, 8);
218 $extension = pathinfo(wp_parse_url($font_url, PHP_URL_PATH), PATHINFO_EXTENSION);
219 if (empty($extension)) {
220 $extension = 'woff2';
221 }
222 $local_filename = $font_slug . '-' . $url_hash . '.' . $extension;
223 $local_path = $folder . '/' . $local_filename;
224 $local_url = $this->get_folder_url() . '/' . $font_slug . '/' . $local_filename;
225
226 // Download font file if not exists
227 if (!file_exists($local_path)) {
228 $font_response = wp_remote_get($font_url, [
229 'user-agent' => $this->user_agent,
230 'timeout' => 30,
231 ]);
232
233 if (!is_wp_error($font_response)) {
234 $font_data = wp_remote_retrieve_body($font_response);
235 if (!empty($font_data)) {
236 file_put_contents($local_path, $font_data);
237 }
238 }
239 }
240
241 // Replace URL in CSS
242 $css_content = str_replace($font_url, $local_url, $css_content);
243 }
244 }
245
246 // Save local CSS file
247 $css_file = $folder . '/' . $font_slug . '.css';
248 $header = "/* Local Google Font: {$font_family} */\n";
249 $header .= "/* Generated by Adminify - " . gmdate('Y-m-d H:i:s') . " */\n\n";
250 file_put_contents($css_file, $header . $css_content);
251
252 return true;
253 }
254
255 /**
256 * Get local font CSS URL
257 */
258 public function get_local_font_url($font_family)
259 {
260 if (empty($font_family)) {
261 return false;
262 }
263
264 $font_slug = sanitize_title($font_family);
265 $css_file = $this->get_folder() . '/' . $font_slug . '/' . $font_slug . '.css';
266
267 if (file_exists($css_file)) {
268 return $this->get_folder_url() . '/' . $font_slug . '/' . $font_slug . '.css?ver=' . filemtime($css_file);
269 }
270
271 return false;
272 }
273
274 /**
275 * Check if font exists locally
276 */
277 public function is_font_local($font_family)
278 {
279 if (empty($font_family)) {
280 return false;
281 }
282
283 $font_slug = sanitize_title($font_family);
284 $css_file = $this->get_folder() . '/' . $font_slug . '/' . $font_slug . '.css';
285
286 return file_exists($css_file);
287 }
288
289 /**
290 * Enqueue local fonts for admin
291 */
292 public function enqueue_local_fonts()
293 {
294 $options = get_option('pxlbsadminify_settings');
295
296 if (empty($options) || !is_array($options)) {
297 return;
298 }
299
300 // Body Font (admin_general_google_font) is a Pro-only feature.
301 // Pro/Classes/Assets_Pro.php enqueues that font via its own
302 // admin_enqueue_scripts callback. Free leaves it alone.
303
304 // Light Mode Logo Typography
305 if (!empty($options['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo_text_typo']['font-family'])) {
306 $font_family = $options['light_dark_mode']['admin_ui_light_mode']['admin_ui_light_logo_text_typo']['font-family'];
307 $local_url = $this->get_local_font_url($font_family);
308
309 if ($local_url) {
310 wp_enqueue_style('adminify-local-logo-light-font', $local_url, [], PXLBSADMINIFY_VER);
311 }
312 }
313
314 // Dark Mode Logo Typography
315 if (!empty($options['light_dark_mode']['admin_ui_dark_mode']['admin_ui_dark_logo_text_typo']['font-family'])) {
316 $font_family = $options['light_dark_mode']['admin_ui_dark_mode']['admin_ui_dark_logo_text_typo']['font-family'];
317 $local_url = $this->get_local_font_url($font_family);
318
319 if ($local_url) {
320 wp_enqueue_style('adminify-local-logo-dark-font', $local_url, [], PXLBSADMINIFY_VER);
321 }
322 }
323 }
324
325 /**
326 * Generate system font fallback CSS when Google Font is not found
327 */
328 private function generate_system_font_fallback($font_family, $weights_array)
329 {
330 // System fonts to try as local fallbacks
331 $system_fonts = [
332 'BlinkMacSystemFont',
333 'Segoe UI',
334 'Roboto',
335 'Helvetica Neue',
336 'Arial',
337 'sans-serif',
338 ];
339
340 $css = "/* Fallback: Google Font '{$font_family}' not found */\n";
341 $css .= "/* Using system font stack as fallback */\n\n";
342
343 foreach ($weights_array as $weight) {
344 $local_src = [];
345 foreach ($system_fonts as $font) {
346 $local_src[] = "local('{$font}')";
347 }
348
349 $css .= "@font-face {\n";
350 $css .= " font-family: '{$font_family}';\n";
351 $css .= " font-style: normal;\n";
352 $css .= " font-weight: {$weight};\n";
353 $css .= " font-display: swap;\n";
354 $css .= " src: " . implode(",\n ", $local_src) . ";\n";
355 $css .= "}\n\n";
356 }
357
358 return $css;
359 }
360 }
361