Carbon Fields

Abstract

Carbon fields is a library for easy creation of custom(meta) fields in WordPress administration panel. It allows theme developer to associate meta-information with various entities in a WordPress site(such as posts, taxonomy terms, widgets and so on).

The main components of the library are:

  • Container - represents group of fields and controls disposition of the fields in the WordPress administration panel.
  • Field - represents single field.
  • Data Storage - controls the underlying data storage for field values.

Containers

Carbon Container is a group of custom fields and display options. Containers are displayed on different parts of the backend, according to their type and display options.

Containers have a title, which must be unique across the whole WordPress instance.

Carbon_Container::factory('custom_fields', 'Custom Data')
	->show_on_post_type('page')
	->add_fields(array(
		Carbon_Field::factory('map', 'location')->set_position(37.423156, -122.084917, 14),
		Carbon_Field::factory('choose_sidebar', 'custom_sidebar'),
		Carbon_Field::factory('image', 'Photo'),
	));

To create a new Carbon Container, you just use the container factory methodCarbon_Container::factory($type, $title), where:

$type
Identifier of the container type (accepted values are custom_fields and theme_options)
$title
Unique title of the container

Custom fields Container

Custom field containers are used to extend the post edit screens with additional fields. Field data is stored separately for each post as post meta (see add_post_meta).

Carbon_Container::factory('custom_fields', 'Post Properties')
	->add_fields(array(
		Carbon_Field::factory('map', 'location')->set_position(37.423156, -122.084917, 14),
		Carbon_Field::factory('choose_sidebar', 'custom_sidebar'),
	));

Visibility options

Custom fields containers are very flexible in terms of display options. You can select specific post type they show on, as well as category, format, parent, etc. A list of all options is displayed below:

post type

->show_on_post_type('page')

You can also show a single container on multiple post types, as seen below:

->show_on_post_type(array('page', 'my_custom_post_type', 'post'))

categories and custom taxonomies

Containers may be assigned to posts from specific categories or taxonomies:

->show_on_category($category_slug)

->show_on_taxonomy_term($term_slug, $taxonomy)

pages and subpages

Show container on a specific page, identified by path:

->show_on_page($page_path)

Show container on all subpages of a specific page, identified by path:

->show_on_page_children($parent_page_path)

page templates

Containers may be assigned to pages using specific template:

->show_on_template($template_path)
The $template_path is the name of the template file (or array of template file names), for example: "about_us.php"

You can also hide the continer from pages using specific template:

->hide_on_template($template_path)

post formats
To display a container posts with specific format, use:

->show_on_post_format($post_format)

Accessing field values

To access field values you need to use the function carbon_get_post_meta($id, $name, $type = null), where:

$id
Post ID where your value was entered.
$name
The name of the field to be retrieved.
$type (optional)
If the field you want to retrieve is of type complex, you need to pass "complex".
<!-- Simple field -->
<p>Article was published in: <?php echo carbon_get_post_meta($article->ID, 'location'); ?></p>

<!-- Complex field -->
<?php 
$slides = carbon_get_post_meta($page->ID, 'slides', 'complex');
foreach ($slides as $slide) {
	echo $slide['image'];
}
?>

You can also use carbon_get_the_post_meta($name, $type = null) to access the values for the current post in The Loop.

<p>Article was published in: <?php echo carbon_get_the_post_meta('location'); ?></p>
<?php $slides = carbon_get_the_post_meta('slides', 'complex'); ?>

Theme Options Container

Theme option containers are used to add pages with options in the back-end. Field data is stored as options.

By default, theme options containers automatically create main page in the admin area menu named "Theme Options". In most cases these default settings are sufficient, but if you need to change the title or the location of a page in the menu, read the "Multiple option pages" section below.

Carbon_Container::factory('theme_options', 'Theme Options')
	->add_fields(array(
		Carbon_Field::factory('text', 'facebook_url'),
		Carbon_Field::factory('textarea', 'footer_text')
	));

Multiple option pages

It is sometimes needed to create more than one option page. At other times you need to place different pages in different sections in the admin menu. For example, you might have extensive list of settings for the background that you would want to place on a separate Theme options page under Appearance.

To change the location of your Theme Options page, you use set_page_parent($parent), where:

