Completed
Push — try/scrutinizer-wp-coding-stan... ( dcaef3...dfddad )
by
unknown
02:30
created

Helper::get_term_meta()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 3
dl 0
loc 5
ccs 0
cts 3
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
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( 'init', array( $this, 'trigger_fields_register' ), 0 );
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-ext', \Carbon_Fields\URL . '/assets/js/ext.js', array( 'jquery' ) );
75
		wp_enqueue_script( 'carbon-app', \Carbon_Fields\URL . '/assets/js/app.js', array( 'jquery', 'backbone', 'underscore', 'jquery-touch-punch', 'jquery-ui-sortable', 'carbon-ext' ) );
76
	}
77
78
	/**
79
	 * Print the carbon JSON data script.
80
	 */
81
	public function print_json_data_script() {
82
		?>
83
<script type="text/javascript">
84
<!--//--><![CDATA[//><!--
85
var carbon_json = <?php echo wp_json_encode( $this->get_json_data() ); ?>;
86
//--><!]]>
87
</script>
88
		<?php
89
	}
90
91
	/**
92
	 * Retrieve containers and sidebars for use in the JS.
93
	 *
94
	 * @return array $carbon_data
95
	 */
96
	public function get_json_data() {
97
		global $wp_registered_sidebars;
1 ignored issue
show
Comprehensibility Naming introduced by
The variable name $wp_registered_sidebars exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
98
99
		$carbon_data = array(
100
			'containers' => array(),
101
			'sidebars' => array(),
102
		);
103
104
		$containers = Container::get_active_containers();
105
106
		foreach ( $containers as $container ) {
107
			$container_data = $container->to_json( true );
108
109
			$carbon_data['containers'][] = $container_data;
110
		}
111
112
		foreach ( $wp_registered_sidebars as $sidebar ) {
113
			// Check if we have inactive sidebars
114
			if ( isset( $sidebar['class'] ) && strpos( $sidebar['class'], 'inactive-sidebar' ) !== false ) {
115
				continue;
116
			}
117
118
			$carbon_data['sidebars'][] = array(
119
				'name' => $sidebar['name'],
120
				'id'   => $sidebar['id'],
121
			);
122
		}
123
124
		return $carbon_data;
125
	}
126
127
	/**
128
	 * Retrieve post meta field for a post.
129
	 *
130
	 * @param  int    $id   Post ID.
131
	 * @param  string $name Custom field name.
132
	 * @param  string $type Custom field type (optional).
133
	 * @return mixed        Meta value.
134
	 */
135
	public static function get_post_meta( $id, $name, $type = null ) {
136
		$name = $name[0] == '_' ? $name : '_' . $name;
137
138
		return self::get_field_value( 'post_meta', $name, $type, $id );
139
	}
140
141
	/**
142
	 * Shorthand for get_post_meta().
143
	 * Uses the ID of the current post in the loop.
144
	 *
145
	 * @param  string $name Custom field name.
146
	 * @param  string $type Custom field type (optional).
147
	 * @return mixed        Meta value.
148
	 */
149
	public static function get_the_post_meta( $name, $type = null ) {
150
		return self::get_post_meta( get_the_ID(), $name, $type );
151
	}
152
153
	/**
154
	 * Retrieve theme option field value.
155
	 *
156
	 * @param  string $name Custom field name.
157
	 * @param  string $type Custom field type (optional).
158
	 * @return mixed        Option value.
159
	 */
160
	public static function get_theme_option( $name, $type = null ) {
161
		return self::get_field_value( 'theme_options', $name, $type );
162
	}
163
164
	/**
165
	 * Retrieve term meta field for a term.
166
	 *
167
	 * @param  int    $id   Term ID.
168
	 * @param  string $name Custom field name.
169
	 * @param  string $type Custom field type (optional).
170
	 * @return mixed        Meta value.
171
	 */
172
	public static function get_term_meta( $id, $name, $type = null ) {
173
		$name = $name[0] == '_' ? $name: '_' . $name;
174
175
		return self::get_field_value( 'term_meta', $name, $type, $id );
176
	}
