Completed
Push — master ( c69bde...ef2933 )
by
unknown
03:03
created

Helper::print_json_data_script()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 5
c 1
b 1
f 0
nc 1
nop 0
dl 0
loc 9
rs 9.6666
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
namespace Carbon_Fields\Helper;
4
5
use Carbon_Fields\Datastore\Datastore;
6
use Carbon_Fields\Container\Container;
7
use Carbon_Fields\Templater\Templater;
8
use Carbon_Fields\Manager\Sidebar_Manager;
9
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
10
11
/**
12
 * Helper functions and main initialization class.
13
 */
14
class Helper {
15
16
	/**
17
	 * Create a new helper.
18
	 * Hook the main Carbon Fields initialization functionality.
19
	 */
20
	public function __construct() {
21
		add_action( 'wp_loaded', array( $this, 'trigger_fields_register' ) );
22
		add_action( 'carbon_after_register_fields', array( $this, 'init_containers' ) );
23
		add_action( 'admin_footer', array( $this, 'init_scripts' ), 0 );
24
		add_action( 'admin_print_footer_scripts', array( $this, 'print_json_data_script' ), 9 );
25
		add_action( 'crb_field_activated', array( $this, 'add_templates' ) );
26
		add_action( 'crb_container_activated', array( $this, 'add_templates' ) );
27
		add_action( 'after_setup_theme', array( $this, 'load_textdomain' ), 9999 );
28
29
		# Initialize templater
30
		new Templater();
31
32
		# Initialize sidebar manager
33
		Sidebar_Manager::instance();
34
	}
35
36
	/**
37
	 * Load the plugin textdomain.
38
	 */
39
	public function load_textdomain() {
40
		$dir = dirname( dirname( __DIR__ ) ) . '/languages/';
41
		$domain = 'carbon-fields';
42
		$locale = get_locale();
43
		$path = $dir . $domain . '-' . $locale . '.mo';
44
		load_textdomain( $domain, $path );
45
	}
46
47
	/**
48
	 * Register containers and fields.
49
	 */
50
	public function trigger_fields_register() {
51
		try {
52
			do_action( 'carbon_register_fields' );
53
			do_action( 'carbon_after_register_fields' );
54
		} catch ( Incorrect_Syntax_Exception $e ) {
55
			$callback = '';
56
			foreach ( $e->getTrace() as $trace ) {
57
				$callback .= '<br/>' . ( isset( $trace['file'] ) ? $trace['file'] . ':' . $trace['line'] : $trace['function'] . '()' );
58
			}
59
			wp_die( '<h3>' . $e->getMessage() . '</h3><small>' . $callback . '</small>' );
60
		}
61
	}
62
63
	/**
64
	 * Initialize containers.
65
	 */
66
	public function init_containers() {
67
		Container::init_containers();
68
	}
69
70
	/**
71
	 * Initialize main scripts
72
	 */
73
	public function init_scripts() {
74
		wp_enqueue_script( 'carbon-app', \Carbon_Fields\URL . '/assets/js/app.js', array( 'jquery', 'backbone', 'underscore', 'jquery-touch-punch', 'jquery-ui-sortable' ) );
75
		wp_enqueue_script( 'carbon-ext', \Carbon_Fields\URL . '/assets/js/ext.js', array( 'carbon-app' ) );
76
77
		$active_fields = Container::get_active_fields();
78
		$active_field_types = array();
79
80
		foreach ( $active_fields as $field ) {
81
			if ( in_array( $field->type, $active_field_types ) ) {
82
				continue;
83
			}
84
85
			$active_field_types[] = $field->type;
86
87
			$field->admin_enqueue_scripts();
88
		}
89
	}
90
91
	/**
92
	 * Print the carbon JSON data script.
93
	 */
94
	public function print_json_data_script() {
95
		?>
96
<script type="text/javascript">
97
<!--//--><![CDATA[//><!--
98
var carbon_json = <?php echo wp_json_encode( $this->get_json_data() ); ?>;
99
//--><!]]>
100
</script>
101
		<?php
102
	}
103
104
	/**
105
	 * Retrieve containers and sidebars for use in the JS.
106
	 *
107
	 * @return array $carbon_data
108
	 */
109
	public function get_json_data() {
110
		global $wp_registered_sidebars;
111
112
		$carbon_data = array(
113
			'containers' => array(),
114
			'sidebars' => array(),
115
		);
116
117
		$containers = Container::get_active_containers();
118
119
		foreach ( $containers as $container ) {
120
			$container_data = $container->to_json( true );
121
122
			$carbon_data['containers'][] = $container_data;
123
		}
124
125
		foreach ( $wp_registered_sidebars as $sidebar ) {
126
			// Check if we have inactive sidebars
127
			if ( isset( $sidebar['class'] ) && strpos( $sidebar['class'], 'inactive-sidebar' ) !== false ) {
128
				continue;
129
			}
130
131
			$carbon_data['sidebars'][] = array(
132
				'name' => $sidebar['name'],
133
				'id'   => $sidebar['id'],
134
			);
135
		}
136
137
		return $carbon_data;
138
	}
139
140
	/**
141
	 * Retrieve post meta field for a post.
142
	 *
143
	 * @param  int    $id   Post ID.
144
	 * @param  string $name Custom field name.
145
	 * @param  string $type Custom field type (optional).
146
	 * @return mixed        Meta value.
147
	 */
148
	public static function get_post_meta( $id, $name, $type = null ) {
149
		$name = $name[0] == '_' ? $name : '_' . $name;
150
151
		return self::get_field_value( 'post_meta', $name, $type, $id );
152
	}
153
154
	/**
155
	 * Shorthand for get_post_meta().
156
	 * Uses the ID of the current post in the loop.
157
	 *
158
	 * @param  string $name Custom field name.
159
	 * @param  string $type Custom field type (optional).
160
	 * @return mixed        Meta value.
161
	 */
162
	public static function get_the_post_meta( $name, $type = null ) {
163
		return self::get_post_meta( get_the_ID(), $name, $type );
164
	}
165
166
	/**
167
	 * Retrieve theme option field value.
168
	 *
169
	 * @param  string $name Custom field name.
170
	 * @param  string $type Custom field type (optional).
171
	 * @return mixed        Option value.
172
	 */
173
	public static function get_theme_option( $name, $type = null ) {
174
		return self::get_field_value( 'theme_options', $name, $type );
175
	}
176
177
	/**
178
	 * Retrieve term meta field for a term.
179
	 *
180
	 * @param  int    $id   Term ID.
181
	 * @param  string $name Custom field name.
182
	 * @param  string $type Custom field type (optional).
183
	 * @return mixed        Meta value.
184
	 */
185
	public static function get_term_meta( $id, $name, $type = null ) {
186
		$name = $name[0] == '_' ? $name: '_' . $name;
187
188
		return self::get_field_value( 'term_meta', $name, $type, $id );
189
	}
190
191
	/**
192
	 * Retrieve user meta field for a user.
193
	 *
194
	 * @param  int    $id   User ID.
195
	 * @param  string $name Custom field name.
196
	 * @param  string $type Custom field type (optional).
197
	 * @return mixed        Meta value.
198
	 */
199
	public static function get_user_meta( $id, $name, $type = null ) {
200
		$name = $name[0] == '_' ? $name: '_' . $name;
201
202
		return self::get_field_value( 'user_meta', $name, $type, $id );
203
	}
204
205
	/**
206
	 * Retrieve comment meta field for a comment.
207
	 *
208
	 * @param  int    $id   Comment ID.
209
	 * @param  string $name Custom field name.
210
	 * @param  string $type Custom field type (optional).
211
	 * @return mixed        Meta value.
212
	 */
213
	public static function get_comment_meta( $id, $name, $type = null ) {
214
		$name = $name[0] == '_' ? $name: '_' . $name;
215
216
		return self::get_field_value( 'comment_meta', $name, $type, $id );
217
	}
218
219
	/**
220
	 * Retrieve a certain field value from the database.
221
	 * Handles the logic for different field types.
222
	 *
223
	 * @param  string $data_type Data type.
224
	 * @param  string $name      Custom field name.
225
	 * @param  string $type      Custom field type (optional).
226
	 * @param  int    $id        ID (optional).
227
	 * @return mixed             Meta value.
228
	 */
229
	public static function get_field_value( $data_type, $name, $type = null, $id = null ) {
230
		$datastore_name = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $data_type ) ) );
