Completed
Push — development ( 381f0e...33ac70 )
by
unknown
05:34
created

Helper::get_field_value_by_datastore()   C

Complexity

Conditions 8
Paths 12

Size

Total Lines 34
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
cc 8
eloc 24
nc 12
nop 3
dl 0
loc 34
ccs 0
cts 23
cp 0
crap 72
rs 5.3846
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\Libraries\Plugin_Update_Warning\Plugin_Update_Warning;
9
use Carbon_Fields\Libraries\Sidebar_Manager\Sidebar_Manager;
10
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
11
12
/**
13
 * Helper functions and main initialization class.
14
 */
15
class Helper {
16
17
	/**
18
	 * Create a new helper.
19
	 * Hook the main Carbon Fields initialization functionality.
20
	 */
21
	public function __construct() {
22
		if ( is_admin() ) {
23
			# Initialize plugin update warning
24
			Plugin_Update_Warning::instance();
25
		}
26
		
27
		# Initialize sidebar manager
28
		Sidebar_Manager::instance();
29
		
30
		add_action( 'init', array( $this, 'trigger_fields_register' ), 0 );
31
		add_action( 'carbon_after_register_fields', array( $this, 'init_containers' ) );
32
		add_action( 'admin_footer', array( $this, 'init_scripts' ), 0 );
33
		add_action( 'admin_print_footer_scripts', array( $this, 'print_json_data_script' ), 9 );
34
		add_action( 'crb_field_activated', array( $this, 'add_templates' ) );
35
		add_action( 'crb_container_activated', array( $this, 'add_templates' ) );
36
		add_action( 'after_setup_theme', array( $this, 'load_textdomain' ), 9999 );
37
38
		# Initialize templater
39
		new Templater();
40
	}
41
42
	/**
43
	 * Load the plugin textdomain.
44
	 */
45
	public function load_textdomain() {
46
		$dir = dirname( dirname( __DIR__ ) ) . '/languages/';
47
		$domain = 'carbon-fields';
48
		$locale = get_locale();
49
		$path = $dir . $domain . '-' . $locale . '.mo';
50
		load_textdomain( $domain, $path );
51
	}
52
53
	/**
54
	 * Register containers and fields.
55
	 */
56
	public function trigger_fields_register() {
57
		try {
58
			do_action( 'carbon_register_fields' );
59
			do_action( 'carbon_after_register_fields' );
60
		} catch ( Incorrect_Syntax_Exception $e ) {
61
			$callback = '';
62
			foreach ( $e->getTrace() as $trace ) {
63
				$callback .= '<br/>' . ( isset( $trace['file'] ) ? $trace['file'] . ':' . $trace['line'] : $trace['function'] . '()' );
64
			}
65
			wp_die( '<h3>' . $e->getMessage() . '</h3><small>' . $callback . '</small>' );
66
		}
67
	}
68
69
	/**
70
	 * Initialize containers.
71
	 */
72
	public function init_containers() {
73
		Container::init_containers();
74
	}
75
76
	/**
77
	 * Initialize main scripts
78
	 */
79
	public function init_scripts() {
80
		wp_enqueue_script( 'carbon-ext', \Carbon_Fields\URL . '/assets/js/ext.js', array( 'jquery' ), \Carbon_Fields\VERSION );
81
		wp_enqueue_script( 'carbon-app', \Carbon_Fields\URL . '/assets/js/app.js', array( 'jquery', 'backbone', 'underscore', 'jquery-touch-punch', 'jquery-ui-sortable', 'carbon-ext' ), \Carbon_Fields\VERSION );
82
	}
83
84
	/**
85
	 * Print the carbon JSON data script.
86
	 */
87
	public function print_json_data_script() {
88
		?>
89
<script type="text/javascript">
90
<!--//--><![CDATA[//><!--
91
var carbon_json = <?php echo wp_json_encode( $this->get_json_data() ); ?>;
92
//--><!]]>
93
</script>
94
		<?php
95
	}
96
97
	/**
98
	 * Retrieve containers and sidebars for use in the JS.
99
	 *
100
	 * @return array $carbon_data
101
	 */
102
	public function get_json_data() {
103
		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...
104
105
		$carbon_data = array(
106
			'containers' => array(),
107
			'sidebars' => array(),
108
		);
109
110
		$containers = Container::get_active_containers();
111
112
		foreach ( $containers as $container ) {
113
			$container_data = $container->to_json( true );
114
115
			$carbon_data['containers'][] = $container_data;
116
		}
117
118
		foreach ( $wp_registered_sidebars as $sidebar ) {
119
			// Check if we have inactive sidebars
120
			if ( isset( $sidebar['class'] ) && strpos( $sidebar['class'], 'inactive-sidebar' ) !== false ) {
121
				continue;
122
			}
123
124
			$carbon_data['sidebars'][] = array(
125
				'name' => $sidebar['name'],
126
				'id'   => $sidebar['id'],
127
			);
128
		}
129
130
		return $carbon_data;
131
	}
132
133
	/**
134
	 * Retrieve post meta field for a post.
135
	 *
136
	 * @param  int    $id   Post ID.
137
	 * @param  string $name Custom field name.
138
	 * @param  string $type Custom field type (optional).
139
	 * @return mixed        Meta value.
140
	 */
141
	public static function get_post_meta( $id, $name, $type = null ) {
142
		$name = $name[0] == '_' ? $name : '_' . $name;
143
144
		return self::get_field_value( 'post_meta', $name, $type, $id );
145
	}
146
147
	/**
148
	 * Shorthand for get_post_meta().
149
	 * Uses the ID of the current post in the loop.
150
	 *
151
	 * @param  string $name Custom field name.
152
	 * @param  string $type Custom field type (optional).
153
	 * @return mixed        Meta value.
154
	 */
155
	public static function get_the_post_meta( $name, $type = null ) {
156
		return self::get_post_meta( get_the_ID(), $name, $type );
157
	}
158
159
	/**
160
	 * Retrieve theme option field value.
161
	 *
162
	 * @param  string $name Custom field name.
163
	 * @param  string $type Custom field type (optional).
164
	 * @return mixed        Option value.
165
	 */
166
	public static function get_theme_option( $name, $type = null ) {
167
		return self::get_field_value( 'theme_options', $name, $type );
168
	}
169
170
	/**
171
	 * Retrieve term meta field for a term.
172
	 *
173
	 * @param  int    $id   Term ID.
174
	 * @param  string $name Custom field name.
175
	 * @param  string $type Custom field type (optional).
176
	 * @return mixed        Meta value.
177
	 */
178
	public static function get_term_meta( $id, $name, $type = null ) {
179
		$name = $name[0] == '_' ? $name: '_' . $name;
180
181
		return self::get_field_value( 'term_meta', $name, $type, $id );
182
	}
183
184
	/**
185
	 * Retrieve user meta field for a user.
186
	 *
187
	 * @param  int    $id   User ID.
188
	 * @param  string $name Custom field name.
189
	 * @param  string $type Custom field type (optional).
190
	 * @return mixed        Meta value.
191
	 */
192
	public static function get_user_meta( $id, $name, $type = null ) {
193
		$name = $name[0] == '_' ? $name: '_' . $name;
194
195
		return self::get_field_value( 'user_meta', $name, $type, $id );
196
	}
197
198
	/**
199
	 * Retrieve comment meta field for a comment.
200
	 *
201
	 * @param  int    $id   Comment ID.
202
	 * @param  string $name Custom field name.
203
	 * @param  string $type Custom field type (optional).
204
	 * @return mixed        Meta value.
205
	 */
206
	public static function get_comment_meta( $id, $name, $type = null ) {
207
		$name = $name[0] == '_' ? $name: '_' . $name;
208
209
		return self::get_field_value( 'comment_meta', $name, $type, $id );
210
	}
211
212
	/**
213
	 * Retrieve a certain field value from the database.
214
	 * Handles the logic for different field types.
215
	 *
216
	 * @param  string $data_type Data type.
217
	 * @param  string $name      Custom field name.
218
	 * @param  string $type      Custom field type (optional).
219
	 * @param  int    $id        ID (optional).
220
	 * @return mixed             Meta value.
221
	 */
222
	public static function get_field_value( $data_type, $name, $type = null, $id = null ) {
223
		$datastore_name = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $data_type ) ) );