177
178
	/**
179
	 * Retrieve user meta field for a user.
180
	 *
181
	 * @param  int    $id   User ID.
182
	 * @param  string $name Custom field name.
183
	 * @param  string $type Custom field type (optional).
184
	 * @return mixed        Meta value.
185
	 */
186
	public static function get_user_meta( $id, $name, $type = null ) {
187
		$name = $name[0] == '_' ? $name: '_' . $name;
188
189
		return self::get_field_value( 'user_meta', $name, $type, $id );
190
	}
191
192
	/**
193
	 * Retrieve comment meta field for a comment.
194
	 *
195
	 * @param  int    $id   Comment ID.
196
	 * @param  string $name Custom field name.
197
	 * @param  string $type Custom field type (optional).
198
	 * @return mixed        Meta value.
199
	 */
200
	public static function get_comment_meta( $id, $name, $type = null ) {
201
		$name = $name[0] == '_' ? $name: '_' . $name;
202
203
		return self::get_field_value( 'comment_meta', $name, $type, $id );
204
	}
205
206
	/**
207
	 * Retrieve a certain field value from the database.
208
	 * Handles the logic for different field types.
209
	 *
210
	 * @param  string $data_type Data type.
211
	 * @param  string $name      Custom field name.
212
	 * @param  string $type      Custom field type (optional).
213
	 * @param  int    $id        ID (optional).
214
	 * @return mixed             Meta value.
215
	 */
216
	public static function get_field_value( $data_type, $name, $type = null, $id = null ) {
217
		$datastore_name = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $data_type ) ) );
218
219
		switch ( $type ) {
220
			case 'complex':
221
				$value = self::get_complex_fields( $datastore_name, $name, $id );
222
			break;
223
224
			case 'map':
225
			case 'map_with_address':
226
				$value = array(
227
					'lat' => (float) self::get_field_value_by_store( $data_type, $name . '-lat', $id ),
228
					'lng' => (float) self::get_field_value_by_store( $data_type, $name . '-lng', $id ),
229
					'address' => self::get_field_value_by_store( $data_type, $name . '-address', $id ),
230
					'zoom' => (int) self::get_field_value_by_store( $data_type, $name . '-zoom', $id ),
231
				);
232
233
				if ( ! array_filter( $value ) ) {
234
					$value = array();
235
				}
236
			break;
237
238
			case 'association':
239
				$raw_value = self::get_field_value_by_store( $data_type, $name, $id );
240
				$value = self::parse_relationship_field( $raw_value, $type );
241
			break;
242
243
			default:
244
				$value = self::get_field_value_by_store( $data_type, $name, $id );
245
246
				// backward compatibility for the old Relationship field
247
				$value = self::maybe_old_relationship_field( $value );
248
		}
249
250
		return $value;
251
	}
252
253
	/**
254
	 * Retrieve a certain field value from the database.
255
	 * Handles the logic for different data stores (containers).
256
	 *
257
	 * @param  string $store_type Data store type.
258
	 * @param  string $name       Custom field name.
259
	 * @param  int    $id         ID (optional).
260
	 * @return mixed              Meta value.
261
	 */
262
	public static function get_field_value_by_store( $store_type, $name, $id = null ) {
263
		$args = array( $id, $name, true );
264
		$function = '';
265
266
		switch ( $store_type ) {
267
			case 'post_meta':
268
				$function = 'get_post_meta';
269
			break;
270
271
			case 'user_meta':
272
				$function = 'get_user_meta';
273
			break;
274
275
			case 'comment_meta':
276
				$function = 'get_comment_meta';
277
			break;
278
279
			case 'term_meta':
280
				$function = 'get_metadata';
281
				$args = array( 'term', $id, $name, true );
282
			break;
283
284
			case 'theme_options':
285
				$function = 'get_option';
286
				$args = array( $name );
287
			break;
288
		}
289
290
		if ( ! empty( $function ) && function_exists( $function ) ) {
291
			return call_user_func_array( $function, $args );
292
		}
293
294
		return false;
295
	}