231
232
		switch ( $type ) {
233
			case 'complex':
234
				$value = self::get_complex_fields( $datastore_name, $name, $id );
235
			break;
236
237
			case 'map':
238
			case 'map_with_address':
239
				$value = array(
240
					'lat' => (float) self::get_field_value_by_store( $data_type, $name . '-lat', $id ),
241
					'lng' => (float) self::get_field_value_by_store( $data_type, $name . '-lng', $id ),
242
					'address' => self::get_field_value_by_store( $data_type, $name . '-address', $id ),
243
					'zoom' => (int) self::get_field_value_by_store( $data_type, $name . '-zoom', $id ),
244
				);
245
246
				if ( ! array_filter( $value ) ) {
247
					$value = array();
248
				}
249
			break;
250
251
			case 'association':
252
				$raw_value = self::get_field_value_by_store( $data_type, $name, $id );
253
				$value = self::parse_relationship_field( $raw_value, $type );
254
			break;
255
256
			default:
257
				$value = self::get_field_value_by_store( $data_type, $name, $id );
258
259
				// backward compatibility for the old Relationship field
260
				$value = self::maybe_old_relationship_field( $value );
261
		}
262
263
		return $value;
264
	}
265
266
	/**
267
	 * Retrieve a certain field value from the database.
268
	 * Handles the logic for different data stores (containers).
269
	 *
270
	 * @param  string $store_type Data store type.
271
	 * @param  string $name       Custom field name.
272
	 * @param  int    $id         ID (optional).
273
	 * @return mixed              Meta value.
274
	 */
