PluginProbe
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts / trunk
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts vtrunk
2.8.14 2.8.13 2.8.12 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.11 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 trunk 1.0.1 1.0.2 1.1.0 1.1.1 All 147 releases
content-blocks-builder / includes / typography.php

typography.php in Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts trunk, at includes/typography.php

510 lines 14.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The typography settings
4 *
5 * @package BoldBlocks
6 * @author Phi Phan <mrphipv@gmail.com>
7 * @copyright Copyright (c) 2022, Phi Phan
8 */
9
10 namespace BoldBlocks;
11
12 // Exit if accessed directly.
13 defined( 'ABSPATH' ) || exit;
14
15 if ( ! class_exists( Typography::class ) ) :
16 /**
17 * The controller class for typography settings.
18 */
19 class Typography extends CoreComponent {
20 /**
21 * Run main hooks
22 *
23 * @return void
24 */
25 public function run() {
26 // Register setting fields.
27 add_action( 'init', [ $this, 'register_setting_fields' ] );
28
29 // Add rest api endpoint to query google fonts.
30 add_action( 'rest_api_init', [ $this, 'register_google_fonts_endpoint' ] );
31
32 // Load typography style.
33 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_frontend_typography_scripts' ], PHP_INT_MAX );
34 add_action( 'enqueue_block_assets', [ $this, 'enqueue_block_editor_typography_scripts' ] );
35
36 add_filter( 'wp_resource_hints', [ $this, 'preconnect_fonts' ], 10, 2 );
37 add_action( 'wp_head', [ $this, 'preload_fonts' ], 5 );
38
39 // Add body class.
40 add_filter( 'body_class', [ $this, 'add_typography_to_body_class' ] );
41 }
42
43 /**
44 * Register custom setting fields
45 *
46 * @return void
47 */
48 public function register_setting_fields() {
49 // Setting fields.
50 // phpcs:disable PluginCheck.CodeAnalysis.SettingSanitization.register_settingDynamic
51 register_setting(
52 'cbb',
53 'cbb_enable_typography',
54 [
55 'type' => 'boolean',
56 'sanitize_callback' => 'rest_sanitize_boolean',
57 'show_in_rest' => [
58 'name' => 'EnableTypography',
59 ],
60 'default' => false,
61 ]
62 );
63
64 register_setting(
65 'cbb',
66 'cbb_use_bunny_fonts',
67 [
68 'type' => 'boolean',
69 'sanitize_callback' => 'rest_sanitize_boolean',
70 'show_in_rest' => [
71 'name' => 'UseBunnyFonts',
72 ],
73 'default' => false,
74 ]
75 );
76
77 register_setting(
78 'cbb',
79 'cbb_typography',
80 [
81 'type' => 'object',
82 'show_in_rest' => array(
83 'name' => 'CBBTypography',
84 'schema' => array(
85 'type' => 'object',
86 'properties' => array(
87 'fonts' => array(
88 'type' => 'string',
89 ),
90 ),
91 'additionalProperties' => array(
92 'type' => 'string',
93 ),
94 ),
95 ),
96 ]
97 );
98 // phpcs:enable PluginCheck.CodeAnalysis.SettingSanitization.register_settingDynamic
99
100 register_meta(
101 'post',
102 'boldblocks_typography',
103 [
104 'single' => true,
105 'type' => 'object',
106 'show_in_rest' => array(
107 'name' => 'CBBTypography',
108 'schema' => array(
109 'type' => 'object',
110 'additionalProperties' => array(
111 'type' => 'string',
112 ),
113 ),
114 ),
115 'auth_callback' => function () {
116 return current_user_can( 'edit_posts' );
117 },
118 ]
119 );
120 }
121
122 /**
123 * Build a custom endpoint to query google fonts.
124 *
125 * @return void
126 */
127 public function register_google_fonts_endpoint() {
128 register_rest_route(
129 'boldblocks/v1',
130 '/getGoogleFonts/',
131 array(
132 'methods' => 'GET',
133 'callback' => [ $this, 'get_google_fonts' ],
134 'permission_callback' => function () {
135 return current_user_can( 'publish_posts' );
136 },
137 )
138 );
139 }
140
141 /**
142 * Get google fonts.
143 *
144 * @param WP_REST_Request $request The request object.
145 * @return void
146 */
147 public function get_google_fonts( $request ) {
148 // fonts file path.
149 $fonts_file = $this->the_plugin_instance->get_file_path( 'data/fonts/google-fonts.json' );
150
151 // Send the error if the fonts file is not exists.
152 if ( ! \file_exists( $fonts_file ) ) {
153 wp_send_json_error( __( 'The google-fonts.json file is not exists.', 'content-blocks-builder' ), 500 );
154 }
155
156 // Parse json.
157 $fonts = wp_json_file_decode( $fonts_file, [ 'associative' => true ] );
158
159 wp_send_json(
160 [
161 'data' => $fonts,
162 'success' => true,
163 ]
164 );
165 }
166
167 /**
168 * Enqueue scripts for typography
169 *
170 * @return void
171 */
172 public function enqueue_frontend_typography_scripts() {
173 $post_id = 0;
174
175 if ( is_singular() ) {
176 $post_id = get_queried_object_id();
177 }
178
179 // Enqueue custom fonts.
180 $this->enqueue_custom_fonts( $post_id );
181 }
182
183 /**
184 * Enqueue typography for editor
185 *
186 * @return void
187 */
188 public function enqueue_block_editor_typography_scripts() {
189 if ( ! is_admin() ) {
190 return;
191 }
192
193 global $post;
194
195 $screen = get_current_screen();
196 $is_editor = $screen->is_block_editor;
197
198 // Bail if it's not a page that has block editor.
199 if ( ! $is_editor ) {
200 return;
201 }
202
203 $post_id = $screen->base === 'post' && $post ? $post->ID : 0;
204
205 // Enqueue custom fonts.
206 $this->enqueue_custom_fonts( $post_id, $is_editor );
207 }
208
209 /**
210 * Enqueue custom fonts
211 *
212 * @param int $post_id
213 * @param boolean $is_editor
214 * @return void
215 */
216 private function enqueue_custom_fonts( $post_id, $is_editor = false ) {
217 $use_bunny_fonts = get_option( 'cbb_use_bunny_fonts' );
218
219 $fonts_url = $this->get_fonts_url( $post_id, $use_bunny_fonts );
220 if ( $fonts_url ) {
221 $prefix = $is_editor ? 'https:' : '';
222 $service_url = $use_bunny_fonts ? '//fonts.bunny.net/css?family=' : '//fonts.googleapis.com/css2?';
223 // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
224 wp_enqueue_style( 'boldblocks-custom-fonts', $prefix . $service_url . $fonts_url, [], null );
225
226 $typography_style = $this->get_typography_style( $post_id, $is_editor );
227 wp_add_inline_style( 'boldblocks-custom-fonts', $typography_style );
228
229 if ( $is_editor ) {
230 $wp_styles = wp_styles();
231 $custom_blocks = $wp_styles->query( $this->the_plugin_instance->get_component( CustomBlocks::class )->custom_blocks_handle, 'registered' );
232
233 if ( $custom_blocks ) {
234 $custom_blocks->deps[] = 'boldblocks-custom-fonts';
235 }
236 }
237 }
238 }
239
240 /**
241 * Add preconnect for font service.
242 *
243 * @param array $urls URLs to print for resource hints.
244 * @param string $relation_type The relation type the URLs are printed.
245 * @return array $urls URLs to print for resource hints.
246 */
247 public function preconnect_fonts( $urls, $relation_type ) {
248 if ( wp_style_is( 'boldblocks-custom-fonts', 'queue' ) && 'preconnect' === $relation_type ) {
249 $use_bunny_fonts = get_option( 'cbb_use_bunny_fonts' );
250
251 $urls[] = array(
252 'href' => $use_bunny_fonts ? '//fonts.bunny.net' : '//fonts.gstatic.com',
253 'crossorigin',
254 );
255 }
256
257 return $urls;
258 }
259
260 /**
261 * Add preload for fonts.
262 *
263 * @return void
264 */
265 public function preload_fonts() {
266 $wp_styles = wp_styles();
267 $style = $wp_styles->query( 'boldblocks-custom-fonts', 'registered' );
268
269 if ( $style ) {
270 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
271 echo "<link rel='preload' as='style' href='{$style->src}'/>";
272 }
273 }
274
275 /**
276 * Load google fonts
277 *
278 * @param int $post_id
279 * @param boolean $use_bunny_fonts
280 * @return void
281 */
282 private function get_fonts_url( $post_id, $use_bunny_fonts ) {
283 $typography = $this->get_custom_typography( $post_id );
284
285 if ( ! $typography ) {
286 return;
287 }
288
289 $family_urls = [];
290
291 $body = $typography['body'] ?? [];
292 $body_font_family = $body['fontFamily'] ?? '';
293 $body_font_variants = $body['fontVariants'] ?? [];
294
295 $body_font_url = $this->get_font_url( $body_font_family, $body_font_variants, $use_bunny_fonts );
296 if ( $body_font_url ) {
297 $family_urls[] = $body_font_url;
298 }
299
300 $headings = $typography['headings'] ?? [];
301 $headings_font_family = $headings['fontFamily'] ?? '';
302 $headings_font_variants = $headings['fontVariants'] ?? [];
303
304 $headings_font_url = $this->get_font_url( $headings_font_family, $headings_font_variants, $use_bunny_fonts );
305 if ( $headings_font_url ) {
306 $family_urls[] = $headings_font_url;
307 }
308
309 if ( count( $family_urls ) > 0 ) {
310 $glue = $use_bunny_fonts ? '|' : '&';
311 return implode( $glue, $family_urls ) . '&display=swap';
312 }
313
314 return;
315 }
316
317 /**
318 * Get font url
319 *
320 * @param string $font_family
321 * @param array $font_variants
322 * @param boolean $use_bunny_fonts
323 * @return string
324 */
325 private function get_font_url( $font_family, $font_variants, $use_bunny_fonts ) {
326 $family_url = '';
327
328 if ( $font_family ) {
329 $family_url = $use_bunny_fonts ? \str_replace( ' ', '+', $font_family ) : 'family=' . \str_replace( ' ', '+', $font_family );
330 if ( $font_variants ) {
331 if ( ! $use_bunny_fonts ) {
332 usort(
333 $font_variants,
334 function( $val1, $val2 ) {
335 $val1_has_italic = strpos( $val1, 'italic' ) !== false;
336 $val2_has_italic = strpos( $val2, 'italic' ) !== false;
337
338 if ( $val1_has_italic && ! $val2_has_italic ) {
339 return 1;
340 } elseif ( ! $val1_has_italic && $val2_has_italic ) {
341 return -1;
342 }
343
344 return strcmp( $val1, $val2 );
345 }
346 );
347 $family_url .= ':ital,wght@';
348 foreach ( $font_variants as $variant ) {
349 if ( strpos( $variant, 'italic' ) === false ) {
350 $family_url .= '0,' . $variant . ';';
351 } else {
352 $family_url .= '1,' . str_replace( 'italic', '', $variant ) . ';';
353 }
354 }
355 } else {
356 $family_url .= array_reduce(
357 $font_variants,
358 function ( $accumulator, $item ) {
359 $variant = \str_replace( 'italic', 'i', $item );
360 if ( empty( $accumulator ) ) {
361 $accumulator = ':' . $variant;
362 } else {
363 $accumulator .= ',' . $variant;
364 }
365
366 return $accumulator;
367 },
368 ''
369 );
370 }
371 }
372 }
373
374 return trim( $family_url, ';' );
375 }
376
377 /**
378 * Build typography style
379 *
380 * @param int $post_id
381 * @param boolean $is_editor
382 * @return string
383 */
384 private function get_typography_style( $post_id = 0, $is_editor = false ) {
385 $typography = $this->get_custom_typography( $post_id );
386
387 if ( ! $typography ) {
388 return;
389 }
390
391 $body = $this->get_typography_style_by_type( 'body', $typography['body'] ?? [], $is_editor );
392 $headings = $this->get_typography_style_by_type( 'headings', $typography['headings'] ?? [], $is_editor );
393
394 if ( $is_editor ) {
395 $style = '.editor-styles-wrapper {' . $body['vars'] . $headings['vars'] . '}';
396 } else {
397 $style = ':root {' . $body['vars'] . $headings['vars'] . '}';
398 }
399 $style .= $body['style'] . $headings['style'];
400
401 return $style;
402 }
403
404 /**
405 * Build CSS variables and style by type
406 *
407 * @param string $type
408 * @param array $type_values
409 * @param boolean $is_editor
410 * @return array
411 */
412 private function get_typography_style_by_type( $type, $type_values, $is_editor = false ) {
413 $type_styles = [];
414 $type_vars = [];
415
416 if ( empty( $type_values ) ) {
417 return [
418 'vars' => '',
419 'style' => '',
420 ];
421 }
422
423 $editor_selector = '.editor-styles-wrapper .is-root-container';
424 $frontend_selector = '.has-boldblocks-typography';
425
426 $font_family = '--cbb--' . $type . '--font-family';
427 $type_styles[] = '.' . $type . '-font-family { font-family: var(' . $font_family . ') !important; }';
428
429 if ( 'headings' !== $type ) {
430 $selector = $type === 'body' ? 'body' : '.' . $type;
431 if ( $is_editor ) {
432 $selector = 'body' === $type ? "{$editor_selector}, {$editor_selector} p" : "{$editor_selector} ." . $type;
433 } else {
434 $selector = 'body' === $type ? "{$selector}, {$frontend_selector} p" : "{$frontend_selector} {$selector}";
435 }
436 $type_styles[] = $selector . ' {';
437 if ( $type_values['fontFamily'] ?? '' ) {
438 $type_vars[] = $font_family . ': "' . $type_values['fontFamily'] . '", ' . ( $type_values['genericFamily'] ?? 'sans-serif' ) . ';';
439 $type_styles[] = 'font-family: var(' . $font_family . ');';
440 }
441
442 $type_styles[] = '}';
443 } else {
444 if ( $type_values['fontFamily'] ?? '' ) {
445 if ( $is_editor ) {
446 $type_styles[] = ".editor-styles-wrapper .wp-block.wp-block-post-title, {$editor_selector} h1, {$editor_selector} .h1, {$editor_selector} h2, {$editor_selector} .h2, {$editor_selector} h3, {$editor_selector} .h3, {$editor_selector} h4, {$editor_selector} .h4, {$editor_selector} h5, {$editor_selector} .h5, {$editor_selector} h6, {$editor_selector} .h6 {";
447 } else {
448 $type_styles[] = "{$frontend_selector} h1,{$frontend_selector} .h1,{$frontend_selector} h2,{$frontend_selector} .h2,{$frontend_selector} h3,{$frontend_selector} .h3,{$frontend_selector} h4,{$frontend_selector} .h4,{$frontend_selector} h5,{$frontend_selector} .h5,{$frontend_selector} h6,{$frontend_selector} .h6 {";
449 }
450 $type_vars[] = $font_family . ':"' . $type_values['fontFamily'] . '", ' . ( $type_values['genericFamily'] ?? 'sans-serif' ) . ';';
451 $type_styles[] = 'font-family: var(' . $font_family . ');';
452 $type_styles[] = '} ';
453 }
454 }
455
456 return [
457 'vars' => \implode( '', $type_vars ),
458 'style' => \implode( '', $type_styles ),
459 ];
460 }
461
462 /**
463 * Check whether current site support custom typography
464 *
465 * @param int $post_id
466 * @return mixed
467 */
468 private function get_custom_typography( $post_id = 0 ) {
469 $enable_typography = \get_option( 'cbb_enable_typography' );
470
471 if ( ! $enable_typography ) {
472 return false;
473 }
474
475 $typography_raw = $post_id > 0 ? get_post_meta( $post_id, 'boldblocks_typography', true ) : false;
476 if ( empty( $typography_raw ) || ! is_array( $typography_raw ) || empty( $typography_raw['fonts'] ) ) {
477 $typography_raw = \get_option( 'cbb_typography' );
478 }
479
480 if ( ! is_array( $typography_raw ) || empty( $typography_raw['fonts'] ) ) {
481 return;
482 }
483
484 $fonts = $typography_raw ? \json_decode( $typography_raw['fonts'], true ) : '';
485
486 if ( empty( $fonts ) ) {
487 return;
488 }
489
490 return $fonts;
491 }
492
493 /**
494 * Add a custom css class to body for typography
495 *
496 * @param array $classes
497 * @return string
498 */
499 public function add_typography_to_body_class( $classes ) {
500 $enable_typography = get_option( 'cbb_enable_typography' );
501
502 if ( $enable_typography ) {
503 $classes[] = 'has-boldblocks-typography';
504 }
505
506 return $classes;
507 }
508 }
509 endif;
510