224
225
		switch ( $type ) {
226
			case 'complex':
227
				$value = self::get_complex_fields( $datastore_name, $name, $id );
228
			break;
229
230
			case 'map':
231
			case 'map_with_address':
232
				$value = array(
233
					'lat' => (float) self::get_field_value_by_datastore( $data_type, $name . '-lat', $id ),
234
					'lng' => (float) self::get_field_value_by_datastore( $data_type, $name . '-lng', $id ),
235
					'address' => self::get_field_value_by_datastore( $data_type, $name . '-address', $id ),
236
					'zoom' => (int) self::get_field_value_by_datastore( $data_type, $name . '-zoom', $id ),
237
				);
238
239
				if ( ! array_filter( $value ) ) {
240
					$value = array();
241
				}
242
			break;
243
244
			case 'association':
245
				$raw_value = self::get_field_value_by_datastore( $data_type, $name, $id );
246
				$value = self::parse_relationship_field( $raw_value, $type );
247
			break;
248
249
			default:
250
				$value = self::get_field_value_by_datastore( $data_type, $name, $id );
251
252
				// backward compatibility for the old Relationship field
253
				$value = self::maybe_old_relationship_field( $value );
254
		}
255
256
		return $value;
257
	}
258
259
	/**
260
	 * Retrieve a certain field value from the database.
261
	 * Handles the logic for different datastores (containers).
262
	 *
263
	 * @param  string $datastore_type Datastore type.
264
	 * @param  string $name       Custom field name.
265
	 * @param  int    $id         ID (optional).
266
	 * @return mixed              Meta value.
267
	 */