$parent
This can be either:
- title of a top level Theme Options page. - indentificator of a top level menu section in the admin menu sidebar. This corresponds to the $parent_slug parameter of add_submenu_page. You can see all predefined page parents here.

Below you see sample code for creating three theme option containers:

// Default options page
Carbon_Container::factory('theme_options', 'Basic Options')
	->add_fields(array(
		Carbon_Field::factory('header_scripts', 'header_script'),
		Carbon_Field::factory('footer_scripts', 'footer_script'),
	));

// Add second options page under 'Basic Options'
Carbon_Container::factory('theme_options', 'Social Links')
	->set_page_parent('Basic Options')	// title of a top level Theme Options page
	->add_fields(array(
		Carbon_Field::factory('text', 'facebook_link'),
		Carbon_Field::factory('text', 'twitter_link')
	));

// Add third options page under "Appearance"
Carbon_Container::factory('theme_options', 'Customize Background')
	->set_page_parent('themes.php')	// indentificator of the "Appearance" admin section
	->add_fields(array(
		Carbon_Field::factory('color', 'background_color'),
		Carbon_Field::factory('image', 'background_image')
	));

For detailed information on managing admin pages, see Administration_Menus.

Every theme options container requires a level of permission, which by default is set to edit_themes (read more about permissions: Roles & Capabilities).
You can change the permission required to view your options page using set_page_permissions($permission)

Accessing field values

To retrieve field values from a theme options container, you need to use the function carbon_get_theme_option($name, $type = null), where:

$name
The name of the field to be retrieved.
$type (optional)
If the field you want to retrieve is of type complex, you need to pass "complex".
<p>Copyright <?php echo carbon_get_theme_option('copyright'); ?></p>
<p>
	Office locations:
	<?php 
	$address_lines = carbon_get_theme_option('addresses', 'complex');
	foreach ($address_lines as $line) {
		echo $line . '<br/>';
	}
	?>
<p>

Term meta Container

Term meta containers are used to extend the term edit screens with additional fields. Field data is stored separately for each term in a custom table ($wpdb->termmeta).

Carbon_Container::factory('term_meta', 'Category Properties')
	->add_fields(array(
		Carbon_Field::factory('color', 'title_color'),
		Carbon_Field::factory('image', 'thumb'),
	));

By default the term meta containers are displayed on category terms, but you can select specific taxonomies they show on using the method show_on_taxonomy($taxonomy), where:

$taxonomy
Can be either name of a single taxonomy or an array of taxonomy names

Accessing field values

To access field values you need to use the function carbon_get_term_meta($term_d, $name, $type = null), where:

$term_d
Term ID where your value was entered.
$name
The name of the field to be retrieved.
$type (optional)
If the field you want to retrieve is of type complex, you need to pass "complex".
<!-- Simple field -->
<p>Editor of this category: <?php echo carbon_get_term_meta($category->ID, 'editor'); ?></p>

<!-- Complex field -->
<?php 
$authors = carbon_get_term_meta($category->ID, 'authors', 'complex');
foreach ($authors as $author) {
	echo $author['name'];
}
?>

User meta Container

User meta containers add extra fields to the user edit screens. Field data is stored separately for each user as user meta (see add_user_meta).

Carbon_Container::factory('user_meta', 'Address')
	->add_fields(array(
		Carbon_Field::factory('text', 'city_and_post', 'City and post code'),
		Carbon_Field::factory('text', 'street', 'Street Name'),
	));

By default the user meta containers are displayed for all users of all roles, but you can select specific user roles they show on using the method show_on_user_role($role), where:

$role
Can be either name of a single role or an array of role names

Accessing field values

To access field values you need to use the function carbon_get_user_meta($user_d, $name, $type = null), where:

$user_d
User ID where your value was entered.
$name
The name of the field to be retrieved.
$type (optional)
If the field you want to retrieve is of type complex, you need to pass "complex".
<!-- Simple field -->
<?php $author = get_the_author(); ?>
<p>Author address: <?php echo carbon_get_user_meta($author->ID, 'street'); ?></p>

<!-- Complex field -->
<?php 
$phone_numbers = carbon_get_user_meta($author->ID, 'phone_numbers', 'complex');
foreach ($phone_numbers as $phone) {
	echo $phone['country_code'] . '-' . $phone['number'];
}
?>

