PluginProbe
Block Animations, Motion & Scroll Effects – Ghost Kit / trunk
Block Animations, Motion & Scroll Effects – Ghost Kit vtrunk
3.7.1 3.7.0 3.6.1 trunk 1.6.3 2.25.0 3.3.0 3.3.1 3.3.2 3.3.3 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.6.0
ghostkit / classes / class-rest.php

class-rest.php in Block Animations, Motion & Scroll Effects – Ghost Kit trunk, at classes/class-rest.php

1,970 lines 54.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Rest API functions
4 *
5 * @package ghostkit
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Class GhostKit_Rest
14 */
15 class GhostKit_Rest extends WP_REST_Controller {
16 /**
17 * Namespace.
18 *
19 * @var string
20 */
21 protected $namespace = 'ghostkit/v';
22
23 /**
24 * Version.
25 *
26 * @var string
27 */
28 protected $version = '1';
29
30 /**
31 * GhostKit_Rest constructor.
32 */
33 public function __construct() {
34 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
35 }
36
37 /**
38 * Register rest routes.
39 */
40 public function register_routes() {
41 $namespace = $this->namespace . $this->version;
42
43 // Get layouts list.
44 register_rest_route(
45 $namespace,
46 '/get_customizer/',
47 array(
48 'methods' => WP_REST_Server::READABLE,
49 'callback' => array( $this, 'get_customizer' ),
50 'permission_callback' => array( $this, 'get_customizer_permission' ),
51 )
52 );
53
54 // Get attachment image <img> tag.
55 register_rest_route(
56 $namespace,
57 '/get_attachment_image/(?P<id>[\d]+)',
58 array(
59 'methods' => WP_REST_Server::READABLE,
60 'callback' => array( $this, 'get_attachment_image' ),
61 'permission_callback' => array( $this, 'get_attachment_image_permission' ),
62 )
63 );
64
65 // Get Instagram profile.
66 register_rest_route(
67 $namespace,
68 '/get_instagram_profile/',
69 array(
70 'methods' => WP_REST_Server::READABLE,
71 'callback' => array( $this, 'get_instagram_profile' ),
72 'permission_callback' => array( $this, 'get_instagram_profile_permission' ),
73 )
74 );
75
76 // Get Instagram feed.
77 register_rest_route(
78 $namespace,
79 '/get_instagram_feed/',
80 array(
81 'methods' => WP_REST_Server::READABLE,
82 'callback' => array( $this, 'get_instagram_feed' ),
83 'permission_callback' => array( $this, 'get_instagram_feed_permission' ),
84 )
85 );
86
87 // Get Twitter profile.
88 register_rest_route(
89 $namespace,
90 '/get_twitter_profile/',
91 array(
92 'methods' => WP_REST_Server::READABLE,
93 'callback' => array( $this, 'get_twitter_profile' ),
94 'permission_callback' => array( $this, 'get_twitter_profile_permission' ),
95 )
96 );
97
98 // Get Twitter feed.
99 register_rest_route(
100 $namespace,
101 '/get_twitter_feed/',
102 array(
103 'methods' => WP_REST_Server::READABLE,
104 'callback' => array( $this, 'get_twitter_feed' ),
105 'permission_callback' => array( $this, 'get_twitter_feed_permission' ),
106 )
107 );
108
109 // Get TOC.
110 register_rest_route(
111 $namespace,
112 '/get_table_of_contents/',
113 array(
114 'methods' => WP_REST_Server::READABLE,
115 'callback' => array( $this, 'get_table_of_contents' ),
116 'args' => array(
117 'headings' => array(
118 'default' => array(),
119 'sanitize_callback' => array( $this, 'sanitize_toc_headings' ),
120 ),
121 'allowedHeaders' => array(
122 'default' => array(),
123 'sanitize_callback' => array( $this, 'sanitize_toc_allowed_headers' ),
124 ),
125 'listStyle' => array(
126 'default' => 'ol',
127 'sanitize_callback' => array( $this, 'sanitize_toc_list_style' ),
128 ),
129 ),
130 'permission_callback' => '__return_true',
131 )
132 );
133
134 // Get Templates.
135 register_rest_route(
136 $namespace,
137 '/get_templates/',
138 array(
139 'methods' => WP_REST_Server::READABLE,
140 'callback' => array( $this, 'get_templates' ),
141 'permission_callback' => '__return_true',
142 )
143 );
144
145 // Get template data.
146 register_rest_route(
147 $namespace,
148 '/get_template_data/',
149 array(
150 'methods' => WP_REST_Server::READABLE,
151 'callback' => array( $this, 'get_template_data' ),
152 'permission_callback' => '__return_true',
153 )
154 );
155
156 // Get Custom Code.
157 register_rest_route(
158 $namespace,
159 '/get_custom_code/',
160 array(
161 'methods' => WP_REST_Server::READABLE,
162 'callback' => array( $this, 'get_custom_code' ),
163 'permission_callback' => array( $this, 'get_custom_code_permission' ),
164 )
165 );
166
167 // Update Custom Code.
168 register_rest_route(
169 $namespace,
170 '/update_custom_code/',
171 array(
172 'methods' => WP_REST_Server::EDITABLE,
173 'callback' => array( $this, 'update_custom_code' ),
174 'permission_callback' => array( $this, 'update_custom_code_permission' ),
175 )
176 );
177
178 // Update Color Palette.
179 register_rest_route(
180 $namespace,
181 '/update_color_palette/',
182 array(
183 'methods' => WP_REST_Server::EDITABLE,
184 'callback' => array( $this, 'update_color_palette' ),
185 'permission_callback' => array( $this, 'update_color_palette_permission' ),
186 )
187 );
188
189 // Get Typography.
190 register_rest_route(
191 $namespace,
192 '/get_custom_typography/',
193 array(
194 'methods' => WP_REST_Server::READABLE,
195 'callback' => array( $this, 'get_custom_typography' ),
196 'permission_callback' => array( $this, 'get_custom_typography_permission' ),
197 )
198 );
199
200 // Update Typography.
201 register_rest_route(
202 $namespace,
203 '/update_custom_typography/',
204 array(
205 'methods' => WP_REST_Server::EDITABLE,
206 'callback' => array( $this, 'update_custom_typography' ),
207 'permission_callback' => array( $this, 'update_custom_typography_permission' ),
208 )
209 );
210
211 // Update Google Maps API key.
212 register_rest_route(
213 $namespace,
214 '/update_google_maps_api_key/',
215 array(
216 'methods' => WP_REST_Server::EDITABLE,
217 'callback' => array( $this, 'update_google_maps_api_key' ),
218 'permission_callback' => array( $this, 'update_google_maps_api_key_permission' ),
219 )
220 );
221
222 // Update Google reCaptcha keys.
223 register_rest_route(
224 $namespace,
225 '/update_google_recaptcha_keys/',
226 array(
227 'methods' => WP_REST_Server::EDITABLE,
228 'callback' => array( $this, 'update_google_recaptcha_keys' ),
229 'permission_callback' => array( $this, 'update_google_recaptcha_keys_permission' ),
230 )
231 );
232
233 // Update Disabled Blocks.
234 register_rest_route(
235 $namespace,
236 '/update_disabled_blocks/',
237 array(
238 'methods' => WP_REST_Server::EDITABLE,
239 'callback' => array( $this, 'update_disabled_blocks' ),
240 'permission_callback' => array( $this, 'update_disabled_blocks_permission' ),
241 )
242 );
243
244 // Update Settings.
245 register_rest_route(
246 $namespace,
247 '/update_settings/',
248 array(
249 'methods' => WP_REST_Server::EDITABLE,
250 'callback' => array( $this, 'update_settings' ),
251 'permission_callback' => array( $this, 'update_settings_permission' ),
252 )
253 );
254
255 // Get Custom Fonts.
256 register_rest_route(
257 $namespace,
258 '/get_custom_fonts/',
259 array(
260 'methods' => WP_REST_Server::READABLE,
261 'callback' => array( $this, 'get_custom_fonts' ),
262 'permission_callback' => array( $this, 'get_custom_fonts_permission' ),
263 )
264 );
265
266 // Update Custom Fonts.
267 register_rest_route(
268 $namespace,
269 '/update_custom_fonts/',
270 array(
271 'methods' => WP_REST_Server::EDITABLE,
272 'callback' => array( $this, 'update_custom_fonts' ),
273 'permission_callback' => array( $this, 'update_custom_fonts_permission' ),
274 )
275 );
276 }
277
278 /**
279 * Get read fonts permissions.
280 *
281 * @return bool
282 */
283 public function get_custom_fonts_permission() {
284 if ( ! current_user_can( 'edit_theme_options' ) ) {
285 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
286 }
287
288 return true;
289 }
290
291 /**
292 * Get edit fonts permissions.
293 *
294 * @return bool
295 */
296 public function update_custom_fonts_permission() {
297 if ( ! current_user_can( 'manage_options' ) ) {
298 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
299 }
300
301 return true;
302 }
303
304 /**
305 * Get read customizer permissions.
306 *
307 * @return bool
308 */
309 public function get_customizer_permission() {
310 if ( ! current_user_can( 'edit_theme_options' ) ) {
311 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change Customizer options.', 'ghostkit' ), true );
312 }
313 return true;
314 }
315
316 /**
317 * Get attachment image <img> tag permissions.
318 *
319 * @return bool
320 */
321 public function get_attachment_image_permission() {
322 return true;
323 }
324
325 /**
326 * Get Instagram feed permissions.
327 *
328 * @return bool
329 */
330 public function get_instagram_feed_permission() {
331 return true;
332 }
333
334 /**
335 * Get Instagram profile permissions.
336 *
337 * @return bool
338 */
339 public function get_instagram_profile_permission() {
340 return true;
341 }
342
343 /**
344 * Get Twitter feed permissions.
345 *
346 * @return bool
347 */
348 public function get_twitter_feed_permission() {
349 return true;
350 }
351
352 /**
353 * Get Twitter profile permissions.
354 *
355 * @return bool
356 */
357 public function get_twitter_profile_permission() {
358 return true;
359 }
360
361 /**
362 * Get read custom code permissions.
363 *
364 * @return bool
365 */
366 public function get_custom_code_permission() {
367 if ( ! current_user_can( 'edit_theme_options' ) ) {
368 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
369 }
370
371 return true;
372 }
373
374 /**
375 * Get edit custom code permissions.
376 *
377 * @return bool
378 */
379 public function update_custom_code_permission() {
380 if ( ! current_user_can( 'manage_options' ) ) {
381 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
382 }
383
384 return true;
385 }
386
387 /**
388 * Get edit color palette permissions.
389 *
390 * @return bool
391 */
392 public function update_color_palette_permission() {
393 if ( ! current_user_can( 'manage_options' ) ) {
394 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
395 }
396
397 return true;
398 }
399
400 /**
401 * Get read typography permissions.
402 *
403 * @return bool
404 */
405 public function get_custom_typography_permission() {
406 if ( ! current_user_can( 'edit_theme_options' ) ) {
407 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
408 }
409
410 return true;
411 }
412
413 /**
414 * Get edit typography permissions.
415 *
416 * @return bool
417 */
418 public function update_custom_typography_permission() {
419 if ( ! current_user_can( 'manage_options' ) ) {
420 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
421 }
422
423 return true;
424 }
425
426 /**
427 * Get read Google Maps API key permissions.
428 *
429 * @return bool
430 */
431 public function update_google_maps_api_key_permission() {
432 if ( ! current_user_can( 'manage_options' ) ) {
433 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
434 }
435
436 return true;
437 }
438
439 /**
440 * Get read Google reCaptcha API keys permissions.
441 *
442 * @return bool
443 */
444 public function update_google_recaptcha_keys_permission() {
445 if ( ! current_user_can( 'manage_options' ) ) {
446 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
447 }
448
449 return true;
450 }
451
452 /**
453 * Get edit options permissions.
454 *
455 * @return bool
456 */
457 public function update_disabled_blocks_permission() {
458 if ( ! current_user_can( 'manage_options' ) ) {
459 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
460 }
461
462 return true;
463 }
464
465 /**
466 * Get edit options permissions.
467 *
468 * @return bool
469 */
470 public function update_settings_permission() {
471 if ( ! current_user_can( 'manage_options' ) ) {
472 return $this->error( 'user_dont_have_permission', __( 'User don\'t have permissions to change options.', 'ghostkit' ), true );
473 }
474
475 return true;
476 }
477
478 /**
479 * Get custom fonts.
480 *
481 * @return mixed
482 */
483 public function get_custom_fonts() {
484 $fonts = get_option( 'ghostkit_fonts_settings', array() );
485 $fonts = apply_filters( 'gkt_rest_get_custom_fonts', $fonts );
486
487 if ( is_array( $fonts ) ) {
488 return $this->success( $fonts );
489 } else {
490 return $this->error( 'no_fonts', __( 'Custom fonts not found.', 'ghostkit' ) );
491 }
492 }
493
494 /**
495 * Update fonts.
496 *
497 * @param WP_REST_Request $request request object.
498 *
499 * @return mixed
500 */
501 public function update_custom_fonts( WP_REST_Request $request ) {
502 $new_fonts = $request->get_param( 'data' );
503 $updated_option = array();
504
505 $new_fonts = apply_filters( 'gkt_rest_update_custom_fonts', $new_fonts );
506
507 if ( is_array( $new_fonts ) ) {
508 $current_fonts = get_option( 'ghostkit_fonts_settings', array() );
509
510 $updated_option = array_merge( is_array( $current_fonts ) ? $current_fonts : array(), $new_fonts );
511
512 update_option( 'ghostkit_fonts_settings', $updated_option );
513 }
514
515 return $this->success( $updated_option );
516 }
517
518 /**
519 * Get customizer data.
520 *
521 * @return mixed
522 */
523 public function get_customizer() {
524 $options = get_option( 'ghostkit_customizer_fields' );
525
526 if ( ! empty( $options ) ) {
527 return $this->success( $options );
528 } else {
529 return $this->error( 'no_options_found', __( 'Options not found.', 'ghostkit' ) );
530 }
531 }
532
533 /**
534 * Get attachment image <img> tag.
535 *
536 * @param WP_REST_Request $request request object.
537 *
538 * @return mixed
539 */
540 public function get_attachment_image( WP_REST_Request $request ) {
541 $id = $request->get_param( 'id' );
542 $size = $request->get_param( 'size' );
543 $icon = $request->get_param( 'icon' );
544 $attr = $request->get_param( 'attr' );
545 $div_tag = $request->get_param( 'div_tag' );
546
547 if ( ! $id ) {
548 return $this->error( 'no_id_found', __( 'Provide image ID.', 'ghostkit' ) );
549 }
550
551 $attr = isset( $attr ) && $attr && is_array( $attr ) ? $attr : array();
552
553 if ( $div_tag ) {
554 $image_url = wp_get_attachment_image_url( $id, $size, $icon );
555
556 if ( ! isset( $attr['style'] ) ) {
557 $attr['style'] = '';
558 }
559
560 $attr['style'] .= 'background-image: url("' . esc_url( $image_url ) . '");';
561
562 $attr = array_map( 'esc_attr', $attr );
563 $image = '<div';
564
565 foreach ( $attr as $name => $value ) {
566 $image .= " $name=" . '"' . $value . '"';
567 }
568
569 $image .= '></div>';
570 } else {
571 $image_src = wp_get_attachment_image_src( $id, $size, $icon );
572
573 if ( $image_src ) {
574 list( $src, $width, $height ) = $image_src;
575
576 $alt = trim( wp_strip_all_tags( get_post_meta( $id, '_wp_attachment_image_alt', true ) ) );
577
578 if ( $alt ) {
579 $attr['alt'] = $alt;
580 }
581
582 if ( ! isset( $attr['class'] ) ) {
583 $attr['class'] = 'wp-image-' . $id;
584 } else {
585 $attr['class'] = 'wp-image-' . $id . ' ' . $attr['class'];
586 }
587
588 $attr['width'] = $width;
589 $attr['height'] = $height;
590
591 $attrs_str = '';
592
593 if ( isset( $attr ) && is_array( $attr ) ) {
594 foreach ( $attr as $name => $val ) {
595 $attrs_str .= ' ' . $name . '="' . esc_attr( $val ) . '"';
596 }
597 }
598
599 $image = '<img src="' . esc_url( $src ) . '"' . $attrs_str . ' />';
600 }
601 }
602
603 if ( isset( $image ) && $image ) {
604 return $this->success( $image );
605 } else {
606 return $this->error( 'no_image_found', __( 'Image not found.', 'ghostkit' ) );
607 }
608 }
609
610 /**
611 * Get Instagram feed.
612 *
613 * @param WP_REST_Request $request request object.
614 *
615 * @return mixed
616 */
617 public function get_instagram_feed( WP_REST_Request $request ) {
618 $cache_name = 'ghostkit_instagram_feed_cache';
619 $cache_expiration = $request->get_param( 'cache_expiration' ) ? $request->get_param( 'cache_expiration' ) : DAY_IN_SECONDS;
620 $count = $request->get_param( 'count' ) ? $request->get_param( 'count' ) : 6;
621 $access_token = $request->get_param( 'access_token' );
622
623 if ( ! $access_token ) {
624 return $this->error( 'no_token_found', __( 'Provide Instagram Access Token.', 'ghostkit' ) );
625 }
626
627 $hash = md5(
628 wp_json_encode(
629 array(
630 $access_token,
631 $count,
632 $cache_expiration,
633 )
634 )
635 );
636
637 $cache_name = $cache_name . '_' . $hash;
638
639 // get cached data.
640 $result = get_transient( $cache_name );
641
642 // if there is no cache available, request instagram feed.
643 if ( false === $result && $access_token ) {
644 // Make Requests.
645 $feed = wp_remote_get( 'https://api.instagram.com/v1/users/self/media/recent/?access_token=' . $access_token . '&count=' . $count );
646
647 if ( ! is_wp_error( $feed ) && isset( $feed['body'] ) ) {
648 $feed = json_decode( $feed['body'], true );
649 } else {
650 $feed = false;
651 }
652
653 if ( $feed && is_array( $feed ) ) {
654 $result = $feed;
655 set_transient( $cache_name, $result, $cache_expiration );
656 }
657 }
658
659 if ( $result ) {
660 return $this->success( $result );
661 } else {
662 return $this->error( 'no_instagram_data_loaded', __( 'Instagram data failed to load.', 'ghostkit' ) );
663 }
664 }
665
666 /**
667 * Get Instagram profile.
668 *
669 * @param WP_REST_Request $request request object.
670 *
671 * @return mixed
672 */
673 public function get_instagram_profile( WP_REST_Request $request ) {
674 $cache_name = 'ghostkit_instagram_profile_cache';
675 $cache_expiration = $request->get_param( 'cache_expiration' ) ? $request->get_param( 'cache_expiration' ) : DAY_IN_SECONDS;
676 $access_token = $request->get_param( 'access_token' );
677
678 if ( ! $access_token ) {
679 return $this->error( 'no_token_found', __( 'Provide Instagram Access Token.', 'ghostkit' ) );
680 }
681
682 $hash = md5(
683 wp_json_encode(
684 array(
685 $access_token,
686 $cache_expiration,
687 )
688 )
689 );
690
691 $cache_name = $cache_name . '_' . $hash;
692
693 // get cached data.
694 $result = get_transient( $cache_name );
695
696 // if there is no cache available, request instagram feed.
697 if ( false === $result && $access_token ) {
698 // Make Requests.
699 $profile = wp_remote_get( 'https://api.instagram.com/v1/users/self/?access_token=' . $access_token );
700
701 if ( ! is_wp_error( $profile ) && isset( $profile['body'] ) ) {
702 $profile = json_decode( $profile['body'], true );
703 } else {
704 $profile = false;
705 }
706
707 if ( $profile && is_array( $profile ) ) {
708 $result = $profile;
709 set_transient( $cache_name, $result, $cache_expiration );
710 }
711 }
712
713 if ( $result ) {
714 return $this->success( $result );
715 } else {
716 return $this->error( 'no_instagram_data_loaded', __( 'Instagram data failed to load.', 'ghostkit' ) );
717 }
718 }
719
720 /**
721 * Get Twitter profile.
722 *
723 * @param WP_REST_Request $request request object.
724 *
725 * @return mixed
726 */
727 public function get_twitter_profile( WP_REST_Request $request ) {
728 $cache_name = 'ghostkit_twitter_profile_cache';
729 $cache_expiration = $request->get_param( 'cache_expiration' ) ? $request->get_param( 'cache_expiration' ) : DAY_IN_SECONDS;
730
731 $consumer_key = $request->get_param( 'consumer_key' );
732 $consumer_secret = $request->get_param( 'consumer_secret' );
733 $access_token = $request->get_param( 'access_token' );
734 $access_token_secret = $request->get_param( 'access_token_secret' );
735 $screen_name = $request->get_param( 'screen_name' );
736
737 if ( ! $consumer_key ) {
738 return $this->error( 'no_consumer_key_found', __( 'Provide Twitter Consumer Key.', 'ghostkit' ) );
739 }
740 if ( ! $consumer_secret ) {
741 return $this->error( 'no_consumer_secret_found', __( 'Provide Twitter Consumer Secret.', 'ghostkit' ) );
742 }
743 if ( ! $access_token ) {
744 return $this->error( 'no_access_token_found', __( 'Provide Twitter Access Token.', 'ghostkit' ) );
745 }
746 if ( ! $access_token_secret ) {
747 return $this->error( 'no_access_token_secret_found', __( 'Provide Twitter Access Token Secret.', 'ghostkit' ) );
748 }
749 if ( ! $screen_name ) {
750 return $this->error( 'no_screen_name_found', __( 'Provide Username.', 'ghostkit' ) );
751 }
752
753 $api_data_ready = $consumer_key && $consumer_secret && $access_token && $access_token_secret;
754
755 $hash = md5(
756 wp_json_encode(
757 array(
758 $consumer_key,
759 $consumer_secret,
760 $access_token,
761 $access_token_secret,
762 $cache_expiration,
763 )
764 )
765 );
766
767 $cache_name = $cache_name . '_' . $screen_name . '_' . $hash;
768
769 // get cached data.
770 $result = get_transient( $cache_name );
771
772 // if there is no cache available, request twitter feed.
773 if ( false === $result && $api_data_ready ) {
774 // request_api_twitter.
775 $profile = $this->request_api_twitter(
776 array(
777 'url' => 'https://api.twitter.com/1.1/users/show.json',
778 'consumer_key' => $consumer_key,
779 'consumer_secret' => $consumer_secret,
780 'access_token' => $access_token,
781 'access_token_secret' => $access_token_secret,
782 'include_entities' => 'true',
783 'screen_name' => $screen_name,
784 )
785 );
786
787 if ( $profile && isset( $profile['screen_name'] ) ) {
788 $result = $profile;
789
790 // prepare different profile image sizes.
791 if ( $result['profile_image_url'] ) {
792 $result['profile_images'] = $this->get_twitter_profile_images( $result['profile_image_url'] );
793 }
794 if ( $result['profile_image_url_https'] ) {
795 $result['profile_images_https'] = $this->get_twitter_profile_images( $result['profile_image_url_https'] );
796 }
797
798 // prepare short counts.
799 $result['followers_count_short'] = $this->convert_number_short( $result['followers_count'] );
800 $result['friends_count_short'] = $this->convert_number_short( $result['friends_count'] );
801 $result['listed_count_short'] = $this->convert_number_short( $result['listed_count'] );
802 $result['favourites_count_short'] = $this->convert_number_short( $result['favourites_count'] );
803 $result['statuses_count_short'] = $this->convert_number_short( $result['statuses_count'] );
804
805 // prepare url link.
806 if ( $result['url'] && isset( $result['entities']['url'] ) ) {
807 $result['url_entitled'] = $this->add_tweet_entity_links( $result['url'], $result['entities']['url'] );
808 }
809
810 // description with links.
811 if ( isset( $result['entities']['description'] ) ) {
812 $result['description_entitled'] = $this->add_tweet_entity_links( $result['description'], $result['entities']['description'] );
813 }
814
815 set_transient( $cache_name, $result, $cache_expiration );
816 }
817 }
818
819 if ( $result ) {
820 return $this->success( $result );
821 } else {
822 return $this->error( 'no_twitter_data_loaded', __( 'Twitter data failed to load.', 'ghostkit' ) );
823 }
824 }
825
826 /**
827 * Get Twitter feed.
828 *
829 * @param WP_REST_Request $request request object.
830 *
831 * @return mixed
832 */
833 public function get_twitter_feed( WP_REST_Request $request ) {
834 $cache_name = 'ghostkit_twitter_feed_cache';
835 $cache_expiration = $request->get_param( 'cache_expiration' ) ? $request->get_param( 'cache_expiration' ) : DAY_IN_SECONDS;
836
837 $count = (int) ( $request->get_param( 'count' ) ? $request->get_param( 'count' ) : 6 );
838 $consumer_key = $request->get_param( 'consumer_key' );
839 $consumer_secret = $request->get_param( 'consumer_secret' );
840 $access_token = $request->get_param( 'access_token' );
841 $access_token_secret = $request->get_param( 'access_token_secret' );
842 $screen_name = $request->get_param( 'screen_name' );
843 $exclude_replies = 'true' === $request->get_param( 'exclude_replies' );
844 $include_rts = 'true' === $request->get_param( 'include_rts' );
845 $tweet_mode_extended = 'true' === $request->get_param( 'tweet_mode_extended' );
846
847 $api_data_ready = $consumer_key && $consumer_secret && $access_token && $access_token_secret;
848
849 if ( ! $consumer_key ) {
850 return $this->error( 'no_consumer_key_found', __( 'Provide Twitter Consumer Key.', 'ghostkit' ) );
851 }
852 if ( ! $consumer_secret ) {
853 return $this->error( 'no_consumer_secret_found', __( 'Provide Twitter Consumer Secret.', 'ghostkit' ) );
854 }
855 if ( ! $access_token ) {
856 return $this->error( 'no_access_token_found', __( 'Provide Twitter Access Token.', 'ghostkit' ) );
857 }
858 if ( ! $access_token_secret ) {
859 return $this->error( 'no_access_token_secret_found', __( 'Provide Twitter Access Token Secret.', 'ghostkit' ) );
860 }
861 if ( ! $screen_name ) {
862 return $this->error( 'no_screen_name_found', __( 'Provide Username.', 'ghostkit' ) );
863 }
864
865 $hash = md5(
866 wp_json_encode(
867 array(
868 $consumer_key,
869 $consumer_secret,
870 $access_token,
871 $access_token_secret,
872 $cache_expiration,
873 $tweet_mode_extended,
874 )
875 )
876 );
877
878 $cache_name = $cache_name . '_' . $screen_name . '_' . $hash;
879
880 // get cached data.
881 $feed = get_transient( $cache_name );
882
883 $result = array();
884
885 if ( $api_data_ready ) {
886 // if there is no cache available, request twitter feed.
887 if ( false === $feed ) {
888 $feed = $this->request_api_twitter(
889 array(
890 'url' => 'https://api.twitter.com/1.1/statuses/user_timeline.json',
891 'consumer_key' => $consumer_key,
892 'consumer_secret' => $consumer_secret,
893 'access_token' => $access_token,
894 'access_token_secret' => $access_token_secret,
895 'screen_name' => $screen_name,
896 'tweet_mode_extended' => $tweet_mode_extended,
897 'exclude_replies' => 'false',
898 'include_rts' => 'true',
899 'count' => 200,
900 )
901 );
902
903 if ( $feed && ! ( isset( $feed['errors'] ) && count( $feed['errors'] ) > 0 ) ) {
904 set_transient( $cache_name, $feed, $cache_expiration );
905 } else {
906 $feed = false;
907 }
908 }
909
910 $tweets_count = $feed ? count( $feed ) : 0;
911 $limit_to_display = min( $count, $tweets_count );
912
913 if ( $limit_to_display > 0 ) {
914 for ( $i = 0; $i < $tweets_count; $i++ ) {
915 $new_item = $feed[ $i ];
916
917 // check for replies.
918 if ( $exclude_replies && $new_item['in_reply_to_user_id'] ) {
919 continue;
920 }
921
922 // check for retweets.
923 if ( ! $include_rts && isset( $new_item['retweeted_status'] ) ) {
924 continue;
925 }
926
927 // full text.
928 if ( $tweet_mode_extended || ! isset( $new_item['text'] ) ) {
929 $text = isset( $new_item['full_text'] ) ? $new_item['full_text'] : null;
930
931 if ( null === $text ) {
932 $text = isset( $new_item['text'] ) ? $new_item['text'] : '';
933 }
934
935 $new_item['text'] = $text;
936 }
937
938 // prepare tweet content.
939 $new_item = $this->prepare_tweet_content( $new_item );
940
941 if ( isset( $new_item['retweeted_status'] ) ) {
942 // full text.
943 if ( $tweet_mode_extended || ! isset( $new_item['retweeted_status']['text'] ) ) {
944 $text = isset( $new_item['retweeted_status']['full_text'] ) ? $new_item['retweeted_status']['full_text'] : null;
945
946 if ( null === $text ) {
947 $text = isset( $new_item['retweeted_status']['text'] ) ? $new_item['retweeted_status']['text'] : '';
948 }
949
950 $new_item['retweeted_status']['text'] = $text;
951 }
952
953 $new_item['retweeted_status'] = $this->prepare_tweet_content( $new_item['retweeted_status'] );
954 }
955
956 $result[] = $new_item;
957
958 if ( count( $result ) >= $limit_to_display ) {
959 break;
960 }
961 }
962 }
963 }
964
965 if ( $result ) {
966 return $this->success( $result );
967 } else {
968 return $this->error( 'no_twitter_data_loaded', __( 'Twitter data failed to load.', 'ghostkit' ) );
969 }
970 }
971
972 /**
973 * Get Twitter API url result
974 *
975 * @param array $data - api request data.
976 *
977 * @return bool|mixed
978 */
979 public function request_api_twitter( $data ) {
980 $data = array_merge(
981 array(
982 'url' => 'https://api.twitter.com/1.1/statuses/user_timeline.json',
983 'consumer_key' => '',
984 'consumer_secret' => '',
985 'access_token' => '',
986 'access_token_secret' => '',
987 'screen_name' => '',
988 'exclude_replies' => '',
989 'include_rts' => '',
990 'count' => '',
991 'include_entities' => '',
992 'tweet_mode_extended' => '',
993 ),
994 $data
995 );
996
997 $oauth = array(
998 'oauth_consumer_key' => $data['consumer_key'],
999 'oauth_nonce' => time(),
1000 'oauth_signature_method' => 'HMAC-SHA1',
1001 'oauth_token' => $data['access_token'],
1002 'oauth_timestamp' => time(),
1003 'oauth_version' => '1.0',
1004 );
1005
1006 $base_info_url = $data['url'];
1007
1008 if ( $data['screen_name'] ) {
1009 $data['url'] .= strpos( $data['url'], '?' ) !== false ? '&' : '?';
1010 $data['url'] .= 'screen_name=' . $data['screen_name'];
1011 $oauth['screen_name'] = $data['screen_name'];
1012 }
1013 if ( $data['exclude_replies'] ) {
1014 $data['url'] .= strpos( $data['url'], '?' ) !== false ? '&' : '?';
1015 $data['url'] .= 'exclude_replies=' . $data['exclude_replies'];
1016 $oauth['exclude_replies'] = $data['exclude_replies'];
1017 }
1018 if ( $data['include_rts'] ) {
1019 $data['url'] .= strpos( $data['url'], '?' ) !== false ? '&' : '?';
1020 $data['url'] .= 'include_rts=' . $data['include_rts'];
1021 $oauth['include_rts'] = $data['include_rts'];
1022 }
1023 if ( $data['count'] ) {
1024 $data['url'] .= strpos( $data['url'], '?' ) !== false ? '&' : '?';
1025 $data['url'] .= 'count=' . $data['count'];
1026 $oauth['count'] = $data['count'];
1027 }
1028 if ( $data['include_entities'] ) {
1029 $data['url'] .= strpos( $data['url'], '?' ) !== false ? '&' : '?';
1030 $data['url'] .= 'include_entities=' . $data['include_entities'];
1031 $oauth['include_entities'] = $data['include_entities'];
1032 }
1033
1034 // Tweet Mode.
1035 if ( $data['tweet_mode_extended'] ) {
1036 $data['url'] .= strpos( $data['url'], '?' ) !== false ? '&' : '?';
1037 $data['url'] .= 'tweet_mode=extended';
1038 $oauth['tweet_mode'] = 'extended';
1039 }
1040
1041 $base_info = $this->build_base_string( $base_info_url, 'GET', $oauth );
1042 $composite_key = rawurlencode( $data['consumer_secret'] ) . '&' . rawurlencode( $data['access_token_secret'] );
1043
1044 // phpcs:ignore
1045 $oauth_signature = base64_encode( hash_hmac( 'sha1', $base_info, $composite_key, true ) );
1046 $oauth['oauth_signature'] = $oauth_signature;
1047
1048 // Make Requests.
1049 $header = array( $this->build_authorization_header( $oauth ), 'Expect:' );
1050 $options_buf = wp_remote_get(
1051 $data['url'],
1052 array(
1053 'headers' => implode( "\n", $header ),
1054 'sslverify' => false,
1055 )
1056 );
1057
1058 if ( ! is_wp_error( $options_buf ) && isset( $options_buf['body'] ) ) {
1059 return json_decode( $options_buf['body'], true );
1060 } else {
1061 return false;
1062 }
1063 }
1064
1065 /**
1066 * Build base string
1067 *
1068 * @param string $base_uri - url.
1069 * @param string $method - method.
1070 * @param array $params - params.
1071 *
1072 * @return string
1073 */
1074 private function build_base_string( $base_uri, $method, $params ) {
1075 $r = array();
1076 ksort( $params );
1077 foreach ( $params as $key => $value ) {
1078 $r[] = "$key=" . rawurlencode( $value );
1079 }
1080 return $method . '&' . rawurlencode( $base_uri ) . '&' . rawurlencode( implode( '&', $r ) );
1081 }
1082
1083 /**
1084 * Build authorization header
1085 *
1086 * @param array $oauth - auth data.
1087 *
1088 * @return string
1089 */
1090 private function build_authorization_header( $oauth ) {
1091 $r = 'Authorization: OAuth ';
1092 $values = array();
1093
1094 foreach ( $oauth as $key => $value ) {
1095 $values[] = "$key=\"" . rawurlencode( $value ) . '"';
1096 }
1097
1098 $r .= implode( ', ', $values );
1099
1100 return $r;
1101 }
1102
1103 /**
1104 * Prepare tweet content.
1105 *
1106 * @param array $tweet - tweet data.
1107 *
1108 * @return array - new tweet data.
1109 */
1110 public function prepare_tweet_content( $tweet ) {
1111 // prepare different profile image sizes.
1112 if ( $tweet['user']['profile_image_url'] ) {
1113 $tweet['user']['profile_images'] = $this->get_twitter_profile_images( $tweet['user']['profile_image_url'] );
1114 }
1115 if ( $tweet['user']['profile_image_url_https'] ) {
1116 $tweet['user']['profile_images_https'] = $this->get_twitter_profile_images( $tweet['user']['profile_image_url_https'] );
1117 }
1118
1119 // prepare short counts.
1120 $tweet['retweet_count_short'] = $this->convert_number_short( $tweet['retweet_count'] );
1121 $tweet['favorite_count_short'] = $this->convert_number_short( $tweet['favorite_count'] );
1122
1123 // user friendly date.
1124 $date = strtotime( $tweet['created_at'] );
1125 $now = time();
1126 $diff_date = $now - $date;
1127
1128 if ( $diff_date / 60 < 1 ) {
1129 // seconds.
1130 // translators: %d - seconds.
1131 $date = sprintf( esc_html__( '%ds', 'ghostkit' ), intval( $diff_date % 60 ) );
1132 } elseif ( $diff_date / 60 < 60 ) {
1133 // minutes.
1134 // translators: %d - minutes.
1135 $date = sprintf( esc_html__( '%dm', 'ghostkit' ), intval( $diff_date / 60 ) );
1136 } elseif ( $diff_date / 3600 < 24 ) {
1137 // hours.
1138 // translators: %d - hours.
1139 $date = sprintf( esc_html__( '%dh', 'ghostkit' ), intval( $diff_date / 3600 ) );
1140 } elseif ( gmdate( 'Y' ) === gmdate( 'Y', $date ) ) {
1141 // current year.
1142 $date = gmdate( esc_html__( 'M j', 'ghostkit' ), $date );
1143 } else {
1144 // past years.
1145 $date = gmdate( esc_html__( 'Y M j', 'ghostkit' ), $date );
1146 }
1147
1148 $tweet['date_formatted'] = $date;
1149
1150 // text with links and media.
1151 $tweet['text_entitled'] = $this->add_tweet_entity_links( $tweet['text'], $tweet['entities'] );
1152
1153 // text with links only.
1154 $tweet['text_entitled_no_media'] = $this->add_tweet_entity_links( $tweet['text'], $tweet['entities'], false );
1155
1156 return $tweet;
1157 }
1158
1159 /**
1160 * Adds a link around any entities in a twitter feed
1161 * twitter entities include urls, user mentions, hashtags and media
1162 * http://stackoverflow.com/a/15390225
1163 *
1164 * @param string $text - tweet text.
1165 * @param array $entities - available entities.
1166 * @param boolean $show_media - show tweet images.
1167 *
1168 * @return string tweet
1169 */
1170 public function add_tweet_entity_links( $text, $entities, $show_media = true ) {
1171 $replacements = array();
1172 if ( isset( $entities['hashtags'] ) ) {
1173 foreach ( $entities['hashtags'] as $hashtag ) {
1174 list ($start, $end) = $hashtag['indices'];
1175 $replacements[ $start ] = array(
1176 $start,
1177 $end,
1178 "<a href=\"https://twitter.com/hashtag/{$hashtag['text']}\" target=\"_blank\">#{$hashtag['text']}</a>",
1179 );
1180 }
1181 }
1182 if ( isset( $entities['urls'] ) ) {
1183 foreach ( $entities['urls'] as $url ) {
1184 list ($start, $end) = $url['indices'];
1185 // you can also use $url['expanded_url'] in place of $url['url'].
1186 $replacements[ $start ] = array(
1187 $start,
1188 $end,
1189 "<a href=\"{$url['url']}\" target=\"_blank\">{$url['display_url']}</a>",
1190 );
1191 }
1192 }
1193 if ( isset( $entities['user_mentions'] ) ) {
1194 foreach ( $entities['user_mentions'] as $mention ) {
1195 list ($start, $end) = $mention['indices'];
1196 $replacements[ $start ] = array(
1197 $start,
1198 $end,
1199 "<a href=\"https://twitter.com/{$mention['screen_name']}\" target=\"_blank\">@{$mention['screen_name']}</a>",
1200 );
1201 }
1202 }
1203 if ( isset( $entities['media'] ) ) {
1204 foreach ( $entities['media'] as $media ) {
1205 list ($start, $end) = $media['indices'];
1206
1207 if ( $show_media ) {
1208 $replacements[ $start ] = array(
1209 $start,
1210 $end,
1211 "<div><a href=\"{$media['url']}\" target=\"_blank\"><img src=\"{$media['media_url_https']}\" /></a></div>",
1212 );
1213 } else {
1214 $replacements[ $start ] = array(
1215 $start,
1216 $end,
1217 "<a href=\"{$media['url']}\" target=\"_blank\">{$media['url']}</a>",
1218 );
1219 }
1220 }
1221 }
1222
1223 // sort in reverse order by start location.
1224 krsort( $replacements );
1225
1226 foreach ( $replacements as $replace_data ) {
1227 list ($start, $end, $replace_text) = $replace_data;
1228
1229 $text = mb_substr( $text, 0, $start, 'UTF-8' ) . $replace_text . mb_substr( $text, $end, null, 'UTF-8' );
1230 }
1231
1232 return $text;
1233 }
1234
1235 /**
1236 * Convert long numbers to short. eg: 1500 -> 1.5K
1237 * Thanks https://code.recuweb.com/2018/php-format-numbers-to-nearest-thousands/ .
1238 *
1239 * @param number $num - number.
1240 * @return string
1241 */
1242 public function convert_number_short( $num ) {
1243 if ( $num > 1000 ) {
1244 $x = round( $num );
1245 $x_number_format = number_format( $x );
1246 $x_array = explode( ',', $x_number_format );
1247 $x_parts = array( 'K', 'M', 'B', 'T' );
1248 $x_count_parts = count( $x_array ) - 1;
1249 $x_display = $x;
1250 $x_display = $x_array[0] . ( (int) 0 !== $x_array[1][0] ? '.' . $x_array[1][0] : '' );
1251 $x_display .= $x_parts[ $x_count_parts - 1 ];
1252
1253 return $x_display;
1254 }
1255
1256 return $num;
1257 }
1258
1259 /**
1260 * Prepare profile image urls
1261 * https://developer.twitter.com/en/docs/accounts-and-users/user-profile-images-and-banners
1262 *
1263 * @param string $url - profile image.
1264 *
1265 * @return array images
1266 */
1267 public function get_twitter_profile_images( $url ) {
1268 if ( strpos( $url, '.jpg' ) !== false ) {
1269 return array(
1270 'normal' => $url,
1271 'bigger' => str_replace( '_normal.jpg', '_bigger.jpg', $url ),
1272 'mini' => str_replace( '_normal.jpg', '_mini.jpg', $url ),
1273 'original' => str_replace( '_normal.jpg', '.jpg', $url ),
1274 );
1275 }
1276
1277 return array(
1278 'normal' => $url,
1279 'bigger' => str_replace( '_normal.png', '_bigger.png', $url ),
1280 'mini' => str_replace( '_normal.png', '_mini.png', $url ),
1281 'original' => str_replace( '_normal.png', '.png', $url ),
1282 );
1283 }
1284
1285 /**
1286 * Sanitize TOC headings list.
1287 *
1288 * @param mixed $headings request headings.
1289 * @return array
1290 */
1291 public function sanitize_toc_headings( $headings ) {
1292 if ( ! is_array( $headings ) ) {
1293 return array();
1294 }
1295
1296 $sanitized = array();
1297
1298 foreach ( $headings as $heading ) {
1299 if ( ! is_array( $heading ) ) {
1300 continue;
1301 }
1302
1303 $level = isset( $heading['level'] ) ? absint( $heading['level'] ) : 0;
1304
1305 if ( $level < 1 || $level > 6 ) {
1306 continue;
1307 }
1308
1309 $sanitized[] = array(
1310 'level' => $level,
1311 'content' => isset( $heading['content'] ) ? wp_kses_post( $heading['content'] ) : '',
1312 'anchor' => isset( $heading['anchor'] ) ? sanitize_text_field( $heading['anchor'] ) : '',
1313 );
1314 }
1315
1316 return $sanitized;
1317 }
1318
1319 /**
1320 * Sanitize TOC allowed headers list.
1321 *
1322 * @param mixed $allowed_headers request allowed headers.
1323 * @return array
1324 */
1325 public function sanitize_toc_allowed_headers( $allowed_headers ) {
1326 if ( ! is_array( $allowed_headers ) ) {
1327 return array();
1328 }
1329
1330 $sanitized = array_values(
1331 array_unique(
1332 array_filter(
1333 array_map( 'absint', $allowed_headers ),
1334 static function ( $level ) {
1335 return $level >= 1 && $level <= 6;
1336 }
1337 )
1338 )
1339 );
1340
1341 return $sanitized;
1342 }
1343
1344 /**
1345 * Sanitize TOC list style.
1346 *
1347 * @param mixed $list_style request list style.
1348 * @return string
1349 */
1350 public function sanitize_toc_list_style( $list_style ) {
1351 $list_style = is_string( $list_style ) ? sanitize_text_field( $list_style ) : 'ol';
1352 $allowed = array( 'ol', 'ul', 'ol-styled', 'ul-styled' );
1353
1354 return in_array( $list_style, $allowed, true ) ? $list_style : 'ol';
1355 }
1356
1357 /**
1358 * Get TOC.
1359 *
1360 * @param WP_REST_Request $request request object.
1361 *
1362 * @return mixed
1363 */
1364 public function get_table_of_contents( WP_REST_Request $request ) {
1365 $headings = $request->get_param( 'headings' );
1366 $allowed_headers = $request->get_param( 'allowedHeaders' );
1367 $list_style = $request->get_param( 'listStyle' );
1368
1369 $html = '';
1370
1371 if ( empty( $allowed_headers ) || empty( $headings ) ) {
1372 return $this->success( $html );
1373 }
1374
1375 $current_depth = 6;
1376 $numbered_items = array();
1377 $numbered_items_min = null;
1378 $count = count( $headings );
1379
1380 // find the minimum heading to establish our baseline.
1381 for ( $i = 0; $i < $count; $i++ ) {
1382 if ( $current_depth > (int) $headings[ $i ]['level'] ) {
1383 $current_depth = (int) $headings[ $i ]['level'];
1384 }
1385 }
1386
1387 $numbered_items[ $current_depth ] = 0;
1388 $numbered_items_min = $current_depth;
1389 for ( $i = 0; $i < $count; $i++ ) {
1390 if ( $current_depth === (int) $headings[ $i ]['level'] ) {
1391 $html .= '<li>';
1392 }
1393
1394 // start lists.
1395 if ( $current_depth !== (int) $headings[ $i ]['level'] ) {
1396 for ( $current_depth; $current_depth < (int) $headings[ $i ]['level']; $current_depth++ ) {
1397 $numbered_items[ $current_depth + 1 ] = 0;
1398
1399 if ( ! in_array( $current_depth, $allowed_headers, true ) ) {
1400 continue;
1401 }
1402
1403 $html .= '<ul>';
1404 $html .= '<li>';
1405 }
1406 }
1407
1408 $html .= '<a href="' . esc_attr( '#' . $headings[ $i ]['anchor'] ) . '">' . wp_kses_post( $headings[ $i ]['content'] ) . '</a>';
1409
1410 // end lists.
1411 if ( $i !== $count - 1 ) {
1412 if ( $current_depth > (int) $headings[ $i + 1 ]['level'] ) {
1413 for ( $current_depth; $current_depth > (int) $headings[ $i + 1 ]['level']; $current_depth-- ) {
1414 $numbered_items[ $current_depth ] = 0;
1415
1416 if ( ! in_array( $current_depth, $allowed_headers, true ) ) {
1417 continue;
1418 }
1419
1420 $html .= '</li>';
1421 $html .= '</ul>';
1422 }
1423 }
1424 if ( isset( $headings[ $i + 1 ]['level'] ) && (int) $headings[ $i + 1 ]['level'] === $current_depth ) {
1425 $html .= '</li>';
1426 }
1427 } else {
1428 // this is the last item, make sure we close off all tags.
1429 for ( $current_depth; $current_depth >= $numbered_items_min; $current_depth-- ) {
1430 if ( ! in_array( $current_depth, $allowed_headers, true ) ) {
1431 continue;
1432 }
1433
1434 $html .= '</li>';
1435 if ( $current_depth !== $numbered_items_min ) {
1436 $html .= '</ul>';
1437 }
1438 }
1439 }
1440 }
1441
1442 // Wrapper.
1443 if ( $html ) {
1444 $list = 'ol';
1445 $list_class_name = '';
1446
1447 switch ( $list_style ) {
1448 case 'ul':
1449 $list = 'ul';
1450 break;
1451 case 'ul-styled':
1452 $list = 'ul';
1453 $list_class_name = 'is-style-styled';
1454 break;
1455 case 'ol-styled':
1456 $list_class_name = 'is-style-styled';
1457 break;
1458 }
1459
1460 $html = '<' . $list . ( $list_class_name ? ( ' class="' . $list_class_name . '"' ) : '' ) . '>' . $html . '</' . $list . '>';
1461 }
1462
1463 return $this->success( $html );
1464 }
1465
1466 /**
1467 * Get templates.
1468 *
1469 * @return mixed
1470 */
1471 public function get_templates() {
1472 $url = 'https://library.ghostkit.io/wp-json/ghostkit-library/v1/get_library/';
1473 $templates = get_transient( 'ghostkit_remote_templates', false );
1474
1475 /*
1476 * Get remote templates.
1477 */
1478 if ( ! $templates ) {
1479 $requested_templates = wp_remote_get(
1480 add_query_arg(
1481 array(
1482 'ghostkit_version' => GHOSTKIT_VERSION,
1483 'ghostkit_pro' => function_exists( 'ghostkit_pro' ),
1484 'ghostkit_pro_version' => function_exists( 'ghostkit_pro' ) ? ghostkit_pro()->plugin_version : null,
1485 ),
1486 $url
1487 )
1488 );
1489
1490 if ( ! is_wp_error( $requested_templates ) ) {
1491 $new_templates = wp_remote_retrieve_body( $requested_templates );
1492 $new_templates = json_decode( $new_templates, true );
1493
1494 if ( $new_templates && isset( $new_templates['response'] ) && is_array( $new_templates['response'] ) ) {
1495 $templates = $new_templates['response'];
1496 set_transient( 'ghostkit_remote_templates', $templates, DAY_IN_SECONDS );
1497 }
1498 }
1499 }
1500
1501 // Remove Pro templates from array, cause for now there is no way to check if pro addon is activated.
1502 if ( $templates ) {
1503 foreach ( $templates as $k => $template ) {
1504 $is_pro = false;
1505
1506 if ( isset( $template['types'] ) && is_array( $template['types'] ) ) {
1507 foreach ( $template['types'] as $type ) {
1508 $is_pro = $is_pro || 'pro' === $type['slug'];
1509 }
1510 }
1511
1512 if ( $is_pro ) {
1513 unset( $templates[ $k ] );
1514 }
1515 }
1516 }
1517
1518 /*
1519 * Get user templates from db.
1520 */
1521
1522 // Stupid hack.
1523 // https://core.trac.wordpress.org/ticket/18408.
1524 global $post;
1525 $backup_global_post = $post;
1526 $local_templates = array();
1527
1528 $local_templates_query = new WP_Query(
1529 array(
1530 'post_type' => 'ghostkit_template',
1531 'posts_per_page' => -1,
1532 'showposts' => -1,
1533 'paged' => -1,
1534 )
1535 );
1536
1537 while ( $local_templates_query->have_posts() ) {
1538 $local_templates_query->the_post();
1539 $db_template = get_post();
1540
1541 $categories = array();
1542 $category_terms = get_the_terms( $db_template->ID, 'ghostkit_template_category' );
1543
1544 if ( $category_terms ) {
1545 foreach ( $category_terms as $cat ) {
1546 $categories[] = array(
1547 'slug' => $cat->slug,
1548 'name' => $cat->name,
1549 );
1550 }
1551 }
1552
1553 $image_id = get_post_thumbnail_id( $db_template->ID );
1554 $image_data = wp_get_attachment_image_src( $image_id, 'large' );
1555
1556 $local_templates[] = array(
1557 'id' => $db_template->ID,
1558 'title' => $db_template->post_title,
1559 'types' => array(
1560 array(
1561 'slug' => 'local',
1562 ),
1563 ),
1564 'categories' => empty( $categories ) ? false : $categories,
1565 'url' => get_post_permalink( $db_template->ID ),
1566 'thumbnail' => isset( $image_data[0] ) ? $image_data[0] : false,
1567 'thumbnail_width' => isset( $image_data[1] ) ? $image_data[1] : false,
1568 'thumbnail_height' => isset( $image_data[2] ) ? $image_data[2] : false,
1569 );
1570 }
1571
1572 // Restore the global $post as it was before custom WP_Query.
1573 // phpcs:ignore
1574 $post = $backup_global_post;
1575
1576 /*
1577 * Get theme templates.
1578 */
1579 $theme_templates = array();
1580 $theme_templates_data = array();
1581
1582 foreach ( glob( get_template_directory() . '/ghostkit/templates/*/content.php' ) as $template ) {
1583 $template_path = dirname( $template );
1584 $template_url = get_template_directory_uri() . str_replace( get_template_directory(), '', $template_path );
1585 $slug = basename( $template_path );
1586
1587 $theme_templates_data[ $slug ] = array(
1588 'slug' => $slug,
1589 'path' => $template_path,
1590 'url' => $template_url,
1591 );
1592 }
1593
1594 // get child theme templates.
1595 if ( get_stylesheet_directory() !== get_template_directory() ) {
1596 foreach ( glob( get_stylesheet_directory() . '/ghostkit/templates/*/content.php' ) as $template ) {
1597 $template_path = dirname( $template );
1598 $template_url = get_stylesheet_directory_uri() . str_replace( get_stylesheet_directory(), '', $template_path );
1599 $slug = basename( $template_path );
1600
1601 $theme_templates_data[ $slug ] = array(
1602 'slug' => $slug,
1603 'path' => $template_path,
1604 'url' => $template_url,
1605 );
1606 }
1607 }
1608
1609 // natural sort.
1610 array_multisort( array_keys( $theme_templates_data ), SORT_NATURAL, $theme_templates_data );
1611
1612 foreach ( $theme_templates_data as $template_data ) {
1613 $file_data = get_file_data(
1614 $template_data['path'] . '/content.php',
1615 array(
1616 'name' => 'Name',
1617 'category' => 'Category',
1618 'source' => 'Source',
1619 )
1620 );
1621
1622 $thumbnail = false;
1623 $thumbnail_width = false;
1624 $thumbnail_height = false;
1625
1626 if ( file_exists( $template_data['path'] . '/thumbnail.png' ) ) {
1627 $thumbnail = $template_data['url'] . '/thumbnail.png';
1628
1629 list($thumbnail_width, $thumbnail_height) = getimagesize( $thumbnail );
1630 }
1631
1632 if ( file_exists( $template_data['path'] . '/thumbnail.jpg' ) ) {
1633 $thumbnail = $template_data['url'] . '/thumbnail.jpg';
1634 }
1635
1636 $theme_templates[] = array(
1637 'id' => basename( $template_data['path'] ),
1638 'title' => $file_data['name'],
1639 'types' => array(
1640 array(
1641 'slug' => 'theme',
1642 ),
1643 ),
1644 'categories' => isset( $file_data['category'] ) && $file_data['category'] ? array(
1645 array(
1646 'slug' => $file_data['category'],
1647 'name' => $file_data['category'],
1648 ),
1649 ) : false,
1650 'url' => false,
1651 'thumbnail' => $thumbnail,
1652 'thumbnail_width' => $thumbnail_width,
1653 'thumbnail_height' => $thumbnail_height,
1654 );
1655 }
1656
1657 // merge all available templates.
1658 $templates = array_merge( $templates, $local_templates, $theme_templates );
1659
1660 if ( is_array( $templates ) ) {
1661 return $this->success( $templates );
1662 } else {
1663 return $this->error( 'no_templates', __( 'Templates not found.', 'ghostkit' ) );
1664 }
1665 }
1666
1667 /**
1668 * Get templates.
1669 *
1670 * @param WP_REST_Request $request request object.
1671 *
1672 * @return mixed
1673 */
1674 public function get_template_data( WP_REST_Request $request ) {
1675 $url = 'https://library.ghostkit.io/wp-json/ghostkit-library/v1/get_library_item/';
1676 $id = $request->get_param( 'id' );
1677 $type = $request->get_param( 'type' );
1678 $template_data = false;
1679
1680 // Validation and security checks.
1681 if ( empty( $id ) || ! preg_match( '/^[a-zA-Z0-9_-]+$/', $id ) ) {
1682 return $this->error( 'invalid_id', __( 'Invalid template ID.', 'ghostkit' ) );
1683 }
1684
1685 switch ( $type ) {
1686 case 'remote':
1687 $template_data = get_transient( 'ghostkit_template_' . $type . '_' . $id, false );
1688
1689 if ( ! $template_data ) {
1690 $requested_template_data = wp_remote_get(
1691 add_query_arg(
1692 array(
1693 'id' => $id,
1694 'ghostkit_version' => GHOSTKIT_VERSION,
1695 'ghostkit_pro' => function_exists( 'ghostkit_pro' ),
1696 'ghostkit_pro_version' => function_exists( 'ghostkit_pro' ) ? ghostkit_pro()->plugin_version : null,
1697 ),
1698 $url
1699 )
1700 );
1701
1702 if ( ! is_wp_error( $requested_template_data ) ) {
1703 $new_template_data = wp_remote_retrieve_body( $requested_template_data );
1704 $new_template_data = json_decode( $new_template_data, true );
1705
1706 if ( $new_template_data && isset( $new_template_data['response'] ) && is_array( $new_template_data['response'] ) ) {
1707 $template_data = $new_template_data['response'];
1708 set_transient( 'ghostkit_template_' . $type . '_' . $id, $template_data, DAY_IN_SECONDS );
1709 }
1710 }
1711 }
1712 break;
1713 case 'local':
1714 $post = get_post( $id );
1715
1716 if ( $post && 'ghostkit_template' === $post->post_type ) {
1717 $template_data = array(
1718 'id' => $post->ID,
1719 'title' => $post->post_title,
1720 'content' => $post->post_content,
1721 );
1722 }
1723
1724 break;
1725 case 'theme':
1726 $template_content_file = get_stylesheet_directory() . '/ghostkit/templates/' . $id . '/content.php';
1727
1728 if ( ! file_exists( $template_content_file ) ) {
1729 $template_content_file = get_template_directory() . '/ghostkit/templates/' . $id . '/content.php';
1730 }
1731
1732 if ( file_exists( $template_content_file ) ) {
1733 ob_start();
1734 include $template_content_file;
1735 $template_content = ob_get_clean();
1736
1737 if ( $template_content ) {
1738 $template_data = get_file_data(
1739 $template_content_file,
1740 array(
1741 'name' => 'Name',
1742 )
1743 );
1744
1745 $template_data = array(
1746 'id' => $id,
1747 'title' => $template_data['name'],
1748 'content' => $template_content,
1749 );
1750 }
1751 }
1752 break;
1753 }
1754
1755 if ( is_array( $template_data ) ) {
1756 return $this->success( $template_data );
1757 } else {
1758 return $this->error( 'no_template_data', __( 'Template data not found.', 'ghostkit' ) );
1759 }
1760 }
1761
1762 /**
1763 * Get custom code.
1764 *
1765 * @return mixed
1766 */
1767 public function get_custom_code() {
1768 $custom_code = get_option( 'ghostkit_custom_code', array() );
1769
1770 if ( is_array( $custom_code ) ) {
1771 return $this->success( $custom_code );
1772 } else {
1773 return $this->error( 'no_custom_code', __( 'Custom code not found.', 'ghostkit' ) );
1774 }
1775 }
1776
1777 /**
1778 * Update custom code.
1779 *
1780 * @param WP_REST_Request $request request object.
1781 *
1782 * @return mixed
1783 */
1784 public function update_custom_code( WP_REST_Request $request ) {
1785 $new_code = $request->get_param( 'data' );
1786
1787 if ( is_array( $new_code ) ) {
1788 $current_code = get_option( 'ghostkit_custom_code', array() );
1789 update_option( 'ghostkit_custom_code', array_merge( $current_code, $new_code ) );
1790 }
1791
1792 return $this->success( true );
1793 }
1794
1795 /**
1796 * Update color palette.
1797 *
1798 * @param WP_REST_Request $request request object.
1799 *
1800 * @return mixed
1801 */
1802 public function update_color_palette( WP_REST_Request $request ) {
1803 $colors = $request->get_param( 'data' );
1804
1805 if ( is_array( $colors ) ) {
1806 update_option( 'ghostkit_color_palette', $colors );
1807 }
1808
1809 return $this->success( true );
1810 }
1811
1812 /**
1813 * Get custom typography.
1814 *
1815 * @return mixed
1816 */
1817 public function get_custom_typography() {
1818 $typography = get_option( 'ghostkit_typography', array() );
1819
1820 if ( is_array( $typography ) ) {
1821 return $this->success( $typography );
1822 } else {
1823 return $this->error( 'no_typography', __( 'Typography not found.', 'ghostkit' ) );
1824 }
1825 }
1826
1827 /**
1828 * Update typography.
1829 *
1830 * @param WP_REST_Request $request request object.
1831 *
1832 * @return mixed
1833 */
1834 public function update_custom_typography( WP_REST_Request $request ) {
1835 $new_typography = $request->get_param( 'data' );
1836 $updated_option = array();
1837
1838 if ( is_array( $new_typography ) ) {
1839 $current_typography = get_option( 'ghostkit_typography', array() );
1840
1841 if ( empty( $current_typography ) ) {
1842 $updated_option = $new_typography;
1843 } else {
1844 $current_typography['ghostkit_typography'] = json_decode( $current_typography['ghostkit_typography'], true );
1845 $new_typography['ghostkit_typography'] = json_decode( $new_typography['ghostkit_typography'], true );
1846
1847 $updated_option = array_merge( $current_typography, $new_typography );
1848
1849 $updated_option['ghostkit_typography'] = wp_json_encode( $updated_option['ghostkit_typography'] );
1850 }
1851
1852 update_option( 'ghostkit_typography', $updated_option );
1853 }
1854
1855 return $this->success( true );
1856 }
1857
1858 /**
1859 * Update Google Maps API key.
1860 *
1861 * @param WP_REST_Request $request request object.
1862 *
1863 * @return mixed
1864 */
1865 public function update_google_maps_api_key( WP_REST_Request $request ) {
1866 update_option( 'ghostkit_google_maps_api_key', $request->get_param( 'key' ) );
1867
1868 return $this->success( true );
1869 }
1870
1871 /**
1872 * Update Google reCaptcha API keys.
1873 *
1874 * @param WP_REST_Request $request request object.
1875 *
1876 * @return mixed
1877 */
1878 public function update_google_recaptcha_keys( WP_REST_Request $request ) {
1879 update_option( 'ghostkit_google_recaptcha_api_site_key', $request->get_param( 'site_key' ) );
1880 update_option( 'ghostkit_google_recaptcha_api_secret_key', $request->get_param( 'secret_key' ) );
1881
1882 return $this->success( true );
1883 }
1884
1885 /**
1886 * Update Disabled Blocks.
1887 *
1888 * @param WP_REST_Request $request request object.
1889 *
1890 * @return mixed
1891 */
1892 public function update_disabled_blocks( WP_REST_Request $request ) {
1893 $new_disabled_blocks = $request->get_param( 'blocks' );
1894
1895 if ( is_array( $new_disabled_blocks ) ) {
1896 $disabled_blocks = array_merge( get_option( 'ghostkit_disabled_blocks', array() ), $new_disabled_blocks );
1897 $result = array();
1898
1899 foreach ( $disabled_blocks as $k => $block_disabled ) {
1900 if ( $block_disabled ) {
1901 $result[ $k ] = true;
1902 }
1903 }
1904
1905 update_option( 'ghostkit_disabled_blocks', $result );
1906 }
1907
1908 return $this->success( true );
1909 }
1910
1911 /**
1912 * Update Settings.
1913 *
1914 * @param WP_REST_Request $request request object.
1915 *
1916 * @return mixed
1917 */
1918 public function update_settings( WP_REST_Request $request ) {
1919 $new_settings = $request->get_param( 'settings' );
1920
1921 if ( is_array( $new_settings ) ) {
1922 $current_settings = get_option( 'ghostkit_settings', array() );
1923 update_option( 'ghostkit_settings', array_merge( $current_settings, $new_settings ) );
1924 }
1925
1926 return $this->success( true );
1927 }
1928
1929 /**
1930 * Success rest.
1931 *
1932 * @param mixed $response response data.
1933 * @return mixed
1934 */
1935 public function success( $response ) {
1936 return new WP_REST_Response(
1937 array(
1938 'success' => true,
1939 'response' => $response,
1940 ),
1941 200
1942 );
1943 }
1944
1945 /**
1946 * Error rest.
1947 *
1948 * @param mixed $code error code.
1949 * @param mixed $response response data.
1950 * @param boolean $true_error use true error response to stop the code processing.
1951 * @return mixed
1952 */
1953 public function error( $code, $response, $true_error = false ) {
1954 if ( $true_error ) {
1955 return new WP_Error( $code, $response, array( 'status' => 401 ) );
1956 }
1957
1958 return new WP_REST_Response(
1959 array(
1960 'error' => true,
1961 'success' => false,
1962 'error_code' => $code,
1963 'response' => $response,
1964 ),
1965 401
1966 );
1967 }
1968 }
1969 new GhostKit_Rest();
1970