296
297
	/**
298
	 * Adds the field/container template(s) to the templates stack.
299
	 *
300
	 * @param object $object field or container object
301
	 **/
302
	public function add_templates( $object ) {
303
		$templates = $object->get_templates();
304
305
		if ( ! $templates ) {
306
			return false;
307
		}
308
309
		foreach ( $templates as $name => $callback ) {
310
			ob_start();
311
312
			call_user_func( $callback );
313
314
			$html = ob_get_clean();
315
316
			// Add the template to the stack
317
			Templater::add_template( $name, $html );
318
		}
319
	}
320
321
	/**
322
	 * Build a string of concatenated pieces for an OR regex.
323
	 *
324
	 * @param  array  $pieces Pieces
325
	 * @param  string $glue   Glue between the pieces
326
	 * @return string         Result string
327
	 */
328
	public static function preg_quote_array( $pieces, $glue = '|' ) {
329
		$pieces = array_map( 'preg_quote', $pieces, array( '~' ) );
330
331
		return implode( $glue, $pieces );
332
	}
333
334
	/**
335
	 * Build the regex for parsing a certain complex field.
336
	 *
337
	 * @param  string $field_name  Name of the complex field.
338
	 * @param  array  $group_names Array of group names.
339
	 * @param  array  $field_names Array of subfield names.
340
	 * @return string              Regex
341
	 */
342
	public static function get_complex_field_regex( $field_name, $group_names = array(), $field_names = array() ) {
343
		if ( ! empty( $group_names ) ) {
344
			$group_regex = self::preg_quote_array( $group_names );
345
		} else {
346
			$group_regex = '\w*';
347
		}
348
349
		if ( ! empty( $field_names ) ) {
350
			$field_regex = self::preg_quote_array( $field_names );
351
		} else {
352
			$field_regex = '.*?';
353
		}
354
355
		return '~^' . preg_quote( $field_name, '~' ) . '(?P<group>' . $group_regex . ')-_?(?P<key>' . $field_regex . ')_(?P<index>\d+)_?(?P<sub>\w+)?(-(?P<trailing>.*))?$~';
356
	}
357
358
	/**
359
	 * Retrieve the complex field data for a certain field.
360
	 *
361
	 * @param  string $type Datastore type.
362
	 * @param  string $name Name of the field.
363
	 * @param  int    $id   ID of the entry (optional).
364
	 * @return array        Complex data entries.
365
	 */
366
	public static function get_complex_fields( $type, $name, $id = null ) {
367
		$datastore = Datastore::factory( $type );
368
369
		if ( $id !== null ) {
370
			$datastore->set_id( $id );
371
		}
372
373
		$group_rows = $datastore->load_values( $name );
374
		$input_groups = array();
375
376
		foreach ( $group_rows as $row ) {
377
			if ( ! preg_match( self::get_complex_field_regex( $name ), $row['field_key'], $field_name ) ) {
378
					continue;
379
			}
380
381
			$row['field_value'] = maybe_unserialize( $row['field_value'] );
382
383
			// backward compatibility for Relationship field
384
			$row['field_value'] = self::parse_relationship_field( $row['field_value'] );
385
386
			$input_groups[ $field_name['index'] ]['_type'] = $field_name['group'];
387
			if ( ! empty( $field_name['trailing'] ) ) {
388
				$input_groups = self::expand_nested_field( $input_groups, $row, $field_name );
389 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...
390
				$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $field_name['sub'] ] = $row['field_value'];
391
			} else {
392
				$input_groups[ $field_name['index'] ][ $field_name['key'] ] = $row['field_value'];
393
			}
394
		}
395
396
		// create groups list with loaded fields
397
		self::ksort_recursive( $input_groups );
398
399
		return $input_groups;
400
	}
