array(), Contains an array of all elements enqueud to this instance
* 'defaults' => array(), Contains all the default values declared for this instance
* 'user_settings' => array(), Contains the values a user sets
* )
*
* @var array
*/
protected $instances = array();
/**
* Unique hook suffixes
*
* This is simply an array of hooks suffixes
* belonging to each unique id passed along when calling EL()
*
* Used to conditionally load assets
* @see self::set_instance_args()
* @var array
*/
protected $hook_suffixes = array();
/**
* An array of all registered elements
*
* @see self::register_element()
* @var array
*/
protected $elements = array();
/**
* Current instance id
* Evaluates to the value passed onto EL() when accessing this object
*
* @see self::instance()
* @var string
*/
protected $instance_id = false;
/**
* The plugin base url
*
* Used when loading assets. If you decide to customize elementa assets;
* Copy them to another path and pass the path here. This makes it easy to
* upgrade to the next version of elementa
*
* @see self::__construct()
* @var string
*/
public $base_url;
/**
* Callbacks used to retrieve custom form data such as users and posts
*
* You can register your own callback by calling self::register_data_callback()
* @see self::get_data()
* @var string
*/
public $data_callbacks = array();
/**
* Main Elementa Instance.
*
* Ensures only one instance of Elementa is loaded or can be loaded.
*
* @since 0.1.0
* @return Elementa - Main instance.
*/
public static function instance( $instance_id = false ) {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self();
}
// If an id is provided and does not exist in the list of instances; add it
if ( $instance_id !== false && !isset( self::$_instance->instances[$instance_id] )) {
self::$_instance->instances[$instance_id] = array();
self::$_instance->instances[$instance_id]['elements'] = array();
self::$_instance->instances[$instance_id]['defaults'] = array();
}
// Change the value of the current instance id since most functions depend on it
self::$_instance->instance_id = $instance_id;
return self::$_instance;
}
/**
* Elementa Constructor.
*
*/
public function __construct() {
$this->register_core_elements();
$this->register_core_data_callbacks();
$this->base_url = plugins_url( '/', __FILE__ );
add_action( 'admin_enqueue_scripts', array( $this, 'enqeue_scripts'), 5);
}
/**
* Registers core elements
*
*
*/
public function register_core_elements() {
$elements = array(
'title' => array(
'callback' => array( $this, 'default_cb'),
'render_default_markup' => false,
),
'textarea' => array(
'callback' => array( $this, 'default_cb'),
),
'text' => array(
'callback' => array( $this, 'default_cb'),
),
'email' => array(
'callback' => array( $this, 'default_cb'),
),
//Displays a color picker
'color' => array(
'callback' => array( $this, 'default_cb'),
'enque' => array( $this, 'enque_color'),
),
//Displays a date picker
'date' => array(
'callback' => array( $this, 'default_cb'),
'enque' => array( $this, 'enque_date'),
),
'search' => array(
'callback' => array( $this, 'default_cb'),
),
'number' => array(
'callback' => array( $this, 'default_cb'),
),
'password' => array(
'callback' => array( $this, 'default_cb'),
),
//Displays a selectize select box
'select' => array(
'callback' => array( $this, 'default_cb'),
'enque' => array( $this, 'enque_select'),
),
'multiselect' => array(
'callback' => array( $this, 'default_cb'),
'enque' => array( $this, 'enque_select'),
),
'button' => array(
'callback' => array( $this, 'default_cb'),
),
//Displays the save and reset buttons
'save' => array(
'callback' => array( $this, 'default_cb'),
),
//Displays the import and export buttons
'import' => array(
'callback' => array( $this, 'default_cb'),
),
'checkbox' => array(
'callback' => array( $this, 'default_cb'),
),
'radio' => array(
'callback' => array( $this, 'default_cb'),
),
//Displays a yes / no
'switch' => array(
'callback' => array( $this, 'default_cb'),
),
'list_group' => array(
'callback' => array( $this, 'default_cb'),
),
'alert' => array(
'callback' => array( $this, 'default_cb'),
),
'card' => array(
'callback' => array( $this, 'default_cb'),
),
//Offers full flexibility
'raw' => array(
'callback' => array( $this, 'default_cb'),
),
);
$this->register_multiple_elements( $elements );
}
/**
* A catch all callback that renders default elements
*
*/
public function default_cb( $args ) {
if ( isset ( $args['type'] ) )
include "elements/{$args['type']}.php";
}
/**
* Enques styles for select elements
*
*/
public function enque_select() {
wp_enqueue_script( 'selectize', $this->base_url . '/assets/js/selectize.min.js', array( 'jquery' ), '4.0.3', false );
wp_enqueue_style( 'selectize-bootstrap3', $this->base_url . '/assets/css/selectize.bootstrap3.css' );
}
/**
* Enques styles for date elements
*
*/
public function enque_date() {
wp_enqueue_script( 'wp-datepicker', $this->base_url . '/assets/js/datepicker.min.js', array( 'jquery' ), '0.4.0', true );
wp_enqueue_style( 'wp-datepicker', $this->base_url . '/assets/css/datepicker.css' );
}
/**
* Enques styles for color elements
*
*/
public function enque_color() {
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
}
/**
* Registers multiple elements at once
*
*/
public function register_multiple_elements( $args = array() ) {
if( !is_array( $args ) )
return;
foreach ( $args as $element => $options ) {
$this->register_element( $element, $options );
}
}
/**
* Registers a new element
*
* An element needs a render callback at minimum
*/
public function register_element( $element_type = false, $args = array() ) {
if( $element_type !== false )
$this->elements[$element_type] = $args;
}
/**
* Returns a list of all registered elements
*
*/
public function get_registered_elements( ) {
return array_keys( $this->elements );
}
/**
* Updates an existing element
*
*/
public function update_element( $element_type = false, $key = false, $value = false ) {
if( $element_type !== false && isset( $this->elements[$element_type] ) )
$this->elements[$element_type][$key] = $value;
}
/**
* @Deprecated.
*
* @see self::queue_element
* @since 0.1.0
* @access public
*/
public function queue_control( $element_id = false, $args = array() ) {
$this->queue_element( $element_id, $args );
}
/**
* Queues an element for rendering
*
* @since 0.1.1
* @access public
*/
public function queue_element( $element_id = false, $args = array() ) {
if( $element_id !== false && $this->instance_id !== false) {
$args['id'] = $element_id;
$this->instances[$this->instance_id]['elements'][] = array(
'id' => $element_id,
'args' => $args
);
//Set it in a separate array to allow easy access; no need to loop the above array
if ( isset ( $args['default'] )) {
$this->instances[$this->instance_id]['defaults'][$element_id] = $args['default'];
}
}
}
/**
* Renders a registered element
*
* @since 0.1.0
* @access public
*/
public function render_element( $args = array() ) {
//If no element is provided or it lacks a render callback return early
if ( !isset ( $args['type'] ) || !isset ( $this->elements[ $args['type'] ][ 'callback' ] ) )
return;
if ( !is_array ( $args ) )
$args = array();
//Normalize the user args
$args = $this->clean_args( $args );
$element = $this->elements[ $args['type'] ];
//Optionally render a default markup
$default_markup = ( !isset ( $element['render_default_markup'] ) || $element['render_default_markup'] == true );
if( $default_markup ) {
$this->render_wrapper_open( $args );
}
//Call the element's render function
call_user_func( $this->elements[ $args['type'] ][ 'callback' ], $args );
if( $default_markup ) {
$this->render_wrapper_end( $args );
}
}
/**
* Outputs the settings page
*
* @since 0.1.0
* @access public
*/
public function render_wrapper_open( $args ) {
$is_full_field = ( isset( $args['full_width'] ) && $args['full_width'] == true );
$content_class = 'col s12';
$class = 'elementa-row';
if( isset ( $args['section'] ) && $args['section'] )
$class .= ' wp-section-wrapper-' . sanitize_html_class( $args['section'] );
echo "
";
//If a title is set; reduce the content class
if ( isset( $args['title'] ) ) {
$content_class = 'col s12 m8';
$title_class = 'col s12 m4';
if ( $is_full_field ) {
$title_class = 'col s12';
$content_class = 'col s12';
}
$title = '
' . $args['title']. '';
if ( isset( $args['subtitle'] ) ) {
$title .= "
{$args['subtitle']}";
}
echo "
$title
";
}
echo "
";
}
/**
* Outputs the closing wrapper around rendered elements
*
* @since 0.1.0
* @access public
*/
public function render_wrapper_end( $args ) {
echo '
';
}
/**
* Normalizes element render args
*
* @since 0.1.0
* @access public
*/
private function clean_args( $args ) {
//Data attibutes
if(! isset( $args['custom_attributes'] )) {
$args['custom_attributes'] = array();
}
$args['_custom_attributes'] = '';
foreach ( $args['custom_attributes'] as $attr => $value ) {
$attr = esc_attr( $attr );
$value = esc_attr( $value );
$args['_custom_attributes'] .= " $attr='$value'";
}
//Default
if(! isset( $args['default'] )) {
$args['default'] = '';
}
//Description
if(! isset( $args['description'] )) {
$args['description'] = '';
}
//Placeholder
if(! isset( $args['placeholder'] )) {
$args['placeholder'] = '';
}
//Option details for select etc
if(! isset( $args['options'] )) {
$args['options'] = array();
}
//Data args
if(! isset( $args['data_args'] )) {
$args['data_args'] = array();
}
//Data, replaces the options field
if( isset( $args['data'] ) ) {
$data = $this->get_data( $args['data'], $args['data_args'] );
if(! empty( $data ) ) {
$args['options'] = $data;
}
}
//Class
if(! isset( $args['class'] )) {
$args['class'] = '';
}
//Value == current value
$args['_value'] = $args['_current'] = $this->get_option( $args['id'] );
//Id attribute
$args['__id'] = esc_attr( $args['id'] );
return $args;
}
/**
* Returns the data provided by a given data callback
*/
public function get_data( $type = '', $args = array()) {
if( empty ( $type ) || !is_string( $type ) || !is_array( $args ))
return array();
$type = strtolower( $type );
$callbacks = $this->data_callbacks;
if( !isset ( $this->data_callbacks[ $type ] ) )
return array();
return call_user_func( $this->data_callbacks[$type], $args );
}
/**
* Registers data callbacks
*
* Take a look at self::register_core_data_callbacks to see how it works.
* If the data callback exists; it will be overwritten. Make sure that
* The callback returns an array of name=>label pairs
*
* @var $data string Required. The type of data e.g post
* @var $callback array Required. The arguments used to fetch your data callback
*/
public function register_data_callback( $data, $callback = array() ) {
if (is_string( $data ) ) {
$this->data_callbacks[$data] = $callback;
}
}
/**
* Returns an array of all registered data callbacks
*/
public function get_registered_data_callbacks( ) {
return $this->data_callbacks;
}
/**
* Registers multiple data callbacks
*/
public function register_core_data_callbacks() {
//Categories
$this->register_data_callback( 'category', array( $this, 'get_categories' ));
$this->register_data_callback( 'categories', array( $this, 'get_categories' ));
//Tags
$this->register_data_callback( 'tag', array( $this, 'get_tags' ));
$this->register_data_callback( 'tags', array( $this, 'get_tags' ));
$this->register_data_callback( 'post_tag', array( $this, 'get_tags' ));
//Taxonomies
$this->register_data_callback( 'taxonomy', array( $this, 'get_taxonomies' ));
$this->register_data_callback( 'taxonomies', array( $this, 'get_taxonomies' ));
//Posts
$this->register_data_callback( 'post', array( $this, 'get_posts' ));
$this->register_data_callback( 'posts', array( $this, 'get_posts' ));
//Menus
$this->register_data_callback( 'menus', array( $this, 'get_menus' ));
$this->register_data_callback( 'menu', array( $this, 'get_menus' ));
//Pages
$this->register_data_callback( 'page', array( $this, 'get_pages' ));
$this->register_data_callback( 'pages', array( $this, 'get_pages' ));
//Post types
$this->register_data_callback( 'post_types', array( $this, 'get_post_types' ));
$this->register_data_callback( 'post_type', array( $this, 'get_post_types' ));
//Post statuses
$this->register_data_callback( 'post_statuses', array( $this, 'get_post_statuses' ));
$this->register_data_callback( 'post_status', array( $this, 'get_post_statuses' ));
//Users
$this->register_data_callback( 'user', array( $this, 'get_users' ));
$this->register_data_callback( 'users', array( $this, 'get_users' ));
//Roles
$this->register_data_callback( 'roles', array( $this, 'get_roles' ));
$this->register_data_callback( 'role', array( $this, 'get_roles' ));
$this->register_data_callback( 'user_roles', array( $this, 'get_roles' ));
$this->register_data_callback( 'user_role', array( $this, 'get_roles' ));
//Capabilities
$this->register_data_callback( 'capabilities', array( $this, 'get_capabilities' ));
$this->register_data_callback( 'capability', array( $this, 'get_capabilities' ));
$this->register_data_callback( 'user_capabilities', array( $this, 'get_capabilities' ));
$this->register_data_callback( 'user_capability', array( $this, 'get_capabilities' ));
//Countries
$this->register_data_callback( 'country', array( $this, 'get_countries' ));
$this->register_data_callback( 'countries', array( $this, 'get_countries' ));
}
/**
* A helper function to modify custom data
*
* It extracts the key and value fields from the data
*/
protected function modify_custom_data( $data, $key, $value ) {
$return = array();
$data = $data;
foreach ( $data as $single ) {
if ( is_array( $single ) ) {
$return[ $single[$key] ] = $single[$value];
}
if ( is_object( $single ) ) {
$return[ $single->$key ] = $single->$value;
}
}
return $return;
}
/**
* Returns an array of post categories
*/
public function get_categories( $args ) {
return $this->modify_custom_data( get_categories( $args ), 'term_id', 'name' );
}
/**
* Returns an array of post tags
*/
public function get_tags( $args ) {
return $this->modify_custom_data( get_tags( $args ), 'term_id', 'name' );
}
/**
* Returns an array of taxonomies
*/
public function get_taxonomies( $args ) {
return $this->modify_custom_data( get_taxonomies( $args, false ), 'name', 'label' );
}
/**
* Returns an array of posts
*/
public function get_posts( $args ) {
return $this->modify_custom_data( get_posts( $args ), 'ID', 'post_title' );
}
/**
* Returns an array of menus
*/
public function get_menus( $args ) {
return $this->modify_custom_data( wp_get_nav_menus( $args ), 'term_id', 'name' );
}
/**
* Returns an array of pages
*/
public function get_pages( $args ) {
return $this->modify_custom_data( get_pages( $args ), 'ID', 'post_title' );
}
/**
* Returns an array of post types
*/
public function get_post_types( $args ) {
return $this->modify_custom_data( get_post_types( $args, false ), 'name', 'label' );
}
/**
* Returns an array of post statuses
*/
public function get_post_statuses( $args ) {
global $wp_post_statuses;
$return = array();
foreach($wp_post_statuses as $status => $details ) {
$return[ $status ] = $details->label;
}
return $return;
}
/**
* Returns an array of countries
*/
public function get_countries( $args ) {
return require( 'data/countries.php' );
}
/**
* Returns an array of users
*/
public function get_users( $args ) {
return $this->modify_custom_data( get_users( $args, false ), 'ID', 'display_name' );
}
/**
* Returns an array of user roles
*/
public function get_roles( $args ) {
global $wp_roles;
return $wp_roles->role_names;
}
/**
* Returns an array of all user capabilities or capabilities for the given user type
*/
public function get_capabilities( $args ) {
global $wp_roles;
$capabilities = array();
if( !isset( $args['user_type'] ) ) {
foreach ( $wp_roles->roles as $role) {
foreach ( $role['capabilities'] as $cap => $bool ) {
if( $bool == true )
$capabilities[$cap] = $cap;
}
}
} else {
if ( isset ($wp_roles->roles[$args['user_type']]) ){
foreach ( $wp_roles->roles[$args['user_type']]['capabilities'] as $cap => $bool ) {
if( $bool == true )
$capabilities[$cap] = $cap;
}
}
}
return array_map( array( $this, 'titalize' ) , $capabilities);
}
/**
* Converts a string to readable form
*/
public function titalize( $string ) {
return ucfirst( str_replace( '_', ' ', $string ) );
}
/**
* Sets the rendering template
*/
public function set_template( $template = false ) {
if ( $this->instance_id !== false && $template !== false )
$this->instances[$this->instance_id]['template'] = $template;
}
/**
* Outputs the settings page
*
* @since 0.1.0
* @access public
*/
public function render() {
if( $this->instance_id === false )
return;
//Save settings if data has been posted
if ( ! empty( $_POST ) )
$this->save();
$template = 'template.php';
if ( isset ( $this->instances[ $this->instance_id ]['template'] ) )
$template = $this->instances[ $this->instance_id ]['template'];
$elements = array();
if ( isset ( $this->instances[ $this->instance_id ]['elements'] ) )
$elements = $this->instances[ $this->instance_id ]['elements'];
require_once ( $template );
}
/**
* Saves submitted data
*
* This is method is called when the render function is called to ensure that data
* is saved only when the right page is requested.
*
* @since 0.1.0
* @access protected
*/
protected function save() {
//Will forever evaluate to true.
if( $this->instance_id === false )
return;
//Make sure to always include an elementa nonce field in your templates unless you save your own settings
if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'elementa' ) )
return;
//Data is being imported.
if ( isset( $_POST['elementa-imported-data'] ) ){
return $this->_save( (array) json_decode( wp_unslash( $_POST['elementa-imported-data'] ) ) );
}
//Data is reset.
if ( isset( $_POST['wpe-reset'] ) ){
return $this->_save( $this->get_defaults( $this->instance_id ) ) ;
}
return $this->_save( $_POST );
}
/**
* Saves dava to the db
*
*
* @since 0.1.1
* @access protected
*/
protected function _save( $data ) {
$data = wp_unslash( $data );
if ( is_array ( $data )) {
unset( $data['_wp_http_referer'] );
unset( $data['_wpnonce'] );
unset( $data['wpe-import'] );
unset( $data['wpe-export'] );
update_option( $this->instance_id, $data );
//Update cached data with our new values
$this->instances[$this->instance_id]['user_settings'] = $data;
}
}
/**
* Returns all default values for the current instance
*
* @since 0.1.0
* @access public
*/
public function get_defaults( $instance_id ) {
//Pull default data
if( isset ( $this->instances[$instance_id]['defaults'] )){
return $this->instances[$instance_id]['defaults'];
}
return array();
}
/**
* Returns default values for the current instance
*
* @since 0.1.1
* @access public
*/
public function get_options() {
if( $this->instance_id === false )
return array();
//Maybe pull data from the db?
if (! isset( $this->instances[$this->instance_id]['user_settings'] ) ) {
$this->instances[$this->instance_id]['user_settings'] = get_option( $this->instance_id, false );
}
//data not yet saved to the database
if (! $this->instances[$this->instance_id]['user_settings'] ) {
return $this->get_defaults( $this->instance_id );
}
return $this->instances[$this->instance_id]['user_settings'];
}
/**
* gets a user defined option
*/
public function get_option( $option = false ) {
if( $option === false OR $this->instance_id === false )
return null;
//Maybe pull data from the db?
if (! isset( $this->instances[$this->instance_id]['user_settings'] ) ) {
$this->instances[$this->instance_id]['user_settings'] = get_option( $this->instance_id, false );
}
$user_settings = $this->instances[$this->instance_id]['user_settings'];
//If data saved, return it
if ( $user_settings && isset( $user_settings[$option] ) ) {
return $user_settings[$option];
}
//If data not saved, maybe return default
$defaults = $this->get_defaults( $this->instance_id );
if (! $user_settings && isset( $defaults[$option] ) ) {
return $defaults[$option];
}
//Nothing to return
return null;
}
/** Set special arguments for the instance
* @since 0.1.0
*/
public function set_instance_args( $args ) {
if ( $this->instance_id === false OR !is_array( $args ) )
return;
// Used to conditionally load data
if ( isset( $args['hook_suffix'] ) && $args['hook_suffix'] ) {
$this->hook_suffixes[ $args['hook_suffix'] ] = array();
if ( isset( $args['element_types'] ) && is_array( $args['element_types'] ) ) {
$this->hook_suffixes[ $args['hook_suffix'] ] = $args['element_types'];
}
}
}
/**
* Adds stylesheets to the queue
* @since 0.1.0
*/
public function enqeue_scripts( $hook_suffix ) {
//Only enque styles on the pages that we render
if (! isset( $this->hook_suffixes[ $hook_suffix ] ) )
return;
//Main css file
wp_enqueue_style( 'elementa', $this->base_url . 'assets/css/elementa.css');
//Finally enque additional styles needed by the current hook_suffix
foreach ( $this->hook_suffixes[ $hook_suffix ] as $element ) {
if ( isset ( $this->elements[$element]['enque'] ) )
call_user_func( $this->elements[$element]['enque'] );
}
//Main js is enqueued last since it depends on the above scripts
wp_enqueue_script( 'elementa', $this->base_url . 'assets/js/elementa.js', array( 'jquery', 'underscore'), '0.1.1', true );
$elementa_translate = array(
'emptyData' => __( 'Please provide the import data.', 'elementa' ),
'emptyJson' => __( 'You provided an empty object so nothing was imported.', 'elementa' ),
'badFormat' => __( 'The data you provided is not it the right format. Sorry.', 'elementa' ),
'importing' => __( 'Importing data...', 'elementa' ),
'finalising' => __( 'Almost done.', 'elementa' ),
'finished' => __( 'Done. Please wait for the page to reload.', 'elementa' ),
);
wp_localize_script( 'elementa', 'elementa_translate', $elementa_translate );
}
/**
* Plucks a given property from all instances
*
* @since 0.1.0
* @access public
*/
public function element_pluck( $property ) {
$return = array();
if( $this->instance_id !== false && isset( $this->instances[$this->instance_id]['elements'] )) {
foreach( $this->instances[$this->instance_id]['elements'] as $element ) {
if ( isset( $element['args'][$property] ) && !empty( $element['args'][$property] ) )
$return[] = $element['args'][$property];
}
}
return $return;
}
}
//Goodbye World!!!!!!!