Widgets

Widget containers are used to create custom widgets for your theme. Each widget is defined as a PHP class. Widget classes must extend the Carbon_Widget class and must have at least two methods - constructor and front_end method.

The constructor must be named after the class and it must call the method setup($name, $description, $fields, $classname), where:

$name
Name of the widget, used in the back-end
$description
Description of the widget, used in the back-end
$fields
Array of Carbon fields
$classname
optional | Provide a custom class attribute for the widget.

The front_end($args, $instance) method is responsible for rendering your widget in the front-end. Here you have access to all values saved for the fields you defined in the constructor via the $instance parameter.

After you define your class, it is important that you register your new widget during the widgets_init action.

class ThemeWidgetExample extends Carbon_Widget {
	// Register widget function. Must have the same name as the class
	function __construct() {
		$this->setup('Theme Widget - Example', 'Displays a block with title/text', array(
			Carbon_Field::factory('text', 'title', 'Title')->set_default_value('Hello World!'),
			Carbon_Field::factory('textarea', 'content', 'Content')->set_default_value('Lorem Ipsum dolor sit amet')
		));
	}
	
	// Called when rendering the widget in the front-end
	function front_end($args, $instance) {
		echo $args['before_title'] . $instance['title'] . $args['after_title'];
		echo '<p>' . $instance['content'] . '</p>';
	}
}

function load_widgets() {
	register_widget('ThemeWidgetExample');
}
add_action('widgets_init', 'load_widgets');

Fields

Fields are the building block of every container.

New field are created using the factory method Carbon_Field::factory($type, $name, $label=null), where:

$type
The type of the field. This parameter should be valid class name of a field. For example text will create field of class Carbon_Field_Text. For a complete list of field types, see Types.
$name
Name of the field. Used as a key when stored in the database and for you to retrieve it's value. Please note, that all fields in a Custom Field Container have their names automatically prefixed with an underscored (e.g. bgcolor becomes _bgcolor). For more information why, see here
$label (optional)
The label of the field is displayed in the back-end only, where the container is visible. When the parameter is omitted it is automatically derived from the $name

The factory greatly simplifies the field creation process, since it returns the field object itself and you don't need to assign it to a variable. The fields API supports method chaining (as seen in the example below).

// Create image field with name "customer_photo" and label "Photo"
Carbon_Field::factory('image', 'customer_photo', 'Photo');

// Here the title is automatically set to "Custom Sidebar"
Carbon_Field::factory('choose_sidebar', 'custom_sidebar');

// Method chaining
Carbon_Field::factory('select', 'color')->addOptions(array('red', 'blue', 'green'))->help_text('Pick a color');

All field types originate from a single class named Carbon_Field and inherit the following basic features:

Default Values

You can assign a default value for each field in every container. The default value is used when there is currently no value for the particular field in the database. This is the case for example, when you add a new post, or you add a new theme options field to existing container.

To assign a default value, you use Carbon_Field::factory(...)->set_default_value($default_value)

Required Fields

You can mark any field as required, in which case the user will need to fill it out before submitting. To set a field as required, you use Carbon_Field::factory(...)->set_required(true)

Help Text

Help text is used as a hint to the user, who will use the field. It is usually rendered under the field and contains more information about what it should contain - requirements, examples, links, etc. HTML tags are allowed.

You add help text using Carbon_Field::factory(...)->help_text($text)

Text Field text

The text field is the simplest and most generic field. It renders a text input field.

Carbon_Field::factory('text', 'subtitle')

Textarea textarea

Multiline text input with HTML allowed

Carbon_Field::factory('textarea', 'meta_description')
Setup methods
set_height($height = 170)
Sets the height.
Carbon_Field::factory("textarea", "related_urls", "Related Links")->set_height(250);

Rich text area rich_text

This field renders the built-in WordPress tinyMCE editor.

Carbon_Field::factory("rich_text", "sidenote", "Sidenote Content");

Date date

Renders a date picker using jQuery UI. The value is stored in YYYY-MM-DD format.

Carbon_Field::factory("date", "event_start_date", "Start");

Color color

Renders color picker using Farbtastic. Colors are represented with six hexadecimal digits prefixed with # (e.g. white is #FFFFFF)

Carbon_Field::factory("color", "box_background", "Background Color");

