File: /home/mmickelson/martyknows.com/wp-content/plugins/simple-lightbox/includes/class.fields.php
<?php
/**
* Collection of default system-wide fields
* @package Simple Lightbox
* @subpackage Fields
* @author Archetyped
*
*/
class SLB_Fields extends SLB_Field_Collection {
public $item_type = 'SLB_Field_Type';
/**
* Placeholder handlers
* @var array
*/
public $placholders = null;
/* Constructor */
function __construct() {
parent::__construct( 'fields' );
}
protected function _hooks() {
parent::_hooks();
// Init fields
add_action( 'init', $this->m( 'register_types' ) );
// Init placeholders
add_action( 'init', $this->m( 'register_placeholders' ) );
}
/* Field Types */
/**
* Initialize fields
*/
function register_types() {
/* Field Types */
// Base
$base = new SLB_Field_Type( 'base' );
$base->set_description( __( 'Default Element', 'simple-lightbox' ) );
$base->set_property( 'tag', 'span' );
$base->set_property( 'class', '', 'attr' );
$base->set_layout( 'form_attr', '{tag} name="{field_name}" id="{field_id}" {properties ref_base="root" group="attr"}' );
$base->set_layout( 'form', '<{form_attr ref_base="layout"} />' );
$base->set_layout( 'label', '<label for="{field_id}">{label}</label>' );
$base->set_layout( 'display', '{data context="display"}' );
$this->add( $base );
// Base closed
$base_closed = new SLB_Field_Type( 'base_closed' );
$base_closed->set_parent( 'base' );
$base_closed->set_description( __( 'Default Element (Closed Tag)', 'simple-lightbox' ) );
$base_closed->set_layout( 'form_start', '<{tag} id="{field_id}" name="{field_name}" {properties ref_base="root" group="attr"}>' );
$base_closed->set_layout( 'form_end', '</{tag}>' );
$base_closed->set_layout( 'form', '{form_start ref_base="layout"}{data}{form_end ref_base="layout"}' );
$this->add( $base_closed );
// Input
$input = new SLB_Field_Type( 'input', 'base' );
$input->set_description( __( 'Default Input Element', 'simple-lightbox' ) );
$input->set_property( 'tag', 'input' );
$input->set_property( 'type', 'text', 'attr' );
$input->set_property( 'value', '{data}', 'attr' );
$this->add( $input );
// Text input
$text = new SLB_Field_Type( 'text', 'input' );
$text->set_description( __( 'Text Box', 'simple-lightbox' ) );
$text->set_property( 'size', 15, 'attr' );
$text->set_property( 'label' );
$text->set_layout( 'form', '{label ref_base="layout"} {inherit}' );
$this->add( $text );
// Checkbox
$cb = new SLB_Field_Type( 'checkbox', 'input' );
$cb->set_property( 'type', 'checkbox' );
$cb->set_property( 'value', null );
$cb->set_layout( 'form_attr', '{inherit} {checked}' );
$cb->set_layout( 'form', '{label ref_base="layout"} <{form_attr ref_base="layout"} />' );
$this->add( $cb );
// Textarea
$ta = new SLB_Field_Type( 'textarea', 'base_closed' );
$ta->set_property( 'tag', 'textarea' );
$ta->set_property( 'cols', 40, 'attr' );
$ta->set_property( 'rows', 3, 'attr' );
$this->add( $ta );
// Rich Text
$rt = new SLB_Field_Type( 'richtext', 'textarea' );
$rt->set_property( 'class', 'theEditor {inherit}' );
$rt->set_layout( 'form', '<div class="rt_container">{inherit}</div>' );
$rt->add_action( 'admin_print_footer_scripts', 'wp_tiny_mce', 25 );
$this->add( $rt );
// Hidden
$hidden = new SLB_Field_Type( 'hidden' );
$hidden->set_parent( 'input' );
$hidden->set_description( __( 'Hidden Field', 'simple-lightbox' ) );
$hidden->set_property( 'type', 'hidden' );
$this->add( $hidden );
// Select
$select = new SLB_Field_Type( 'select', 'base_closed' );
$select->set_description( __( 'Select tag', 'simple-lightbox' ) );
$select->set_property( 'tag', 'select' );
$select->set_property( 'tag_option', 'option' );
$select->set_property( 'options', array() );
$select->set_layout( 'form', '{label ref_base="layout"} {form_start ref_base="layout"}{option_loop ref_base="layout"}{form_end ref_base="layout"}' );
$select->set_layout( 'option_loop', '{loop data="properties.options" layout="option" layout_data="option_data"}' );
$select->set_layout( 'option', '<{tag_option} value="{data_ext id="option_value" context="attr"}">{data_ext id="option_text" context="text"}</{tag_option}>' );
$select->set_layout( 'option_data', '<{tag_option} value="{data_ext id="option_value" context="attr"}" selected="selected">{data_ext id="option_text" context="text"}</{tag_option}>' );
$this->add( $select );
// Span
$span = new SLB_Field_Type( 'span', 'base_closed' );
$span->set_description( __( 'Inline wrapper', 'simple-lightbox' ) );
$span->set_property( 'tag', 'span' );
$span->set_property( 'value', 'Hello there!' );
$this->add( $span );
// Enable plugins to modify (add, remove, etc.) field types
$this->util->do_action_ref_array( 'register_fields', array( $this ), false );
// Signal completion of field registration
$this->util->do_action_ref_array( 'fields_registered', array( $this ), false );
}
/* Placeholder handlers */
function register_placeholders() {
// Default placeholder handlers
$this->register_placeholder( 'all', $this->m( 'process_placeholder_default' ), 11 );
$this->register_placeholder( 'field_id', $this->m( 'process_placeholder_id' ) );
$this->register_placeholder( 'field_name', $this->m( 'process_placeholder_name' ) );
$this->register_placeholder( 'data', $this->m( 'process_placeholder_data' ) );
$this->register_placeholder( 'data_ext', $this->m( 'process_placeholder_data_ext' ) );
$this->register_placeholder( 'loop', $this->m( 'process_placeholder_loop' ) );
$this->register_placeholder( 'label', $this->m( 'process_placeholder_label' ) );
$this->register_placeholder( 'checked', $this->m( 'process_placeholder_checked' ) );
// Allow other code to register placeholders
$this->util->do_action_ref_array( 'register_field_placeholders', array( $this ), false );
// Signal completion of field placeholder registration
$this->util->do_action_ref_array( 'field_placeholders_registered', array( $this ), false );
}
/**
* Register a function to handle a placeholder
* Multiple handlers may be registered for a single placeholder
* Adds filter hook to WP for handling specified placeholder
* Placeholders are in layouts and are replaced with data at runtime
* @uses add_filter()
* @param string $placeholder Name of placeholder to add handler for (Using 'all' will set the function as a handler for all placeholders
* @param callback $callback Function to set as a handler
* @param int $priority (optional) Priority of handler
* @return void
*/
function register_placeholder( $placeholder, $callback, $priority = 10 ) {
if ( 'all' === $placeholder ) {
$placeholder = '';
} else {
$placeholder = '_' . $placeholder;
}
$hook = $this->add_prefix( 'process_placeholder' . $placeholder );
add_filter( $hook, $callback, $priority, 5 );
}
/**
* Handles default placeholder processing.
*
* Processes placeholders that have not been processed by another handler.
*
* @param string $output Value to be used in place of placeholder. Should be empty.
* @param SLB_Field $item Field containing placeholder.
* @param array $placeholder Current placeholder.
* @see SLB_Field::parse_layout for structure of `$placeholder` array.
* @param string $layout Layout to build.
* @param array $data Extended data for item.
* @return string Value to use in place of current placeholder.
*/
function process_placeholder_default( $output, $item, $placeholder, $layout, $data ) {
// Validate parameters before processing.
if (
! empty( $output )
|| ( ! $item instanceof SLB_Field_Type )
|| ! is_array( $placeholder )
) {
return $output;
}
// Build path to replacement data.
$output = $item->get_member_value( $placeholder );
// Check if value is group (properties, etc.)
// All groups must have additional attributes (beyond reserved attributes) that define how items in group are used
if (
is_array( $output )
&& ! empty( $placeholder['attributes'] )
&& is_array( $placeholder['attributes'] )
&& 'properties' === $placeholder['tag']
) {
// Targeted property is an array.
// Placeholder contains additional options on how property is to be used.
// Find items matching criteria in $output
// Check for group criteria
$prop_group = $item->get_group( $placeholder['attributes']['group'] );
if ( ! empty( $prop_group ) ) {
/* Process group */
$group_out = array();
// Iterate through properties in group and build string.
foreach ( array_keys( $prop_group ) as $prop_key ) {
$prop_val = $item->get_property( $prop_key );
if ( is_null( $prop_val ) ) {
continue;
}
// Process placeholders.
$prop_val = $item->process_placeholders( $prop_val, $layout, $data );
// Add property to attribute string output.
$group_out[] = esc_attr( $prop_key ) . '="' . esc_attr( $prop_val ) . '"';
}
$output = implode( ' ', $group_out );
}
} elseif ( is_object( $output ) && ( $output instanceof $item->base_class ) ) {
/* Targeted property is actually a nested item */
// Set caller to current item
$output->set_caller( $item );
// Build layout for nested element
$output = $output->build_layout( $layout );
}
return $output;
}
/**
* Renders field ID formatted for a form field's `id` attribute.
*
* ID is formatted to be unique identifier for form field.
* Example: `options_field_id`.
* Registered as handler for `{field_id}` placeholder.
*
* @param string $output Placeholder's rendered value.
* @param SLB_Field $item Field containing placeholder.
* @param array &$placeholder Placeholder being processed.
* @param string $layout Name of layout being built.
* @param array $data Additional data for current field.
* @return string Field's ID (formatted for a form field's `id` attribute).
*/
function process_placeholder_id( $output, $item, &$placeholder, $layout, $data ) {
// Get attributes
$args = wp_parse_args( $placeholder['attributes'], array( 'format' => 'attr_id' ) );
$output = $item->get_id( $args );
// Set default placeholder context.
if ( ! isset( $placeholder['attributes']['context'] ) ) {
$placeholder['attributes']['context'] = 'attr';
}
return $output;
}
/**
* Renders field ID formatted for a form field's `name` attribute.
*
* ID is formatted to be part of an associative array for processing form submission.
* Example: `options[field_id]`.
* Registered as handler for `{field_name}` placeholder.
*
* @param string $output Placeholder's rendered value.
* @param SLB_Field $item Field containing placeholder.
* @param array &$placeholder Placeholder being processed.
* @param string $layout Name of layout being built.
* @param array $data Additional data for current field.
* @return string Field's ID (formatted for a form field's `name` attribute).
*/
function process_placeholder_name( $output, $item, &$placeholder, $layout, $data ) {
// Get attributes
$args = wp_parse_args( $placeholder['attributes'], array( 'format' => 'attr_name' ) );
$output = $item->get_id( $args );
// Set default placeholder context.
if ( ! isset( $placeholder['attributes']['context'] ) ) {
$placeholder['attributes']['context'] = 'attr';
}
return $output;
}
/**
* Build item label
* @see SLB_Fields::process_placeholder_default for parameter descriptions
* @return string Field label
*/
function process_placeholder_label( $output, $item, $placeholder, $layout, $data ) {
// Check if item has label property (e.g. sub-elements)
$out = $item->get_property( 'label' );
// If property not set, use item title
if ( empty( $out ) ) {
$out = $item->get_title();
}
return $out;
}
/**
* Retrieve data for item
* @see SLB_Field_Type::process_placeholder_default for parameter descriptions
* @return string Placeholder output
*/
function process_placeholder_data( $output, $item, $placeholder, $layout ) {
$opts = $placeholder['attributes'];
// Strip context from data retrieval options (Formatting handled upstream).
if ( is_array( $opts ) ) {
unset( $opts['context'] );
}
// Get data
$out = $item->get_data( $opts );
// Get specific member in value (e.g. value from a specific item element).
if (
is_array( $out )
&& is_array( $opts )
&& isset( $opts['element'] )
&& isset( $out[ $opts['element'] ] )
) {
$out = $out[ $opts['element'] ];
}
// Return data
return $out;
}
/**
* Set checked attribute on item
* Evaluates item's data to see if item should be checked or not
* @see SLB_Fields::process_placeholder_default for parameter descriptions
* @return string Appropriate checkbox attribute
*/
function process_placeholder_checked( $output, $item, $placeholder, $layout, $data ) {
$out = '';
$c = $item->get_container();
$d = ( isset( $c->data[ $item->get_id() ] ) ) ? $c->data[ $item->get_id() ] : null;
$item->set_property( 'd', true );
if ( $item->get_data() ) {
$out = 'checked="checked"';
}
$item->set_property( 'd', false );
return $out;
}
/**
* Loops over data to build item output
* Options:
* data - Dot-delimited path in item that contains data to loop through
* layout - Name of layout to use for each data item in loop
* layout_data - Name of layout to use for data item that matches previously-saved item data
* @see SLB_Field_Type::process_placeholder_default for parameter descriptions
* @return string Placeholder output
*/
function process_placeholder_loop( $output, $item, $placeholder, $layout, $data ) {
// Setup loop options
$attr_defaults = array(
'layout' => '',
'layout_data' => null,
'data' => '',
);
$attr = wp_parse_args( $placeholder['attributes'], $attr_defaults );
if ( is_null( $attr['layout_data'] ) ) {
$attr['layout_data'] =& $attr['layout'];
}
// Get data for loop
$path = explode( '.', $attr['data'] );
$loop_data = $item->get_member_value( $path );
// Check if data is callback
if ( is_callable( $loop_data ) ) {
$loop_data = call_user_func( $loop_data );
}
// Get item data
$data = $item->get_data();
// Iterate over data and build output
$out = array();
if ( is_array( $loop_data ) && ! empty( $loop_data ) ) {
foreach ( $loop_data as $value => $label ) {
// Load appropriate layout based on item value
$layout = ( ( 0 === $data && $value === $data ) xor $data === $value ) ? $attr['layout_data'] : $attr['layout'];
// Stop processing if no valid layout is returned
if ( empty( $layout ) ) {
continue;
}
// Prep extended item data
$data_ext = array(
'option_value' => $value,
'option_text' => $label,
);
$out[] = $item->build_layout( $layout, $data_ext );
}
}
// Return output
return implode( $out );
}
/**
* Returns specified value from extended data array for item.
*
* @param string $output Value to be used in place of placeholder.
* @param SLB_Field $item Field containing placeholder.
* @param array $placeholder Current placeholder.
* @see SLB_Field::parse_layout for structure of `$placeholder` array.
* @param string $layout Name of layout being built.
* @param array $data Extended data for item.
*
* @return string Processed value.
*/
function process_placeholder_data_ext( $output, SLB_Field $item, array $placeholder, $layout, array $data ) {
$key = ( isset( $placeholder['attributes']['id'] ) ) ? $placeholder['attributes']['id'] : false;
if ( ! ! $key && isset( $data[ $key ] ) && is_scalar( $data[ $key ] ) ) {
$output = strval( $data[ $key ] );
}
return $output;
}
/* Build */
/**
* Outputs items in a group.
*
* @param string $group ID of Group to output.
* @return string Group output.
* @todo Make compatible with parent::build_group()
*/
function build_group( $group ) {
$out = array();
/**
* Renders group output as a string.
*
* @uses $out Array containing group output.
* @return string Group output.
*/
$render_output = function() use ( $out ) {
// Combine output.
return implode( '', $out );
};
// Stop if group does not exist.
if ( ! $this->group_exists( $group ) ) {
return $render_output();
}
// Classnames.
$cls = (object) [
'multi' => 'multi_field',
'single' => 'single_field',
'elements' => 'has_elements',
'group_desc' => $this->add_prefix( 'group_description' ),
'group_wrap' => $this->add_prefix( 'attributes_wrap' ),
'item_wrap' => $this->add_prefix( 'attribute_wrap' ),
];
// Templates.
$tpl = (object) [
'container_start' => '<div class="%s">',
'container_end' => '</div>',
'item_start' => '<div id="%1$s_wrap" class="%2$s">',
'item_end' => '</div>',
'text_block' => '<p class="%1$s">%2$s</p>',
];
// Process group.
$group = $this->get_group( $group );
$group_items = ( count( $group->items ) > 1 ) ? $cls->multi : $cls->single;
$fs = array_keys( $group->items );
$f =& $group->items[ $fs[0] ];
$els = $f->get_member_value( 'elements', '', null );
if ( ! empty( $els ) ) {
$group_items .= '_' . $cls->elements;
}
// Wrap items with container element.
$classname = array( $cls->group_wrap, $group_items );
$out[] = sprintf( $tpl->container_start, implode( ' ', $classname ) );
// Clear temp variables.
unset( $fs, $f, $els, $classname );
// Build layout for each item in group
foreach ( array_keys( $group->items ) as $item_id ) {
// Init item.
$item =& $group->items[ $item_id ];
$item->set_caller( $this );
// Start item output.
$id = $this->add_prefix( 'field_' . $item->get_id() );
$out[] = sprintf( $tpl->item_start, $id, $cls->item_wrap );
// Build item layout.
$out[] = $item->build_layout();
// End item output.
$out[] = $tpl->item_end;
// Cleanup.
$item->clear_caller();
unset( $item, $id );
}
// Close items container.
$out[] = $tpl->container_end;
// Add description if exists
if ( ! empty( $group->description ) ) {
$out[] = sprintf( $tpl->text_block, $cls->group_desc, $group->description );
}
// Render and return output.
return $render_output();
}
}