275
	public static function get_field_value_by_store( $store_type, $name, $id = null ) {
276
		$args = array( $id, $name, true );
277
		$function = '';
278
279
		switch ( $store_type ) {
280
			case 'post_meta':
281
				$function = 'get_post_meta';
282
			break;
283
284
			case 'user_meta':
285
				$function = 'get_user_meta';
286
			break;
287
288
			case 'comment_meta':
289
				$function = 'get_comment_meta';
290
			break;
291
292
			case 'term_meta':
293
				$function = 'get_metadata';
294
				$args = array( 'term', $id, $name, true );
295
			break;
296
297
			case 'theme_options':
298
				$function = 'get_option';
299
				$args = array( $name );
300
			break;
301
		}
302
303
		if ( ! empty( $function ) && function_exists( $function ) ) {
304
			return call_user_func_array( $function, $args );
305
		}
306
307
		return false;
308
	}
309
310
	/**
311
	 * Adds the field/container template(s) to the templates stack.
312
	 *
313
	 * @param object $object field or container object
314
	 **/
315
	public function add_templates( $object ) {
316
		$templates = $object->get_templates();
317
318
		if ( ! $templates ) {
319
			return false;
320
		}
321
322
		foreach ( $templates as $name => $callback ) {
323
			ob_start();
324
325
			call_user_func( $callback );
326
327
			$html = ob_get_clean();
328
329
			// Add the template to the stack
330
			Templater::add_template( $name, $html );
331
		}
332
	}
333
334
	/**
335
	 * Build a string of concatenated pieces for an OR regex.
336
	 *
337
	 * @param  array  $pieces Pieces
338
	 * @param  string $glue   Glue between the pieces
339
	 * @return string         Result string
340
	 */
341
	public static function preg_quote_array( $pieces, $glue = '|' ) {
342
		$pieces = array_map( 'preg_quote', $pieces, array( '~' ) );
343
344
		return implode( $glue, $pieces );
345
	}
346
347
	/**
348
	 * Build the regex for parsing a certain complex field.
349
	 *
350
	 * @param  string $field_name  Name of the complex field.
351
	 * @param  array  $group_names Array of group names.
352
	 * @param  array  $field_names Array of subfield names.
353
	 * @return string              Regex
354
	 */
355
	public static function get_complex_field_regex( $field_name, $group_names = array(), $field_names = array() ) {
356
		if ( ! empty( $group_names ) ) {
357
			$group_regex = self::preg_quote_array( $group_names );
358
		} else {
359
			$group_regex = '\w*';
360
		}
361
362
		if ( ! empty( $field_names ) ) {
363
			$field_regex = self::preg_quote_array( $field_names );
364
		} else {
365
			$field_regex = '.*?';
366
		}
367
368
		return '~^' . preg_quote( $field_name, '~' ) . '(?P<group>' . $group_regex . ')-_?(?P<key>' . $field_regex . ')_(?P<index>\d+)_?(?P<sub>\w+)?(-(?P<trailing>.*))?$~';
369
	}
370
371
	/**
372
	 * Retrieve the complex field data for a certain field.
373
	 *
374
	 * @param  string $type Datastore type.
375
	 * @param  string $name Name of the field.
376
	 * @param  int    $id   ID of the entry (optional).
377
	 * @return array        Complex data entries.
378
	 */
