getTrace() as $trace ) {
$callback .= '
' . ( isset( $trace['file'] ) ? $trace['file'] . ':' . $trace['line'] : $trace['function'] . '()' );
}
wp_die( '
' . $e->getMessage() . '
' . $callback . '' );
}
}
/**
* Initialize containers.
*/
public function init_containers() {
Container::init_containers();
}
/**
* Initialize main scripts
*/
public function init_scripts() {
wp_enqueue_script( 'carbon-ext', \Carbon_Fields\URL . '/assets/js/ext.js', array( 'jquery' ) );
wp_enqueue_script( 'carbon-app', \Carbon_Fields\URL . '/assets/js/app.js', array( 'jquery', 'backbone', 'underscore', 'jquery-touch-punch', 'jquery-ui-sortable', 'carbon-ext' ) );
}
/**
* Print the carbon JSON data script.
*/
public function print_json_data_script() {
?>
array(),
'sidebars' => array(),
);
$containers = Container::get_active_containers();
foreach ( $containers as $container ) {
$container_data = $container->to_json( true );
$carbon_data['containers'][] = $container_data;
}
foreach ( $wp_registered_sidebars as $sidebar ) {
// Check if we have inactive sidebars
if ( isset( $sidebar['class'] ) && strpos( $sidebar['class'], 'inactive-sidebar' ) !== false ) {
continue;
}
$carbon_data['sidebars'][] = array(
'name' => $sidebar['name'],
'id' => $sidebar['id'],
);
}
return $carbon_data;
}
/**
* Retrieve post meta field for a post.
*
* @param int $id Post ID.
* @param string $name Custom field name.
* @param string $type Custom field type (optional).
* @return mixed Meta value.
*/
public static function get_post_meta( $id, $name, $type = null ) {
$name = $name[0] == '_' ? $name : '_' . $name;
return self::get_field_value( 'post_meta', $name, $type, $id );
}
/**
* Shorthand for get_post_meta().
* Uses the ID of the current post in the loop.
*
* @param string $name Custom field name.
* @param string $type Custom field type (optional).
* @return mixed Meta value.
*/
public static function get_the_post_meta( $name, $type = null ) {
return self::get_post_meta( get_the_ID(), $name, $type );
}
/**
* Retrieve theme option field value.
*
* @param string $name Custom field name.
* @param string $type Custom field type (optional).
* @return mixed Option value.
*/
public static function get_theme_option( $name, $type = null ) {
return self::get_field_value( 'theme_options', $name, $type );
}
/**
* Retrieve term meta field for a term.
*
* @param int $id Term ID.
* @param string $name Custom field name.
* @param string $type Custom field type (optional).
* @return mixed Meta value.
*/
public static function get_term_meta( $id, $name, $type = null ) {
$name = $name[0] == '_' ? $name: '_' . $name;
return self::get_field_value( 'term_meta', $name, $type, $id );
}
/**
* Retrieve user meta field for a user.
*
* @param int $id User ID.
* @param string $name Custom field name.
* @param string $type Custom field type (optional).
* @return mixed Meta value.
*/
public static function get_user_meta( $id, $name, $type = null ) {
$name = $name[0] == '_' ? $name: '_' . $name;
return self::get_field_value( 'user_meta', $name, $type, $id );
}
/**
* Retrieve comment meta field for a comment.
*
* @param int $id Comment ID.
* @param string $name Custom field name.
* @param string $type Custom field type (optional).
* @return mixed Meta value.
*/
public static function get_comment_meta( $id, $name, $type = null ) {
$name = $name[0] == '_' ? $name: '_' . $name;
return self::get_field_value( 'comment_meta', $name, $type, $id );
}
/**
* Retrieve a certain field value from the database.
* Handles the logic for different field types.
*
* @param string $data_type Data type.
* @param string $name Custom field name.
* @param string $type Custom field type (optional).
* @param int $id ID (optional).
* @return mixed Meta value.
*/
public static function get_field_value( $data_type, $name, $type = null, $id = null ) {
$datastore_name = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $data_type ) ) );
switch ( $type ) {
case 'complex':
$value = self::get_complex_fields( $datastore_name, $name, $id );
break;
case 'map':
case 'map_with_address':
$value = array(
'lat' => (float) self::get_field_value_by_store( $data_type, $name . '-lat', $id ),
'lng' => (float) self::get_field_value_by_store( $data_type, $name . '-lng', $id ),
'address' => self::get_field_value_by_store( $data_type, $name . '-address', $id ),
'zoom' => (int) self::get_field_value_by_store( $data_type, $name . '-zoom', $id ),
);
if ( ! array_filter( $value ) ) {
$value = array();
}
break;
case 'association':
$raw_value = self::get_field_value_by_store( $data_type, $name, $id );
$value = self::parse_relationship_field( $raw_value, $type );
break;
default:
$value = self::get_field_value_by_store( $data_type, $name, $id );
// backward compatibility for the old Relationship field
$value = self::maybe_old_relationship_field( $value );
}
return $value;
}
/**
* Retrieve a certain field value from the database.
* Handles the logic for different data stores (containers).
*
* @param string $store_type Data store type.
* @param string $name Custom field name.
* @param int $id ID (optional).
* @return mixed Meta value.
*/
public static function get_field_value_by_store( $store_type, $name, $id = null ) {
$args = array( $id, $name, true );
$function = '';
switch ( $store_type ) {
case 'post_meta':
$function = 'get_post_meta';
break;
case 'user_meta':
$function = 'get_user_meta';
break;
case 'comment_meta':
$function = 'get_comment_meta';
break;
case 'term_meta':
$function = 'get_metadata';
$args = array( 'term', $id, $name, true );
break;
case 'theme_options':
$function = 'get_option';
$args = array( $name );
break;
}
if ( ! empty( $function ) && function_exists( $function ) ) {
return call_user_func_array( $function, $args );
}
return false;
}
/**
* Adds the field/container template(s) to the templates stack.
*
* @param object $object field or container object
**/
public function add_templates( $object ) {
$templates = $object->get_templates();
if ( ! $templates ) {
return false;
}
foreach ( $templates as $name => $callback ) {
ob_start();
call_user_func( $callback );
$html = ob_get_clean();
// Add the template to the stack
Templater::add_template( $name, $html );
}
}
/**
* Build a string of concatenated pieces for an OR regex.
*
* @param array $pieces Pieces
* @param string $glue Glue between the pieces
* @return string Result string
*/
public static function preg_quote_array( $pieces, $glue = '|' ) {
$pieces = array_map( 'preg_quote', $pieces, array( '~' ) );
return implode( $glue, $pieces );
}
/**
* Build the regex for parsing a certain complex field.
*
* @param string $field_name Name of the complex field.
* @param array $group_names Array of group names.
* @param array $field_names Array of subfield names.
* @return string Regex
*/
public static function get_complex_field_regex( $field_name, $group_names = array(), $field_names = array() ) {
if ( ! empty( $group_names ) ) {
$group_regex = self::preg_quote_array( $group_names );
} else {
$group_regex = '\w*';
}
if ( ! empty( $field_names ) ) {
$field_regex = self::preg_quote_array( $field_names );
} else {
$field_regex = '.*?';
}
return '~^' . preg_quote( $field_name, '~' ) . '(?P' . $group_regex . ')-_?(?P' . $field_regex . ')_(?P\d+)_?(?P\w+)?(-(?P.*))?$~';
}
/**
* Retrieve the complex field data for a certain field.
*
* @param string $type Datastore type.
* @param string $name Name of the field.
* @param int $id ID of the entry (optional).
* @return array Complex data entries.
*/
public static function get_complex_fields( $type, $name, $id = null ) {
$datastore = Datastore::factory( $type );
if ( $id !== null ) {
$datastore->set_id( $id );
}
$group_rows = $datastore->load_values( $name );
$input_groups = array();
foreach ( $group_rows as $row ) {
if ( ! preg_match( self::get_complex_field_regex( $name ), $row['field_key'], $field_name ) ) {
continue;
}
$row['field_value'] = maybe_unserialize( $row['field_value'] );
// backward compatibility for Relationship field
$row['field_value'] = self::parse_relationship_field( $row['field_value'] );
$input_groups[ $field_name['index'] ]['_type'] = $field_name['group'];
if ( ! empty( $field_name['trailing'] ) ) {
$input_groups = self::expand_nested_field( $input_groups, $row, $field_name );
} else if ( ! empty( $field_name['sub'] ) ) {
$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $field_name['sub'] ] = $row['field_value'];
} else {
$input_groups[ $field_name['index'] ][ $field_name['key'] ] = $row['field_value'];
}
}
// create groups list with loaded fields
self::ksort_recursive( $input_groups );
return $input_groups;
}
/**
* Recursively expand the subfields of a complex field.
*
* @param array $input_groups Input groups.
* @param array $row Data row (key and value).
* @param array $field_name Field name pieces.
* @return array Expanded data.
*/
public static function expand_nested_field( $input_groups, $row, $field_name ) {
$subfield_key_token = $field_name['key'] . '_' . $field_name['sub'] . '-' . $field_name['trailing'];
if ( ! preg_match( self::get_complex_field_regex( $field_name['key'] ), $subfield_key_token, $subfield_name ) ) {
return $input_groups;
}
$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ]['_type'] = $subfield_name['group'];
if ( ! empty( $subfield_name['trailing'] ) ) {
$input_groups[ $field_name['index'] ][ $field_name['key'] ] = self::expand_nested_field( $input_groups[ $field_name['index'] ][ $field_name['key'] ], $row, $subfield_name );
} else if ( ! empty( $subfield_name['sub'] ) ) {
$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ][ $subfield_name['sub'] ] = $row['field_value'];
} else {
$input_groups[ $field_name['index'] ][ $field_name['key'] ][ $subfield_name['index'] ][ $subfield_name['key'] ] = $row['field_value'];
}
return $input_groups;
}
/**
* Parse the raw value of the relationship and association fields.
*
* @param string $raw_value Raw relationship value.
* @param string $type Field type.
* @return array Array of parsed data.
*/
public static function parse_relationship_field( $raw_value = '', $type = '' ) {
if ( $raw_value && is_array( $raw_value ) ) {
$value = array();
foreach ( $raw_value as $raw_value_item ) {
if ( is_string( $raw_value_item ) && strpos( $raw_value_item, ':' ) !== false ) {
$item_data = explode( ':', $raw_value_item );
$item = array(
'id' => $item_data[2],
'type' => $item_data[0],
);
if ( $item_data[0] === 'post' ) {
$item['post_type'] = $item_data[1];
} elseif ( $item_data[0] === 'term' ) {
$item['taxonomy'] = $item_data[1];
}
$value[] = $item;
} elseif ( $type === 'association' ) {
$value[] = array(
'id' => $raw_value_item,
'type' => 'post',
'post_type' => get_post_type( $raw_value_item ),
);
} else {
$value[] = $raw_value_item;
}
}
$raw_value = $value;
}
return $raw_value;
}
/**
* Detect if using the old way of storing the relationship field values.
* If so, parse them to the new way of storing the data.
*
* @param mixed $value Old field value.
* @return mixed New field value.
*/
public static function maybe_old_relationship_field( $value ) {
if ( is_array( $value ) && ! empty( $value ) ) {
if ( preg_match( '~^\w+:\w+:\d+$~', $value[0] ) ) {
$new_value = array();
foreach ( $value as $value_entry ) {
$pieces = explode( ':', $value_entry );
$new_value[] = $pieces[2];
}
$value = $new_value;
}
}
return $value;
}
/**
* Recursive sorting function by array key.
* @param array &$array The input array.
* @param int $sort_flags Flags for controlling sorting behavior.
* @return array Sorted array.
*/
public static function ksort_recursive( &$array, $sort_flags = SORT_REGULAR ) {
if ( ! is_array( $array ) ) {
return false;
}
ksort( $array, $sort_flags );
foreach ( $array as $key => $value ) {
self::ksort_recursive( $array[ $key ], $sort_flags );
}
return true;
}
}