PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.0.10
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.0.10
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / codeinwp / themeisle-sdk / class-themeisle-sdk-endpoints.php

class-themeisle-sdk-endpoints.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.0.10, at vendor/codeinwp/themeisle-sdk/class-themeisle-sdk-endpoints.php

313 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The class that exposes endpoints.
4 *
5 * @package ThemeIsleSDK
6 * @subpackage Endpoints
7 * @copyright Copyright (c) 2017, Marius Cristea
8 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
9 * @since 1.0.0
10 */
11 // Exit if accessed directly.
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15 if ( ! class_exists( 'ThemeIsle_SDK_Endpoints' ) ) :
16 /**
17 * Expose endpoints for ThemeIsle SDK.
18 */
19 final class ThemeIsle_SDK_Endpoints {
20
21 const SDK_ENDPOINT = 'themeisle-sdk';
22 const SDK_ENDPOINT_VERSION = 1;
23
24 const HASH_FILE = 'themeisle-hash.json';
25
26 // if true, the endpoint will expect a product slug and will return the value only for that.
27 const PRODUCT_SPECIFIC = false;
28
29 /**
30 * @var ThemeIsle_SDK_Product $products Array of Themeisle Product.
31 */
32 static protected $products = array();
33
34 /**
35 * ThemeIsle_SDK_Endpoints constructor.
36 *
37 * @param ThemeIsle_SDK_Product $product_object Product Object.
38 */
39 public function __construct( $product_object ) {
40 if ( $product_object instanceof ThemeIsle_SDK_Product ) {
41 self::$products[] = $product_object;
42 }
43 $this->setup_endpoints();
44 }
45
46 /**
47 * Setup endpoints.
48 */
49 private function setup_endpoints() {
50 global $wp_version;
51 if ( version_compare( $wp_version, '4.4', '<' ) ) {
52 // no REST support.
53 return;
54 }
55
56 $this->setup_rest();
57 }
58
59 /**
60 * Setup REST endpoints.
61 */
62 private function setup_rest() {
63 add_action( 'rest_api_init', array( $this, 'rest_register' ) );
64 }
65
66 /**
67 * Registers the endpoints
68 */
69 function rest_register() {
70 register_rest_route(
71 self::SDK_ENDPOINT . '/v' . self::SDK_ENDPOINT_VERSION,
72 '/checksum/' . ( self::PRODUCT_SPECIFIC ? '(?P<slug>.*)/' : '' ),
73 array(
74 'methods' => 'GET',
75 'callback' => array( $this, 'checksum' ),
76 )
77 );
78 }
79
80 /**
81 * The checksum endpoint.
82 *
83 * @param WP_REST_Request $data the request.
84 *
85 * @return WP_REST_Response Response or the error
86 */
87 function checksum( WP_REST_Request $data ) {
88 $products = self::$products;
89 if ( self::PRODUCT_SPECIFIC ) {
90 $params = $this->validate_params( $data, array( 'slug' ) );
91 foreach ( self::$products as $product ) {
92 if ( $params['slug'] === $product->get_slug() ) {
93 $products = array( $product );
94 break;
95 }
96 }
97 }
98 $response = array();
99 $custom_css = $this->has_custom_css();
100 if ( is_bool( $custom_css ) ) {
101 $response['custom_css'] = $custom_css;
102 }
103
104 $response['child_theme'] = $this->get_theme_properties();
105
106 foreach ( $products as $product ) {
107 $files = array();
108 switch ( $product->get_type() ) {
109 case 'plugin':
110 $files = array();
111 break;
112 case 'theme':
113 $files = array( 'style.css', 'functions.php' );
114 break;
115 }
116
117 $error = '';
118
119 // if any element in the $files array contains a '/', this would imply recursion is required.
120 $diff = $this->generate_diff( $product, $files, array_reduce( $files, array( $this, 'is_recursion_required' ), false ) );
121 if ( is_wp_error( $diff ) ) {
122 $error = $diff->get_error_message();
123 $diff = array();
124 }
125
126 $response['products'][] = array(
127 'slug' => $product->get_slug(),
128 'version' => $product->get_version(),
129 'diffs' => $diff,
130 'error' => $error,
131 );
132 }
133
134 return new WP_REST_Response( array( 'checksum' => $response ) );
135 }
136
137 /**
138 * Get the current theme properties.
139 *
140 * @return array Properties of the current theme.
141 */
142 function get_theme_properties() {
143 if ( ! is_child_theme() ) {
144 return false;
145 }
146
147 $properties = array();
148 $theme = wp_get_theme();
149 // @codingStandardsIgnoreStart
150 $properties['name'] = $theme->Name;
151 // @codingStandardsIgnoreEnd
152
153 // get the files in the child theme.
154 require_once( ABSPATH . 'wp-admin/includes/file.php' );
155 WP_Filesystem();
156 global $wp_filesystem;
157 $path = str_replace( ABSPATH, $wp_filesystem->abspath(), get_stylesheet_directory() );
158 $list = $wp_filesystem->dirlist( $path, false, false );
159 if ( $list ) {
160 $list = array_keys( self::flatten_dirlist( $list ) );
161 $properties['files'] = $list;
162 }
163 return $properties;
164 }
165
166 /**
167 * Check if custom css has been added to the theme.
168 *
169 * @return bool Whether custom css has been added to the theme.
170 */
171 private function has_custom_css() {
172 $query = new WP_Query(
173 array(
174 'post_type' => 'custom_css',
175 'post_status' => 'publish',
176 'numberposts' => 1,
177 'update_post_meta_cache' => false,
178 'update_post_term_cache' => false,
179 )
180 );
181
182 if ( $query->have_posts() ) {
183 $query->the_post();
184 $content = get_the_content();
185 // if the content contains a colon, a CSS rule has been added.
186 return strpos( $content, ':' ) === false ? false : true;
187 }
188 return false;
189 }
190
191 /**
192 * Check if recursion needs to be enabled on the WP_Filesystem by reducing the array of files to a boolean.
193 *
194 * @param string $carry Value of the previous iteration.
195 * @param string $item Value of the current iteration.
196 *
197 * @return bool Whether to recurse or not.
198 */
199 function is_recursion_required( $carry, $item ) {
200 if ( ! $carry ) {
201 return ( strpos( $item, '/' ) !== false );
202 }
203 return $carry;
204 }
205
206 /**
207 * Generate the diff of the files.
208 *
209 * @param ThemeIsle_SDK_Product $product Themeisle Product.
210 * @param array $files Array of files.
211 * @param bool $recurse Whether to recurse or not.
212 *
213 * @return string
214 */
215 private function generate_diff( $product, $files, $recurse = false ) {
216 require_once( ABSPATH . 'wp-admin/includes/file.php' );
217 WP_Filesystem();
218 global $wp_filesystem;
219
220 $diff = array();
221 $path = str_replace( ABSPATH, $wp_filesystem->abspath(), plugin_dir_path( $product->get_basefile() ) );
222 $list = $wp_filesystem->dirlist( $path, false, $recurse );
223 // nothing found.
224 if ( ! $list ) {
225 return $diff;
226 }
227 $list = array_keys( self::flatten_dirlist( $list ) );
228
229 // now let's get the valid files that actually exist.
230 if ( empty( $files ) ) {
231 $files = $list;
232 } else {
233 $files = array_intersect( $files, $list );
234 }
235
236 // fetch the calculated hashes.
237 if ( ! $wp_filesystem->is_readable( $path . '/' . self::HASH_FILE ) ) {
238 return new WP_Error( 'themeisle_sdk_hash_not_found', sprintf( '%s not found', self::HASH_FILE ) );
239 }
240
241 $hashes = json_decode( $wp_filesystem->get_contents( $path . '/' . self::HASH_FILE ), true );
242 ksort( $hashes );
243
244 $diff = array();
245 foreach ( $files as $file ) {
246 // file does not exist in the hashes.
247 if ( ! array_key_exists( $file, $hashes ) ) {
248 continue;
249 }
250 $new = md5( $wp_filesystem->get_contents( $path . $file ) );
251 $old = $hashes[ $file ];
252
253 // same hash, bail.
254 if ( $new === $old ) {
255 continue;
256 }
257 $diff[] = $file;
258 }
259 return $diff;
260 }
261
262 /**
263 * Flatten the results of WP_Filesystem::dirlist() for iterating over.
264 *
265 * @access private
266 *
267 * @param array $nested_files Array of files as returned by WP_Filesystem::dirlist().
268 * @param string $path Relative path to prepend to child nodes. Optional.
269 * @return array $files A flattened array of the $nested_files specified.
270 */
271 private static function flatten_dirlist( $nested_files, $path = '' ) {
272 $files = array();
273 foreach ( $nested_files as $name => $details ) {
274 $files[ $path . $name ] = $details;
275 // Append children recursively
276 if ( ! empty( $details['files'] ) ) {
277 $children = self::flatten_dirlist( $details['files'], $path . $name . '/' );
278 // Merge keeping possible numeric keys, which array_merge() will reindex from 0..n
279 $files = $files + $children;
280 }
281 }
282 return $files;
283 }
284
285 /**
286 * Validates the parameters to the API
287 *
288 * @param WP_REST_Request $data the request.
289 * @param array $params the parameters to validate.
290 *
291 * @return array of parameter name=>value
292 */
293 private function validate_params( WP_REST_Request $data, $params ) {
294 $collect = array();
295 foreach ( $params as $param ) {
296 $value = sanitize_text_field( $data[ $param ] );
297 if ( empty( $value ) ) {
298 return new WP_Error(
299 'themeisle_' . $param . '_invalid', sprintf( 'Invalid %', $param ), array(
300 'status' => 403,
301 )
302 );
303 } else {
304 $collect[ $param ] = $value;
305 }
306 }
307
308 return $collect;
309 }
310
311 }
312 endif;
313