401
402
	/**
403
	 * Recursively expand the subfields of a complex field.
404
	 *
405
	 * @param  array $input_groups Input groups.
406
	 * @param  array $row          Data row (key and value).
407
	 * @param  array $field_name   Field name pieces.
408
	 * @return array               Expanded data.
409
	 */
410
	public static function expand_nested_field( $input_groups, $row, $field_name ) {
411
		$subfield_key_token = $field_name['key'] . '_' . $field_name['sub'] . '-' . $field_name['trailing'];
412
		if ( ! preg_match( self::get_complex_field_regex( $field_name['key'] ), $subfield_key_token, $subfield_name ) ) {
413
			return $input_groups;
414
		}
415
416
		$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ]['_type'] = $subfield_name['group'];
417
418
		if ( ! empty( $subfield_name['trailing'] ) ) {
419
			$input_groups[ $field_name['index'] ][ $field_name['key'] ] = self::expand_nested_field( $input_groups[ $field_name['index'] ][ $field_name['key'] ], $row, $subfield_name );
420 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...
421
			$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ][ $subfield_name['sub'] ] = $row['field_value'];
422
		} else {
423
			$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ] = $row['field_value'];
424
		}
425
426
		return $input_groups;
427
	}
428
429
	/**
430
	 * Parse the raw value of the relationship and association fields.
431
	 *
432
	 * @param  string $raw_value Raw relationship value.
433
	 * @param  string $type      Field type.
434
	 * @return array             Array of parsed data.
435
	 */
436
	public static function parse_relationship_field( $raw_value = '', $type = '' ) {
437
		if ( $raw_value && is_array( $raw_value ) ) {
438
			$value = array();
439
			foreach ( $raw_value as $raw_value_item ) {
440
				if ( is_string( $raw_value_item ) && strpos( $raw_value_item, ':' ) !== false ) {
441
					$item_data = explode( ':', $raw_value_item );
442
					$item = array(
443
						'id' => $item_data[2],
444
						'type' => $item_data[0],
445
					);
446
447
					if ( $item_data[0] === 'post' ) {
448
						$item['post_type'] = $item_data[1];
449
					} elseif ( $item_data[0] === 'term' ) {
450
						$item['taxonomy'] = $item_data[1];
451
					}
452
453
					$value[] = $item;
454
				} elseif ( $type === 'association' ) {
455
					$value[] = array(
456
						'id' => $raw_value_item,
457
						'type' => 'post',
458
						'post_type' => get_post_type( $raw_value_item ),
459
					);
460
				} else {
461
					$value[] = $raw_value_item;
462
				}
463
			}
464
465
			$raw_value = $value;
466
		}
467
468
		return $raw_value;
469
	}
470
471
	/**
472
	 * Detect if using the old way of storing the relationship field values.
473
	 * If so, parse them to the new way of storing the data.
474
	 *
475
	 * @param  mixed $value Old field value.
476
	 * @return mixed        New field value.
477
	 */
478
	public static function maybe_old_relationship_field( $value ) {
479
		if ( is_array( $value ) && ! empty( $value ) && ! empty( $value[0] ) ) {
480
			if ( preg_match( '~^\w+:\w+:\d+$~', $value[0] ) ) {
481
				$new_value = array();
482
				foreach ( $value as $value_entry ) {
483
					$pieces = explode( ':', $value_entry );
484
					$new_value[] = $pieces[2];
485
				}
486
				$value = $new_value;
487
			}
488
		}
489
490
		return $value;
491
	}
492
493
	/**
494
	 * Recursive sorting function by array key.
495
	 * @param  array  &$array     The input array.
496
	 * @param  int    $sort_flags Flags for controlling sorting behavior.
497
	 * @return array              Sorted array.
498
	 */
499
	public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) {
500
		if ( ! is_array( $array ) ) {
501
			return false;
502
		}
503
504
		ksort( $array, $sort_flags );
505
		foreach ( $array as $key => $value ) {
506
			self::ksort_recursive( $array[ $key ], $sort_flags );
507
		}
508
509
		return true;
510
	}
511
}
512