PluginProbe ʕ •ᴥ•ʔ
Kubio AI Page Builder / 1.2.3
Kubio AI Page Builder v1.2.3
2.8.5 2.8.4 2.8.3 2.8.2 2.8.1 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.9.0 2.0.0 2.1.1 2.1.2 2.1.3 2.2.0 2.2.3 2.2.4 2.2.5 2.3.0 2.3.1 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.5 2.5.0 2.5.1 2.5.2 2.5.3 2.6.0 2.6.1 2.6.2 2.6.3 2.6.5 2.6.6 2.6.7 2.7.0 2.7.1 2.7.2 2.7.3 2.8.0
kubio / lib / src / GoogleFontsLocalLoader.php
kubio / lib / src Last commit date
Core 4 years ago DemoSites 4 years ago Config.php 4 years ago Flags.php 4 years ago GoogleFontsLocalLoader.php 4 years ago Migrations.php 4 years ago NotificationsManager.php 4 years ago PluginsManager.php 4 years ago
GoogleFontsLocalLoader.php
376 lines
1 <?php
2
3 namespace Kubio;
4
5 use IlluminateAgnostic\Arr\Support\Arr;
6 use Kubio\Core\Utils;
7
8 class GoogleFontsLocalLoader {
9
10 private static $instance = null;
11
12 private $queries_transient = 'kubio_local_google_queries_transient';
13 private $font_file_action = 'kubio_get_google_font_file';
14 private $fonts_css_action = 'kubio_get_google_font_css';
15
16 private $uploads_dir = 'kubio-google-fonts-cache';
17
18 private $google_css_url = 'https://fonts.googleapis.com/css';
19 private $google_font_url = 'https://fonts.gstatic.com/s';
20 private $user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:97.0) Gecko/20100101 Firefox/97.0';
21
22 private $local_fonts_dir;
23 private $local_fonts_url;
24
25
26 /**
27 *
28 * @return GoogleFontsLocalLoader
29 */
30 public static function getInstance() {
31 if ( ! static::$instance ) {
32 static::$instance = new static();
33 }
34
35 return static::$instance;
36 }
37
38 public function __construct() {
39
40 $upload_dir = wp_upload_dir();
41 $upload_path = untrailingslashit( $upload_dir['basedir'] );
42
43 $this->local_fonts_dir = "{$upload_path}/{$this->uploads_dir}";
44 $this->local_fonts_url = "{$upload_dir['baseurl']}/{$this->uploads_dir}";
45
46 if ( ! file_exists( $this->local_fonts_dir ) ) {
47 mkdir( $this->local_fonts_dir, 0777, true );
48 }
49 }
50
51
52 public function resolveFontsCSS() {
53 $action = Arr::get( $_REQUEST, 'action' );
54
55 if ( $action !== $this->fonts_css_action ) {
56 return;
57 }
58
59 header( 'Content-type: text/css' );
60 header( 'Cache-control: public' );
61
62 $key = Arr::get( $_REQUEST, 'key', '' );
63 $cached = $this->getCachedDataByKey( $key );
64
65 if ( ! $cached ) {
66 die( '' );
67 }
68
69 $query = Arr::get( $cached, 'query' );
70 $css = Arr::get( $cached, 'css' );
71
72 if ( ! $css ) {
73 $css = $this->getCSS( $query );
74 $this->cacheQueryCSS( $query, $css );
75 }
76
77 $css = $this->replacePlaceholdersWithLocalCSS( $css );
78
79 if ( Utils::isDebug() ) {
80 $css = "/* {$this->google_css_url}?family={$query} */\n\n{$css}";
81 }
82
83 die( $css );
84
85 }
86
87 private function getCSS( $query ) {
88 $fonts_url = add_query_arg(
89 array(
90 'family' => urlencode( $query ),
91 'display' => 'swap',
92 ),
93 $this->google_css_url
94 );
95
96 $response = wp_remote_get(
97 $fonts_url,
98 array(
99 'user-agent' => $this->user_agent,
100 )
101 );
102
103 if ( is_wp_error( $response ) ) {
104 return null;
105 }
106
107 if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
108 return null;
109 }
110
111 $css = wp_remote_retrieve_body( $response );
112 $css = $this->replaceGoogleURLS( $css );
113
114 return $css;
115 }
116
117 private function replaceGoogleURLS( $css ) {
118
119 $google_font_url = $this->google_font_url;
120 $css = preg_replace_callback(
121 '#url\((.*?)\)#',
122 function( $matches ) use ( $google_font_url ) {
123 $url = str_replace( $google_font_url, '', Arr::get( $matches, 1, '' ) );
124
125 return "url({{{{$url}}}})";
126 },
127 $css
128 );
129
130 return $css;
131 }
132
133 public function getLocalFontFilePath( $font_file ) {
134 $file_key = md5( $font_file );
135 return "{$this->local_fonts_dir}/{$file_key}.woff2";
136 }
137
138 public function getLocalFontFileURL( $font_file ) {
139 $file_key = md5( $font_file );
140 return "{$this->local_fonts_url}/{$file_key}.woff2";
141 }
142
143 public function saveFontContentToLocalFile( $font_file, $content ) {
144 $path = $this->getLocalFontFilePath( $font_file );
145 return file_put_contents( $path, $content );
146 }
147
148 public function localFontFileExists( $font_file ) {
149 return file_exists( $this->getLocalFontFilePath( $font_file ) );
150 }
151
152 private function replacePlaceholdersWithLocalCSS( $css ) {
153 $self = $this;
154 $action = $this->font_file_action;
155
156 $css = preg_replace_callback(
157 '#url\(\{\{\{(.*?)\}\}\}\)#',
158 function( $matches ) use ( $self, $action ) {
159 $font_file = Arr::get( $matches, 1, '' );
160
161 if ( $self->localFontFileExists( $font_file ) ) {
162
163 $url = $self->getLocalFontFileURL( $font_file );
164 } else {
165 $url = add_query_arg(
166 array(
167 'font' => urlencode( $font_file ),
168 'action' => $action,
169 'security' => $self->createSecurityKey( "{$action}_{$font_file}" ),
170 ),
171 admin_url( 'admin-ajax.php' )
172 );
173 }
174
175 return "url(${url})";
176 },
177 $css
178 );
179
180 return $css;
181
182 }
183
184 public function getCachedDataByKey( $key ) {
185 $transient = get_transient( $this->queries_transient );
186 if ( ! is_array( $transient ) ) {
187 return null;
188 }
189 return Arr::get( $transient, $key );
190 }
191
192 public function getCachedQueryData( $query ) {
193 return $this->getCachedDataByKey( md5( $query ) );
194 }
195
196 public function cacheQueryCSS( $query, $css ) {
197
198 $transient = get_transient( $this->queries_transient );
199 if ( ! is_array( $transient ) ) {
200 $transient = array();
201 }
202
203 Arr::set(
204 $transient,
205 md5( $query ),
206 array(
207 'query' => $query,
208 'css' => $css,
209 )
210 );
211
212 set_transient( $this->queries_transient, $transient );
213 }
214
215 public function addQueryToCache( $query ) {
216 $transient = get_transient( $this->queries_transient );
217 if ( ! is_array( $transient ) ) {
218 $transient = array();
219 }
220
221 $key = md5( $query );
222
223 if ( isset( $transient[ $key ] ) ) {
224 return;
225 }
226
227 Arr::set(
228 $transient,
229 $key,
230 array(
231 'query' => $query,
232 )
233 );
234 set_transient( $this->queries_transient, $transient );
235 }
236
237
238 public function enqueueFonts( $query ) {
239 $cached = $this->getCachedQueryData( $query );
240 if ( ! $cached ) {
241 $this->addQueryToCache(
242 $query,
243 array(
244 'query' => $query,
245 )
246 );
247 }
248
249 wp_enqueue_style(
250 'kubio-local-google-fonts',
251 add_query_arg(
252 array(
253 'action' => $this->fonts_css_action,
254 'key' => md5( $query ),
255 ),
256 site_url()
257 )
258 );
259 }
260
261 public function resolveFont() {
262
263 $font_file = sanitize_text_field( Arr::get( $_REQUEST, 'font', '' ) );
264 $security_key = sanitize_text_field( Arr::get( $_REQUEST, 'security', '' ) );
265
266 $valid_nonce = $this->verifySecurityKey( $security_key, "{$this->font_file_action}_{$font_file}" );
267
268 if ( ! $valid_nonce ) {
269 wp_die( __( 'Frobidden', 'kubio' ), 403 );
270 }
271
272 $content = $this->resolveFontFileContent( $font_file );
273
274 if ( is_wp_error( $content ) ) {
275 wp_die( $content, 404 );
276 }
277
278 $this_year = strtotime( date( 'Y' ) . '-01-01' );
279 header( 'Content-type: font/woff2' );
280 header( 'Cache-control: public' );
281 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $this_year ) . ' GMT' );
282 header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', $this_year + YEAR_IN_SECONDS ) . ' GMT' );
283 header( 'Etag: ' . md5( base64_encode( $content ) ) );
284
285 die( $content );
286 }
287
288 private function resolveFontFileContent( $font_file ) {
289 if ( $this->localFontFileExists( $font_file ) ) {
290 return file_get_contents( $this->getLocalFontFilePath( $font_file ) );
291 }
292
293 if ( ! is_writable( $this->local_fonts_dir ) ) {
294 return new \WP_Error( 'folder_not_writable' );
295 }
296
297 $google_font_url = "{$this->google_font_url}/{$font_file}";
298
299 $reponse = wp_remote_get( $google_font_url );
300 if ( is_wp_error( $reponse ) ) {
301 return new \WP_Error( 'could_not_retrieve_url' );
302 }
303
304 $content = wp_remote_retrieve_body( $reponse );
305
306 $path = $this->saveFontContentToLocalFile( $font_file, $content );
307 file_put_contents( $path, $content );
308
309 return $content;
310
311 }
312
313 private function getSecuritySalt() {
314 if ( defined( 'NONCE_KEY' ) ) {
315 return NONCE_KEY;
316 }
317
318 if ( define( 'SECURE_AUTH_KEY' ) ) {
319 return SECURE_AUTH_KEY;
320 }
321
322 if ( define( 'AUTH_KEY' ) ) {
323 return AUTH_KEY;
324 }
325
326 if ( define( 'SECURE_AUTH_SALT' ) ) {
327 return SECURE_AUTH_SALT;
328 }
329
330 if ( define( 'AUTH_SALT' ) ) {
331 return AUTH_SALT;
332 }
333
334 $pro_activation_time = Flags::get( 'kubio_pro_activation_time' );
335
336 if ( $pro_activation_time ) {
337 return $pro_activation_time;
338 }
339
340 $activation_time = Flags::get( 'kubio_activation_time' );
341
342 if ( $activation_time ) {
343 return $activation_time;
344 }
345
346 return uniqid( time() );
347 }
348
349 public function createSecurityKey( $action ) {
350 return wp_hash( $this->getSecuritySalt() . '|' . $action );
351 }
352
353 public function verifySecurityKey( $nonce, $action ) {
354 return $nonce === $this->createSecurityKey( $action );
355 }
356
357
358 public function addAdminAjaxActions() {
359 add_action( "wp_ajax_{$this->font_file_action}", array( $this, 'resolveFont' ) );
360 add_action( "wp_ajax_nopriv_{$this->font_file_action}", array( $this, 'resolveFont' ) );
361
362 add_action( 'plugins_loaded', array( $this, 'resolveFontsCSS' ) );
363
364 }
365
366 public static function enqueuLocalGoogleFonts( $fonts_query ) {
367 return GoogleFontsLocalLoader::getInstance()->enqueueFonts( $fonts_query );
368 }
369
370
371 public static function registerFontResolver() {
372 return GoogleFontsLocalLoader::getInstance()->addAdminAjaxActions();
373 }
374
375 }
376