Checkbox checkbox

The checkbox field create a single tick-able option with a label next to it.

Setup methods
set_option_value($value)
Set the value that will be saved when the option is ticked.
NB! When unticked, the value is not saved in the database.
Carbon_Field::factory("checkbox", "show_content", "Show content")
	->set_option_value('yes');

Select select

Creates a select box with pre-defined options.

Setup methods
add_options($options)
Add an associative array with options. NB! If you provide indexed array with no key values, the default indexes (0, 1, 2 ...) of the elements will be used.
The method can be called multiple times, in which case the options between the calls will be appended (instead of overwritten).
Carbon_Field::factory("select", "content_align", "Text alignment")->add_options(array(
	'left' => 'Left',
	'center' => 'Center',
	'right' => 'Right',
));

Radio radio

Similar to the Select field, but instead of in a select box, options are rendered as a set of radio buttons.

Setup methods
add_options($options)
Add an associative array with options. If you provide indexed array with no key values, the default indexes (0, 1, 2 ...) of the elements will be used.
The method can be called multiple times, in which case the options between the calls will be appended (instead of overwritten).
Carbon_Field::factory("radio", "subtitle_styling", "Subtitle text style")->add_options(array(
	'em' => 'Italic',
	'strong' => 'Bold',
	'del' => 'Strike',
));

Set set

The set field creates a list of tick-able options. This field enables to select multiple options. The value is retrieved as array containing the ticked options.

Setup methods
add_options($options)
Add an associative array with options. If you provide indexed array with no key values, the default indexes (0, 1, 2 ...) of the elements will be used.
The method can be called multiple times, in which case the options between the calls will be appended (instead of overwritten).
Carbon_Field::factory("set", "product_features", "Features")->add_options(array(
	'bluetooth' => 'Bluetooth',
	'gps' => 'GPS navigation',
	'nfc' => 'Near field communication',
));

Relationship relationship

This field allows to select multiple posts, pages or custom post types. Useful for creating links between different posts.

Setup methods
set_post_type($post_type)
Set the type of posts to choose from (or array of post types).
set_max($max)
Maximum number of posts to select. Must be greater than 0. Defaults to -1 (no limit).
Carbon_Field::factory("relationship", "publications", "Publications")->set_post_type('publication');
Carbon_Field::factory("relationship", "artworks", "Artworks")->set_post_type(array('painting', 'sculpture'));

File file

Renders a text input with URL and file upload button. The built-in WordPress file handling interface is used.

Carbon_Field::factory("file", "price_list", "Price list (PDF)");

Image image

Renders a text input with URL and image upload button. The built-in WordPress file handling interface is used.

Supported image formats are: jpg, jpeg, gif, png and bmp

Carbon_Field::factory("image", "employee_photo", "Photo");

Attachment attachment

Renders a button to choose file (attachment) from the Media library. Instead of the URL, the field saves the attachment's ID. This allows you to retrieve additional information such as titles, descriptions, captions and different thumbnail sizes.

Carbon_Field::factory("attachment", "relateD_file", "Related File");

Map map

Creates a Google-powered map. The location is represented as longitude and latitude and is saved in three times in the database - one row for the latitude (with name $field_name . '_lat'), one row for the longitude (with name $field_name . '_lng') and one row as a single string ($latitude . ',' . $longitude with name $field_name)

Setup methods
set_position($lat, $lng, $zoom)
Set the default position on the map specified by $lat and $lng and the default zoom level to $zoom (zoom 0 corresponds to a map of the Earth fully zoomed out).
Carbon_Field::factory("map", "company_location", "Location")->help_text('drag and drop the pin on the map to select location');

Map with Address map_with_address

Map field with additional field for address search.

Carbon_Field::factory("map_with_address", "company_location", "Location")

Separator separator

Creates visual separator between adjacent fields. Has aesthetic function only, no data is saved.

Carbon_Field::factory("separator", "style_options", "Style");

Choose Sidebar choose_sidebar

Adds a drop-down field that lists existing sidebars and provides the ability to add new sidebars to the site.

