Completed
Push — master ( 607c85...0ab7b5 )
by Marin
02:34
created

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