PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.8.2
Secure Custom Fields v6.8.2
6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / includes / local-json.php
secure-custom-fields / includes Last commit date
Blocks 1 year ago Meta 1 year ago abilities 6 months ago admin 7 months ago ajax 3 months ago api 6 months ago fields 3 months ago forms 6 months ago legacy 1 year ago locations 1 year ago post-types 6 months ago rest-api 3 months ago walkers 1 year ago acf-bidirectional-functions.php 1 year ago acf-field-functions.php 7 months ago acf-field-group-functions.php 7 months ago acf-form-functions.php 1 year ago acf-helper-functions.php 1 year ago acf-hook-functions.php 1 year ago acf-input-functions.php 7 months ago acf-internal-post-type-functions.php 7 months ago acf-meta-functions.php 1 year ago acf-post-functions.php 1 year ago acf-post-type-functions.php 1 year ago acf-taxonomy-functions.php 1 year ago acf-user-functions.php 1 year ago acf-utility-functions.php 1 year ago acf-value-functions.php 1 year ago acf-wp-functions.php 1 year ago assets.php 10 months ago blocks.php 3 months ago class-acf-data.php 10 months ago class-acf-internal-post-type.php 6 months ago class-acf-options-page.php 1 year ago class-acf-site-health.php 3 months ago class-scf-json-schema-validator.php 6 months ago class-scf-schema-builder.php 6 months ago compatibility.php 1 year ago deprecated.php 1 year ago fields.php 10 months ago index.php 1 year ago l10n.php 1 year ago local-fields.php 1 year ago local-json.php 1 year ago local-meta.php 1 year ago locations.php 1 year ago loop.php 10 months ago media.php 1 year ago rest-api.php 10 months ago revisions.php 10 months ago scf-ui-options-page-functions.php 1 year ago third-party.php 7 months ago upgrades.php 1 year ago validation.php 10 months ago wpml.php 1 year ago
local-json.php
569 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit; // Exit if accessed directly
5 }
6
7 if ( ! class_exists( 'ACF_Local_JSON' ) ) :
8
9 class ACF_Local_JSON {
10
11 /**
12 * The found JSON field group files.
13 *
14 * @since ACF 5.9.0
15 * @var array
16 */
17 private $files = array();
18
19 /**
20 * Constructor.
21 *
22 * @date 14/4/20
23 * @since ACF 5.9.0
24 *
25 * @return void
26 */
27 public function __construct() {
28
29 // Update settings.
30 acf_update_setting( 'save_json', get_stylesheet_directory() . '/acf-json' );
31 acf_append_setting( 'load_json', get_stylesheet_directory() . '/acf-json' );
32
33 // Add listeners.
34 add_action( 'acf/update_field_group', array( $this, 'update_field_group' ) );
35 add_action( 'acf/untrash_field_group', array( $this, 'update_field_group' ) );
36 add_filter( 'acf/trash_field_group', array( $this, 'delete_field_group' ) );
37 add_filter( 'acf/delete_field_group', array( $this, 'delete_field_group' ) );
38 add_filter( 'acf/update_post_type', array( $this, 'update_internal_post_type' ) );
39 add_filter( 'acf/untrash_post_type', array( $this, 'update_internal_post_type' ) );
40 add_filter( 'acf/trash_post_type', array( $this, 'delete_internal_post_type' ) );
41 add_filter( 'acf/delete_post_type', array( $this, 'delete_internal_post_type' ) );
42 add_filter( 'acf/update_taxonomy', array( $this, 'update_internal_post_type' ) );
43 add_filter( 'acf/untrash_taxonomy', array( $this, 'update_internal_post_type' ) );
44 add_filter( 'acf/trash_taxonomy', array( $this, 'delete_internal_post_type' ) );
45 add_filter( 'acf/delete_taxonomy', array( $this, 'delete_internal_post_type' ) );
46
47 // Include fields.
48 add_action( 'acf/include_fields', array( $this, 'include_fields' ) );
49 add_action( 'acf/include_post_types', array( $this, 'include_post_types' ) );
50 add_action( 'acf/include_taxonomies', array( $this, 'include_taxonomies' ) );
51 }
52
53 /**
54 * Returns true if this component is enabled.
55 *
56 * @date 14/4/20
57 * @since ACF 5.9.0
58 *
59 * @return boolean
60 */
61 public function is_enabled() {
62 return (bool) acf_get_setting( 'json' );
63 }
64
65 /**
66 * Gets the path(s) to load JSON from.
67 *
68 * @since ACF 6.2
69 *
70 * @return array
71 */
72 public function get_load_paths() {
73 $paths = (array) acf_get_setting( 'load_json' );
74
75 /**
76 * Filters the path(s) used to load JSON from.
77 *
78 * @since ACF 6.2
79 *
80 * @param array $paths An array of potential paths to load JSON from.
81 * @return array
82 */
83 return (array) apply_filters( 'acf/json/load_paths', $paths );
84 }
85
86 /**
87 * Gets the path(s) to save JSON to.
88 *
89 * @since ACF 6.2
90 *
91 * @param string $key The key to get paths for (optional).
92 * @param array $post The main ACF post array (optional).
93 * @return array
94 */
95 public function get_save_paths( $key = '', $post = array() ) {
96 $name = ! empty( $post['title'] ) ? (string) $post['title'] : '';
97 $post_type = acf_determine_internal_post_type( $key );
98 $paths = array();
99
100 // Paths are sorted by priority, with key overriding name, etc.
101 $paths[] = acf_get_setting( "save_json/key={$key}" );
102 $paths[] = acf_get_setting( "save_json/name={$name}" );
103 $paths[] = acf_get_setting( "save_json/type={$post_type}" );
104 $paths[] = acf_get_setting( 'save_json' );
105 $paths = array_values( array_filter( $paths ) );
106
107 /**
108 * Filters the paths used to save JSON.
109 *
110 * @since ACF 6.2
111 *
112 * @param array $paths An array of the potential paths to save JSON to.
113 * @param array $post The ACF field group, post type, or taxonomy array.
114 * @return array
115 */
116 return (array) apply_filters( 'acf/json/save_paths', $paths, $post );
117 }
118
119 /**
120 * Writes field group data to JSON file.
121 *
122 * @date 14/4/20
123 * @since ACF 5.9.0
124 *
125 * @param array $field_group The field group.
126 * @return void
127 */
128 public function update_field_group( $field_group ) {
129
130 // Bail early if disabled.
131 if ( ! $this->is_enabled() ) {
132 return false;
133 }
134
135 // Append fields.
136 $field_group['fields'] = acf_get_fields( $field_group );
137
138 // Save to file.
139 $this->save_file( $field_group['key'], $field_group );
140 }
141
142 /**
143 * Writes ACF posts to the JSON file.
144 *
145 * @since ACF 6.1
146 *
147 * @param array $post The main ACF post array.
148 * @return boolean
149 */
150 public function update_internal_post_type( $post ) {
151 if ( ! $this->is_enabled() ) {
152 return false;
153 }
154
155 /**
156 * Filters the ACF post before saving it to the file.
157 *
158 * @since ACF 6.1
159 *
160 * @param array $post The main ACF post array
161 */
162 $post = apply_filters( 'acf/pre_save_json_file', $post );
163
164 return $this->save_file( $post['key'], $post );
165 }
166
167 /**
168 * Deletes a field group JSON file.
169 *
170 * @date 14/4/20
171 * @since ACF 5.9.0
172 *
173 * @param array $field_group The field group.
174 * @return boolean
175 */
176 public function delete_field_group( $field_group ) {
177 return $this->delete_internal_post_type( $field_group );
178 }
179
180 /**
181 * Deletes an ACF JSON file.
182 *
183 * @since ACF 6.1
184 *
185 * @param array $post The main ACF post array.
186 * @return boolean
187 */
188 public function delete_internal_post_type( $post ) {
189 if ( ! $this->is_enabled() ) {
190 return false;
191 }
192
193 // WP appends '__trashed' to the end of 'key' (post_name).
194 $key = str_replace( '__trashed', '', $post['key'] );
195
196 return $this->delete_file( $key, $post );
197 }
198
199 /**
200 * Includes all local JSON fields.
201 *
202 * @date 14/4/20
203 * @since ACF 5.9.0
204 *
205 * @return void
206 */
207 public function include_fields() {
208
209 // Bail early if disabled.
210 if ( ! $this->is_enabled() ) {
211 return false;
212 }
213
214 // Get load paths.
215 $files = $this->scan_files( 'acf-field-group' );
216 foreach ( $files as $key => $file ) {
217 $json = json_decode( file_get_contents( $file ), true );
218 $json['local'] = 'json';
219 $json['local_file'] = $file;
220 acf_add_local_field_group( $json );
221 }
222 }
223
224 /**
225 * Includes all local JSON post types.
226 *
227 * @since ACF 6.1
228 */
229 public function include_post_types() {
230 // Bail early if disabled.
231 if ( ! $this->is_enabled() ) {
232 return false;
233 }
234
235 // Get load paths.
236 $files = $this->scan_files( 'acf-post-type' );
237 foreach ( $files as $key => $file ) {
238 $json = json_decode( file_get_contents( $file ), true );
239 $json['local'] = 'json';
240 $json['local_file'] = $file;
241 acf_add_local_internal_post_type( $json, 'acf-post-type' );
242 }
243 }
244
245 /**
246 * Includes all local JSON taxonomies.
247 *
248 * @since ACF 6.1
249 */
250 public function include_taxonomies() {
251 // Bail early if disabled.
252 if ( ! $this->is_enabled() ) {
253 return false;
254 }
255
256 // Get load paths.
257 $files = $this->scan_files( 'acf-taxonomy' );
258 foreach ( $files as $key => $file ) {
259 $json = json_decode( file_get_contents( $file ), true );
260 $json['local'] = 'json';
261 $json['local_file'] = $file;
262 acf_add_local_internal_post_type( $json, 'acf-taxonomy' );
263 }
264 }
265
266 /**
267 * Scans for JSON field groups.
268 *
269 * @date 14/4/20
270 * @since ACF 5.9.0
271 *
272 * @return array
273 */
274 function scan_field_groups() {
275 return $this->scan_files( 'acf-field-group' );
276 }
277
278 /**
279 * Scans for JSON files.
280 *
281 * @since ACF 6.1
282 *
283 * @param string $post_type The ACF post type to scan for.
284 * @return array
285 */
286 function scan_files( $post_type = 'acf-field-group' ) {
287 $json_files = array();
288
289 // Loop over "local_json" paths and parse JSON files.
290 foreach ( $this->get_load_paths() as $path ) {
291 if ( is_dir( $path ) ) {
292 $files = scandir( $path );
293 if ( $files ) {
294 foreach ( $files as $filename ) {
295
296 // Ignore hidden files.
297 if ( $filename[0] === '.' ) {
298 continue;
299 }
300
301 // Ignore sub directories.
302 $file = untrailingslashit( $path ) . '/' . $filename;
303 if ( is_dir( $file ) ) {
304 continue;
305 }
306
307 // Ignore non JSON files.
308 $ext = pathinfo( $filename, PATHINFO_EXTENSION );
309 if ( $ext !== 'json' ) {
310 continue;
311 }
312
313 // Read JSON data.
314 $json = json_decode( file_get_contents( $file ), true );
315 if ( ! is_array( $json ) || ! isset( $json['key'] ) ) {
316 continue;
317 }
318
319 // Append data.
320 $json_files[ $json['key'] ] = $file;
321 }
322 }
323 }
324 }
325
326 // Store data and return.
327 $this->files = $json_files;
328 return $this->get_files( $post_type );
329 }
330
331 /**
332 * Returns an array of found JSON files.
333 *
334 * @date 14/4/20
335 * @since ACF 5.9.0
336 *
337 * @param string $post_type The ACF post type to get files for.
338 * @return array
339 */
340 public function get_files( $post_type = 'acf-field-group' ) {
341 $files = array();
342
343 foreach ( $this->files as $key => $path ) {
344 $internal_post_type = acf_determine_internal_post_type( $key );
345
346 if ( $internal_post_type === $post_type ) {
347 $files[ $key ] = $path;
348 } elseif ( 'acf-field-group' === $post_type ) {
349 // If we can't figure out the ACF post type, make an educated guess that it's a field group.
350 $json = json_decode( file_get_contents( $path ), true );
351 if ( ! is_array( $json ) ) {
352 continue;
353 }
354
355 if ( isset( $json['fields'] ) ) {
356 $files[ $key ] = $path;
357 }
358 }
359 }
360
361 return $files;
362 }
363
364 /**
365 * Gets the filename for an ACF JSON file.
366 *
367 * @since ACF 6.3
368 *
369 * @param string $key The ACF post key.
370 * @param array $post The main ACF post array.
371 * @return string|boolean
372 */
373 public function get_filename( $key, $post ) {
374 $load_path = '';
375
376 if ( is_array( $this->files ) && isset( $this->files[ $key ] ) ) {
377 $load_path = $this->files[ $key ];
378 }
379
380 /**
381 * Filters the filename used when saving JSON.
382 *
383 * @since ACF 6.2
384 *
385 * @param string $filename The default filename.
386 * @param array $post The main post array for the item being saved.
387 * @param string $load_path The path that the item was loaded from.
388 */
389 $filename = apply_filters( 'acf/json/save_file_name', $key . '.json', $post, $load_path );
390
391 if ( ! is_string( $filename ) ) {
392 return false;
393 }
394
395 $filename = sanitize_file_name( $filename );
396
397 // sanitize_file_name() can potentially remove all characters.
398 if ( empty( $filename ) ) {
399 return false;
400 }
401
402 return $filename;
403 }
404
405 /**
406 * Saves an ACF JSON file.
407 *
408 * @date 17/4/20
409 * @since ACF 5.9.0
410 *
411 * @param string $key The ACF post key.
412 * @param array $post The main ACF post array.
413 * @return boolean
414 */
415 public function save_file( $key, $post ) {
416 $paths = $this->get_save_paths( $key, $post );
417 $filename = $this->get_filename( $key, $post );
418 $file = false;
419 $first_writable = false;
420
421 if ( ! $filename ) {
422 return false;
423 }
424
425 foreach ( $paths as $path ) {
426 if ( ! is_writable( $path ) ) { //phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable -- non-compatible function for this purpose.
427 continue;
428 }
429
430 if ( false === $first_writable ) {
431 $first_writable = $path;
432 }
433
434 $file_to_check = trailingslashit( $path ) . $filename;
435
436 if ( is_file( $file_to_check ) ) {
437 $file = $file_to_check;
438 }
439 }
440
441 if ( ! $file ) {
442 if ( $first_writable ) {
443 $file = trailingslashit( $first_writable ) . $filename;
444 } else {
445 return false;
446 }
447 }
448
449 // Make sure this is a valid ACF post type.
450 $post_type = acf_determine_internal_post_type( $key );
451 if ( ! $post_type ) {
452 return false;
453 }
454
455 // Append modified time.
456 if ( $post['ID'] ) {
457 $post['modified'] = get_post_modified_time( 'U', true, $post['ID'] );
458 } else {
459 $post['modified'] = strtotime( 'now' );
460 }
461
462 // Prepare for export and save the file.
463 $post = acf_prepare_internal_post_type_for_export( $post, $post_type );
464 $result = file_put_contents( $file, acf_json_encode( $post ) . apply_filters( 'acf/json/eof_newline', PHP_EOL ) ); //phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- potentially could run outside of admin.
465
466 // Return true if bytes were written.
467 return is_int( $result );
468 }
469
470 /**
471 * Deletes an ACF JSON file.
472 *
473 * @date 17/4/20
474 * @since ACF 5.9.0
475 *
476 * @param string $key The ACF post key.
477 * @param array $post The main ACF post array.
478 * @return boolean
479 */
480 public function delete_file( $key, $post = array() ) {
481 $paths = $this->get_save_paths( $key, $post );
482 $filename = $this->get_filename( $key, $post );
483
484 if ( ! $filename ) {
485 return false;
486 }
487
488 foreach ( $paths as $path_to_check ) {
489 $file = untrailingslashit( $path_to_check ) . '/' . $filename;
490
491 if ( is_writable( $file ) ) { //phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable -- non-compatible function for this purpose.
492 wp_delete_file( $file );
493 }
494 }
495
496 return true;
497 }
498
499 /**
500 * Includes all local JSON files.
501 *
502 * @date 10/03/2014
503 * @since ACF 5.0.0
504 * @deprecated 5.9.0
505 */
506 public function include_json_folders() {
507 _deprecated_function( __METHOD__, '5.9.0', 'ACF_Local_JSON::include_fields()' );
508 $this->include_fields();
509 }
510
511 /**
512 * Includes local JSON files within a specific folder.
513 *
514 * @date 01/05/2017
515 * @since ACF 5.5.13
516 * @deprecated 5.9.0
517 *
518 * @param string $path The path to a specific JSON folder.
519 * @return void
520 */
521 public function include_json_folder( $path = '' ) {
522 _deprecated_function( __METHOD__, '5.9.0' );
523 // Do nothing.
524 }
525 }
526
527 // Initialize.
528 acf_new_instance( 'ACF_Local_JSON' );
529 endif; // class_exists check
530
531 /**
532 * Returns an array of found JSON field group files.
533 *
534 * @date 14/4/20
535 * @since ACF 5.9.0
536 *
537 * @param string $post_type The ACF post type to get files for.
538 * @return array
539 */
540 function acf_get_local_json_files( $post_type = 'acf-field-group' ) {
541 return acf_get_instance( 'ACF_Local_JSON' )->get_files( $post_type );
542 }
543
544 /**
545 * Saves a field group JSON file.
546 *
547 * @date 5/12/2014
548 * @since ACF 5.1.5
549 *
550 * @param array $field_group The field group.
551 * @return boolean
552 */
553 function acf_write_json_field_group( $field_group ) {
554 return acf_get_instance( 'ACF_Local_JSON' )->save_file( $field_group['key'], $field_group );
555 }
556
557 /**
558 * Deletes a field group JSON file.
559 *
560 * @date 5/12/2014
561 * @since ACF 5.1.5
562 *
563 * @param string $key The field group key.
564 * @return boolean True on success.
565 */
566 function acf_delete_json_field_group( $key ) {
567 return acf_get_instance( 'ACF_Local_JSON' )->delete_file( $key );
568 }
569