Setup methods
disable_add_new()
Remove the ability to add new sidebars to the site.
set_sidebar_options($sidebar_options)
Set the options used for sidebars created by this field. See register_sidebar for more information (). The default options are:
array(
	'before_widget' => '<li id="%1$s" class="widget %2$s">',
	'after_widget' => '</li>',
	'before_title' => '<h2 class="widgettitle">',
	'after_title' => '</h2>',
)

The example below shows how to create a Choose Sidebar field with custom sidebar options and help text:

Carbon_Field::factory("choose_sidebar", "custom_sidebar", "Sidebar")
	->help_text('Select which sidebar to show in this page, or click "Add New" to create a new one')
	->set_sidebar_options(array(
		'before_widget' => '<div id="%1$s" class="widget %2$s">',
		'after_widget' => '</div>',
		'before_title' => '<h3 class="widgettitle">',
		'after_title' => '<div class="cl">&nbsp;</div></h3>',
	));

Header scripts header_scripts

Applicable to Theme Options container only. Displays a text area, the contents of which will be automatically printed in the <head> of each page. Useful for printing user-defined javascript, as well as styles, meta tags, etc.

Carbon_Field::factory("header_scripts", "header_script");

Footer scripts footer_scripts

Applicable to Theme Options container only. Displays a text area, the contents of which will be automatically printed before the closing </body> of each page (during wp_footer()). Useful for printing Google Analytics code, or user-defined javascript.

Carbon_Field::factory("footer_scripts", "footer_script");

HTML html

Render custom HTML markup

