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