268
	public static function get_field_value_by_datastore( $datastore_type, $name, $id = null ) {
269
		$args = array( $id, $name, true );
270
		$function = '';
271
272
		switch ( $datastore_type ) {
273
			case 'post_meta':
274
				$function = 'get_post_meta';
275
			break;
276
277
			case 'user_meta':
278
				$function = 'get_user_meta';
279
			break;
280
281
			case 'comment_meta':
282
				$function = 'get_comment_meta';
283
			break;
284
285
			case 'term_meta':
286
				$function = 'get_metadata';
287
				$args = array( 'term', $id, $name, true );
288
			break;
289
290
			case 'theme_options':
291
				$function = 'get_option';
292
				$args = array( $name );
293
			break;
294
		}
295
296
		if ( ! empty( $function ) && function_exists( $function ) ) {
297
			return call_user_func_array( $function, $args );
298
		}
299
300
		return false;
301
	}
302
303
	/**
304
	 * Adds the field/container template(s) to the templates stack.
305
	 *
306
	 * @param object $object field or container object
307
	 **/
308
	public function add_templates( $object ) {
309
		$templates = $object->get_templates();
310
311
		if ( ! $templates ) {
312
			return false;
313
		}
314
315
		foreach ( $templates as $name => $callback ) {
316
			ob_start();
317
318
			call_user_func( $callback );
319
320
			$html = ob_get_clean();
321
322
			// Add the template to the stack
323
			Templater::add_template( $name, $html );
324
		}
325
	}
326
327
	/**
328
	 * Build a string of concatenated pieces for an OR regex.
329
	 *
330
	 * @param  array  $pieces Pieces
331
	 * @param  string $glue   Glue between the pieces
332
	 * @return string         Result string
333
	 */
334
	public static function preg_quote_array( $pieces, $glue = '|' ) {
335
		$pieces = array_map( 'preg_quote', $pieces, array( '~' ) );
336
337
		return implode( $glue, $pieces );
338
	}
339
340
	/**
341
	 * Build the regex for parsing a certain complex field.
342
	 *
343
	 * @param  string $field_name  Name of the complex field.
344
	 * @param  array  $group_names Array of group names.
345
	 * @param  array  $field_names Array of subfield names.
346
	 * @return string              Regex
347
	 */
348
	public static function get_complex_field_regex( $field_name, $group_names = array(), $field_names = array() ) {
349
		if ( ! empty( $group_names ) ) {
350
			$group_regex = self::preg_quote_array( $group_names );
351
		} else {
352
			$group_regex = '\w*';
353
		}
354
355
		if ( ! empty( $field_names ) ) {
356
			$field_regex = self::preg_quote_array( $field_names );
357
		} else {
358
			$field_regex = '.*?';
359
		}
360
361
		return '~^' . preg_quote( $field_name, '~' ) . '(?P<group>' . $group_regex . ')-_?(?P<key>' . $field_regex . ')_(?P<index>\d+)_?(?P<sub>\w+)?(-(?P<trailing>.*))?$~';
362
	}
363
364
	/**
365
	 * Retrieve the complex field data for a certain field.
366
	 *
367
	 * @param  string $type Datastore type.
368
	 * @param  string $name Name of the field.
369
	 * @param  int    $id   ID of the entry (optional).
370
	 * @return array        Complex data entries.
371
	 */
372
	public static function get_complex_fields( $type, $name, $id = null ) {
373
		$datastore = Datastore::factory( $type );
374
375
		if ( $id !== null ) {
376
			$datastore->set_id( $id );
377
		}
378
379
		$group_rows = $datastore->load_values( $name );
380
		$input_groups = array();
381
382
		foreach ( $group_rows as $row ) {
383
			if ( ! preg_match( self::get_complex_field_regex( $name ), $row['field_key'], $field_name ) ) {
384
					continue;
385
			}
386
387
			$row['field_value'] = maybe_unserialize( $row['field_value'] );
388
389
			// backward compatibility for Relationship field
390
			$row['field_value'] = self::parse_relationship_field( $row['field_value'] );
391
392
			$input_groups[ $field_name['index'] ]['_type'] = $field_name['group'];
393
			if ( ! empty( $field_name['trailing'] ) ) {
394
				$input_groups = self::expand_nested_field( $input_groups, $row, $field_name );
395 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...
396
				$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $field_name['sub'] ] = $row['field_value'];
397
			} else {
398
				$input_groups[ $field_name['index'] ][ $field_name['key'] ] = $row['field_value'];
399
			}
400
		}