Setup methods
set_html($html)
Set the HTML markup you would like to show to $html.
Carbon_Field::factory("html", "information_text")->set_html('<h2>Lorem ipsum</h2>
<p>Quisque mattis ligula eget placerat volutpat. Praesent tincidunt ultricies tempor.</p>');

Complex Field

Complex fields act as containers to which you can add multiple groups of fields. It is represented as a table, where each row is a field group. The user is able to add infinite rows of each group. This allows to repeat a set of fields multiple times creating customizable and sortable lists. This is useful when creating image galleries, lists of data or advanced content and layout elements.

Carbon_Field::factory('complex', 'slide')->add_fields(array(
	Carbon_Field::factory('text', 'title'),
	Carbon_Field::factory('image', 'photo'),
)),

The example above shows how to make a slide show. We crated a single complex field named slide, to which we attached one group of fields that represents a single slide - title and photo. The user will be able to add multiple rows of title and photo, thus creating a list of slides for the slide show.

A more advanced usage of the complex field is shown below:

Carbon_Field::factory('complex', 'media_item')
	->add_fields('photograph', array(
		Carbon_Field::factory('image', 'image'),
		Carbon_Field::factory('text', 'caption'),
	))
	->add_fields('movie', array(
		Carbon_Field::factory('file', 'video'),
		Carbon_Field::factory('text', 'title'),
		Carbon_Field::factory('text', 'length'),
	)),

Here we have to create a list of media items, lets say for an art exhibition. There are two types of items - photos (defined by an image and a caption) and movies (having a title, length and the video file itself). Since items have different properties, we need to define separate group for each one. Groups also must have a name, by which they will be recognized later - photograph and movie.

As you can see, depending on their usage, complex fields can either contain a single unnamed group or multiple named groups.

Single group

To add a single group of fields you use add_fields($fields), where:

$fields
Numeric array of fields.
add_fields(array(
	Carbon_Field::factory('text', 'name'),
	Carbon_Field::factory('text', 'job_title'),
)),

Multiple groups

To add multiple groups of fields you use add_fields($name, $fields), where:

$name
Unique name of the group.
$fields
Numeric array of fields.
Carbon_Field::factory('complex', 'job')
	->add_fields('driver', array(
		Carbon_Field::factory('text', 'name'),
		Carbon_Field::factory('text', 'drivers_license_id'),
	))
	->add_fields('teacher', array(
		Carbon_Field::factory('image', 'name'),
		Carbon_Field::factory('image', 'years_of_experience'),
	)),

Each call to add_fields($name, $fields) creates a new group and adds it to the complex field.

You can also give each group a label different from their name using add_fields($name, $label, $fields).

All data stored in a complex field is returned as a two-dimensional array with the following format:

array (
	0 => array (
		'_type' => 'photograph',
		'caption' => 'Lorem Ipsum',
		'image' => 'http://example.com/wp-content/uploads/2012/12/Jellyfish.jpg',
	),
	1 => array (
		'_type' => 'movie',
		'length' => '1:56',
		'title' => 'Dolor sit amet',
		'video' => 'http://example.com/wp-content/uploads/2012/12/video_new.mp4',
	),
	2 => array (
		'_type' => 'photograph',
		'caption' => 'Consectetur adipiscing elit',
		'image' => 'http://example.com/wp-content/uploads/2012/12/Koala.jpg',
	),
)

Each item represents the values stored by a single group. The name of the group is stored in element with key _type. When the complex field contains one group only, it's type will be an empty string - "".

Complex field values are retrieved using either carbon_get_post_meta or carbon_get_theme_option (depending on the container it is added to) and passing the string "complex" as $type argument (see Types).

Nested Complex Fields

Complex fields can be nested. The following will define a container that creates multiple slides and allows positioning of multiple text fragments on each slide:

Carbon_Container::factory('custom_fields', 'Slider Data')
	->show_on_post_type('post')
	->add_fields(array(
		Carbon_Field::factory('complex', 'slides')->add_fields(array(
			Carbon_Field::factory('image', 'image'),
			Carbon_Field::factory('complex', 'slide_fragments')->add_fields(array(
				Carbon_Field::factory('text', 'fragment_text'),
				Carbon_Field::factory('select', 'fragment_position')->add_options(array('Top Left', 'Top Right', "Bottom Left", "Bottom Right")),
			))
		)),
	));
					

Values are retrieved as usual using either carbon_get_post_meta or carbon_get_theme_option. The format of the returned data is a multi-dimensional array, as follows:

array (
	0 => array (
		'photo' => 'http://example.com/lorem.jpg',
		'people_on_photo' => array (
			0 => array (
				'name' => 'John',
			),
			1 => array (
				'name' => 'Karen',
			),
		)
	),
	1 => array (
		'photo' => 'http://example.com/ipsum.jpg',
		'people_on_photo' => array (
			0 => array (
				'name' => 'Paul',
			),
			1 => array (
				'name' => 'Kasper',
			),
			2 => array (
				'name' => 'Julie',
			),
		)
	),
)

Limit the number of rows

You can constrain the number of rows in a complex field using the following two functions:

Setup Methods

add_fields
This method is identical to Container add_fields method.
set_layout($layout = table | list)
There are two layouts available for displaying a complex field:
  • Carbon_Field_Complex::LAYOUT_LIST(default) - lists groups as rows and their fields as a columns.
  • Carbon_Field_Complex::LAYOUT_TABLE - lists groups as rows. Each field in the group is displayed in a new line with the label on the left and the user control on the right.
set_min($min)
Minimum number of rows. Must be greater than 0. Defaults to -1 (no limit).
set_max($max)
Maximum number of rows. Must be greater than 0. Defaults to -1 (no limit).
setup_labels($labels)
Allows client code to change labels for this complex field. The following items are accepted:
  • Add Row -- wording of the button for adding new field group.
Example usage:
$employees_labels = array(
	'plural_name'=>'Employees',
	'singular_name'=>'Employee',
);
Carbon_Field::factory('complex', 'employee_data')
	->setup_labels($employees_labels)
	->set_layout(Carbon_Field_Complex::LAYOUT_TABLE)
	->add_fields(array(
		Carbon_Field::factory('text', 'name')->help_text('Name of employee'),
		Carbon_Field::factory('text', 'position')->help_text('Position title'),
		Carbon_Field::factory('image', 'image'),
		Carbon_Field::factory('rich_text', 'description'),
	))

Data Storage

Complex field values are saved in the database in multiple rows - a row per field per group. To be able to distinguish which value for field is, a special format of the keys (meta_key or option_name) is adopted:

{complex_field_name}_{group_name}-{field_name}_{number}, where

complex_field_name
Name of the complex field, as passed to Carbon_Field::factory()
group_name
Name of the group as passed to add_fields(), or "" if only one group is present.
field_name
Name of the field in the group, as passed to Carbon_Field::factory()
number
Identifies the number of the row this value is part of.

NB! When in a Custom Fields container, the name of the complex field is prefixed with an underscore, but the name of the field (field_name) is not. Thus, the key format becomes: _{complex_field_name}_{group_name}-{field_name}_{number}