379
	public static function get_complex_fields( $type, $name, $id = null ) {
380
		$datastore = Datastore::factory( $type );
381
382
		if ( $id !== null ) {
383
			$datastore->set_id( $id );
384
		}
385
386
		$group_rows = $datastore->load_values( $name );
387
		$input_groups = array();
388
389
		foreach ( $group_rows as $row ) {
390
			if ( ! preg_match( self::get_complex_field_regex( $name ), $row['field_key'], $field_name ) ) {
391
					continue;
392
			}
393
394
			$row['field_value'] = maybe_unserialize( $row['field_value'] );
395
396
			// backward compatibility for Relationship field
397
			$row['field_value'] = self::parse_relationship_field( $row['field_value'] );
398
399
			$input_groups[ $field_name['index'] ]['_type'] = $field_name['group'];
400
			if ( ! empty( $field_name['trailing'] ) ) {
401
				$input_groups = self::expand_nested_field( $input_groups, $row, $field_name );
402 View Code Duplication
			} else if ( ! empty( $field_name['sub'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
403
				$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $field_name['sub'] ] = $row['field_value'];
404
			} else {
405
				$input_groups[ $field_name['index'] ][ $field_name['key'] ] = $row['field_value'];
406
			}
407
		}
408
409
		// create groups list with loaded fields
410
		self::ksort_recursive( $input_groups );
411
412
		return $input_groups;
413
	}
414
415
	/**
416
	 * Recursively expand the subfields of a complex field.
417
	 *
418
	 * @param  array $input_groups Input groups.
419
	 * @param  array $row          Data row (key and value).
420
	 * @param  array $field_name   Field name pieces.
421
	 * @return array               Expanded data.
422
	 */
423
	public static function expand_nested_field( $input_groups, $row, $field_name ) {
424
		$subfield_key_token = $field_name['key'] . '_' . $field_name['sub'] . '-' . $field_name['trailing'];
425
		if ( ! preg_match( self::get_complex_field_regex( $field_name['key'] ), $subfield_key_token, $subfield_name ) ) {
426
			return $input_groups;
427
		}
428
429
		$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ]['_type'] = $subfield_name['group'];
430
431
		if ( ! empty( $subfield_name['trailing'] ) ) {
432
			$input_groups[ $field_name['index'] ][ $field_name['key'] ] = self::expand_nested_field( $input_groups[ $field_name['index'] ][ $field_name['key'] ], $row, $subfield_name );
433 View Code Duplication
		} else if ( ! empty( $subfield_name['sub'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
434
			$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ][ $subfield_name['sub'] ] = $row['field_value'];
435
		} else {
436
			$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ] = $row['field_value'];
437
		}
438
439
		return $input_groups;
440
	}
441
442
	/**
443
	 * Parse the raw value of the relationship and association fields.
444
	 *
445
	 * @param  string $raw_value Raw relationship value.
446
	 * @param  string $type      Field type.
447
	 * @return array             Array of parsed data.
448
	 */
449
	public static function parse_relationship_field( $raw_value = '', $type = '' ) {
450
		if ( $raw_value && is_array( $raw_value ) ) {
451
			$value = array();
452
			foreach ( $raw_value as $raw_value_item ) {
453
				if ( is_string( $raw_value_item ) && strpos( $raw_value_item, ':' ) !== false ) {
454
					$item_data = explode( ':', $raw_value_item );
455
					$item = array(
456
						'id' => $item_data[2],
457
						'type' => $item_data[0],
458
					);
459
460
					if ( $item_data[0] === 'post' ) {
461
						$item['post_type'] = $item_data[1];
462
					} elseif ( $item_data[0] === 'term' ) {
463
						$item['taxonomy'] = $item_data[1];
464
					}
465
466
					$value[] = $item;
467
				} elseif ( $type === 'association' ) {
468
					$value[] = array(
469
						'id' => $raw_value_item,
470
						'type' => 'post',
471
						'post_type' => get_post_type( $raw_value_item ),
472
					);
473
				} else {
474
					$value[] = $raw_value_item;
475
				}
476
			}
477
478
			$raw_value = $value;
479
		}
480
481
		return $raw_value;
482
	}
483
484
	/**
485
	 * Detect if using the old way of storing the relationship field values.
486
	 * If so, parse them to the new way of storing the data.
487
	 *
488
	 * @param  mixed $value Old field value.
489
	 * @return mixed        New field value.
490
	 */
491
	public static function maybe_old_relationship_field( $value ) {
492
		if ( is_array( $value ) && ! empty( $value ) ) {
493
			if ( preg_match( '~^\w+:\w+:\d+$~', $value[0] ) ) {
494
				$new_value = array();
495
				foreach ( $value as $value_entry ) {
496
					$pieces = explode( ':', $value_entry );
497
					$new_value[] = $pieces[2];
498
				}
499
				$value = $new_value;
500
			}
501
		}
502
503
		return $value;
504
	}
505
506
	/**
507
	 * Recursive sorting function by array key.
508
	 * @param  array  &$array     The input array.
509
	 * @param  int    $sort_flags Flags for controlling sorting behavior.
510
	 * @return array              Sorted array.
511
	 */
512
	public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) {
513
		if ( ! is_array( $array ) ) {
514
			return false;
515
		}
516
517
		ksort( $array, $sort_flags );
518
		foreach ( $array as $key => $value ) {
519
			self::ksort_recursive( $array[ $key ], $sort_flags );
520
		}
521
522
		return true;
523
	}
524
}
525