401
402
		// create groups list with loaded fields
403
		self::ksort_recursive( $input_groups );
404
405
		return $input_groups;
406
	}
407
408
	/**
409
	 * Recursively expand the subfields of a complex field.
410
	 *
411
	 * @param  array $input_groups Input groups.
412
	 * @param  array $row          Data row (key and value).
413
	 * @param  array $field_name   Field name pieces.
414
	 * @return array               Expanded data.
415
	 */
416
	public static function expand_nested_field( $input_groups, $row, $field_name ) {
417
		$subfield_key_token = $field_name['key'] . '_' . $field_name['sub'] . '-' . $field_name['trailing'];
418
		if ( ! preg_match( self::get_complex_field_regex( $field_name['key'] ), $subfield_key_token, $subfield_name ) ) {
419
			return $input_groups;
420
		}
421
422
		$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ]['_type'] = $subfield_name['group'];
423
424
		if ( ! empty( $subfield_name['trailing'] ) ) {
425
			$input_groups[ $field_name['index'] ][ $field_name['key'] ] = self::expand_nested_field( $input_groups[ $field_name['index'] ][ $field_name['key'] ], $row, $subfield_name );
426 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...
427
			$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ][ $subfield_name['sub'] ] = $row['field_value'];
428
		} else {
429
			$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ] = $row['field_value'];
430
		}
431
432
		return $input_groups;
433
	}
434
435
	/**
436
	 * Parse the raw value of the relationship and association fields.
437
	 *
438
	 * @param  string $raw_value Raw relationship value.
439
	 * @param  string $type      Field type.
440
	 * @return array             Array of parsed data.
441
	 */
442
	public static function parse_relationship_field( $raw_value = '', $type = '' ) {
443
		if ( $raw_value && is_array( $raw_value ) ) {
444
			$value = array();
445
			foreach ( $raw_value as $raw_value_item ) {
446
				if ( is_string( $raw_value_item ) && strpos( $raw_value_item, ':' ) !== false ) {
447
					$item_data = explode( ':', $raw_value_item );
448
					$item = array(
449
						'id' => $item_data[2],
450
						'type' => $item_data[0],
451
					);
452
453
					if ( $item_data[0] === 'post' ) {
454
						$item['post_type'] = $item_data[1];
455
					} elseif ( $item_data[0] === 'term' ) {
456
						$item['taxonomy'] = $item_data[1];
457
					}
458
459
					$value[] = $item;
460
				} elseif ( $type === 'association' ) {
461
					$value[] = array(
462
						'id' => $raw_value_item,
463
						'type' => 'post',
464
						'post_type' => get_post_type( $raw_value_item ),
465
					);
466
				} else {
467
					$value[] = $raw_value_item;
468
				}
469
			}
470
471
			$raw_value = $value;
472
		}
473
474
		return $raw_value;
475
	}
476
477
	/**
478
	 * Detect if using the old way of storing the relationship field values.
479
	 * If so, parse them to the new way of storing the data.
480
	 *
481
	 * @param  mixed $value Old field value.
482
	 * @return mixed        New field value.
483
	 */
484
	public static function maybe_old_relationship_field( $value ) {
485
		if ( is_array( $value ) && ! empty( $value ) && ! empty( $value[0] ) ) {
486
			if ( preg_match( '~^\w+:\w+:\d+$~', $value[0] ) ) {
487
				$new_value = array();
488
				foreach ( $value as $value_entry ) {
489
					$pieces = explode( ':', $value_entry );
490
					$new_value[] = $pieces[2];
491
				}
492
				$value = $new_value;
493
			}
494
		}
495
496
		return $value;
497
	}
498
499
	/**
500
	 * Recursive sorting function by array key.
501
	 * @param  array  &$array     The input array.
502
	 * @param  int    $sort_flags Flags for controlling sorting behavior.
503
	 * @return array              Sorted array.
504
	 */
505
	public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) {
506
		if ( ! is_array( $array ) ) {
507
			return false;
508
		}
509
510
		ksort( $array, $sort_flags );
511
		foreach ( $array as $key => $value ) {
512
			self::ksort_recursive( $array[ $key ], $sort_flags );
513
		}
514
515
		return true;
516
	}
517
}
518