'', 'url' => '', 'views' => array( 'section' => 'views/section.php', 'component-tab-vertical' => 'views/component-tab-vertical.php', 'component-tab-horizontal' => 'views/component-tab-horizontal.php', 'component-toggle' => 'views/component-toggle.php', 'component-accordion' => 'views/component-accordion.php', 'component-repeater' => 'views/component-repeater.php', 'settings' => 'views/settings.php', 'control' => 'views/control.php', 'settings-children-title' => 'views/settings-children-title.php', 'tab-children-title' => 'views/tab-children-title.php', 'toggle-children-title' => 'views/toggle-children-title.php', 'form' => 'views/form.php', 'html' => 'views/html.php', ), 'views_args' => array( 'parent' => '', 'type' => '', 'view' => '', 'view_wrapping' => true, 'html' => '', 'scroll' => false, 'title' => '', 'description' => '', 'condition' => array(), ), ); /** * A reference to an instance of this class. * * @since 1.0.0 * @access private * @var object */ private static $instance = null; /** * UI element instance. * * @since 1.0.0 * @access public * @var object */ public $controls = null; /** * The structure of the interface elements. * * @since 1.0.0 * @access private * @var array */ private $structure = array(); /** * Dependencies array * @var array */ private $deps = array( 'css' => array(), 'js' => array( 'jquery' ), ); /** * Cherry_Interface_Builder constructor. * * @since 1.0.0 * @access public * @return void */ public function __construct( array $args = array() ) { $this->path = $args['path']; $this->url = $args['url']; $this->args = array_merge( $this->args, $args ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); require trailingslashit( $this->path ) . 'inc/class-cx-controls-manager.php'; $this->controls = new CX_Controls_Manager( $this->path, $this->url ); } /** * Register element type section. * * @since 1.0.0 * @access public * @param array $args Options section. * @return void */ public function register_section( array $args = array() ) { $this->add_new_element( $args, 'section' ); } /** * Register element type component. * * @since 1.0.0 * @access public * @param array $args Options component. * @return void */ public function register_component( array $args = array() ) { $this->add_new_element( $args, 'component' ); } /** * Register element type settings. * * @since 1.0.0 * @access public * @param array $args Options settings. * @return void */ public function register_settings( array $args = array() ) { $this->add_new_element( $args, 'settings' ); } /** * Register element type control. * * @since 1.0.0 * @access public * @param array $args Options control. * @return void */ public function register_control( array $args = array() ) { $this->add_new_element( $args, 'control' ); } /** * Register element type form. * * @since 1.0.0 * @access public * @param array $args Options form. * @return void */ public function register_form( array $args = array() ) { $this->add_new_element( $args, 'form' ); } /** * Register element type html. * * @since 1.0.0 * @access public * @param array $args Options control. * @return void */ public function register_html( array $args = array() ) { $this->add_new_element( $args, 'html' ); } /** * This function adds a new element to the structure. * * @since 1.0.0 * @access protected * @param array $args Options new element. * @param string $type Type new element. * @return void */ protected function add_new_element( array $args = array(), $type = 'section' ) { if ( ! isset( $args[0] ) && ! is_array( current( $args ) ) ) { if ( 'control' !== $type && 'component' !== $type ) { $args['type'] = $type; } if ( ! isset( $args['name'] ) && isset( $args['id'] ) ) { $args['name'] = $args['id']; } if ( 'control' === $type ) { $instance = $this->controls->register_control( $args['type'], $args ); $args['instance'] = $instance; $this->add_dependencies( $instance ); } if ( array_key_exists( 'conditions', $args ) ) { $this->conditions[ $args['id'] ] = $args['conditions']; } if ( array_key_exists( 'value', $args ) ) { $this->fields_value[ $args['id'] ] = $args['value']; } $this->structure[ $args['id'] ] = $args; } else { foreach ( $args as $key => $value ) { if ( 'control' !== $type && 'component' !== $type ) { $value['type'] = $type; } if ( ! isset( $value['id'] ) ) { $value['id'] = $key; } if ( ! isset( $value['name'] ) ) { $value['name'] = $key; } if ( 'control' === $type ) { $instance = $this->controls->register_control( $value['type'], $value ); $value['instance'] = $instance; $this->add_dependencies( $instance ); } if ( array_key_exists( 'conditions', $value ) ) { $this->conditions[ $key ] = $value['conditions']; } if ( array_key_exists( 'value', $value ) ) { $this->fields_value[ $key ] = $value['value']; } $this->structure[ $key ] = $value; } } } /** * Add control dependencies to global builder deps * * @param [type] $control [description] */ protected function add_dependencies( $control ) { if ( ! $control instanceof CX_Controls_Base ) { return; } $this->deps['js'] = array_merge( $this->deps['js'], $control->get_script_depends() ); $this->deps['css'] = array_merge( $this->deps['css'], $control->get_style_depends() ); } /** * Sorts the elements of the structure, adding child items to the parent. * * @since 1.0.0 * @access protected * @param array $structure The original structure of the elements. * @param string $parent_key The key of the parent element. * @return array */ protected function sort_structure( array $structure = array(), $parent_key = null ) { $new_array = array(); foreach ( $structure as $key => $value ) { if ( ( null === $parent_key && ! isset( $value['parent'] ) ) || null === $parent_key && ! isset( $structure[ $value['parent'] ] ) || ( isset( $value['parent'] ) && $value['parent'] === $parent_key ) ) { $new_array[ $key ] = $value; $children = $this->sort_structure( $structure, $key ); if ( ! empty( $children ) ) { $new_array[ $key ]['children'] = $children; } } } return $new_array; } /** * Reset structure array. * Call this method only after render. * * @since 1.0.1 * @return void */ public function reset_structure() { $this->structure = array(); } /** * Get view for interface elements. * * @since 1.0.0 * @access protected * @param string $type View type. * @param array $args Input data. * @return string */ protected function get_view( $type = 'control', array $args = array() ) { if ( empty( $args['view'] ) ) { $path = ( array_key_exists( $type, $this->args['views'] ) ) ? $this->args['views'][ $type ] : $this->args['views']['control']; $path = is_array( $path ) ? $path[0] : $path; $path = file_exists( $path ) ? $path : $this->path . $path; } else { $path = $args['view']; } ob_start(); include $path; return ob_get_clean(); } /** * Render HTML elements. * * @since 1.0.0 * @access public * @param bool $echo Input data. * @param array $args The original structure of the elements. * @return string */ public function render( $echo = true, array $args = array() ) { if ( empty( $args ) ) { $args = $this->structure; } if ( empty( $args ) ) { return false; } $sorted_structure = $this->sort_structure( $args ); $output = $this->build( $sorted_structure ); $output = str_replace( array( "\r\n", "\r", "\n", "\t" ), '', $output ); $this->reset_structure(); return $this->output_method( $output, $echo ); } /** * Render HTML elements. * * @since 1.0.0 * @access protected * @param array $args Input data. * @return string */ protected function build( array $args = array() ) { $output = ''; $views = $this->args['views']; foreach ( $args as $key => $value ) { $value = wp_parse_args( $value, $this->args['views_args'] ); $value['class'] = isset( $value['class'] ) ? $value['class'] . ' ' : ''; $value['class'] .= $value['id'] . ' '; if ( $value['scroll'] ) { $value['class'] .= 'cx-scroll '; } $type = array_key_exists( $value['type'], $views ) ? $value['type'] : 'field'; $has_child = isset( $value['children'] ) && is_array( $value['children'] ) && ! empty( $value['children'] ); switch ( $type ) { case 'component-tab-vertical': case 'component-tab-horizontal': if ( $has_child ) { $value['tabs'] = ''; foreach ( $value['children'] as $key_children => $value_children ) { $value['tabs'] .= $this->get_view( 'tab-children-title', $value_children ); unset( $value['children'][ $key_children ]['title'] ); } } break; case 'component-toggle': case 'component-accordion': if ( $has_child ) { foreach ( $value['children'] as $key_children => $value_children ) { $value['children'][ $key_children ]['title_in_view'] = $this->get_view( 'toggle-children-title', $value_children ); } } break; case 'settings': if ( isset( $value['title'] ) && $value['title'] ) { $value['title'] = isset( $value['title_in_view'] ) ? $value['title_in_view'] : $this->get_view( 'settings-children-title', $value ); } break; case 'html': $value['children'] = $value['html']; break; case 'form': $value['accept-charset'] = isset( $value['accept-charset'] ) ? $value['accept-charset'] : 'utf-8'; $value['action'] = isset( $value['action'] ) ? $value['action'] : '' ; $value['autocomplete'] = isset( $value['autocomplete'] ) ? $value['autocomplete'] : 'on'; $value['enctype'] = isset( $value['enctype'] ) ? $value['enctype'] : 'application/x-www-form-urlencoded'; $value['method'] = isset( $value['method'] ) ? $value['method'] : 'post'; $value['novalidate'] = ( isset( $value['novalidate'] ) && $value['novalidate'] ) ? 'novalidate' : ''; $value['target'] = isset( $value['target'] ) ? $value['target'] : ''; break; case 'field': $ui_args = $value; $ui_args['class'] = isset( $ui_args['child_class'] ) ? $ui_args['child_class'] : '' ; $control = isset( $ui_args['instance'] ) ? $ui_args['instance'] : false; if ( $control ) { $value['children'] = $control->render(); } else { $value['children'] = 'Control not found'; } break; } if ( $has_child ) { $value['children'] = $this->build( $value['children'] ); } $output .= ( $value['view_wrapping'] ) ? $this->get_view( $type, $value ) : $value['children']; } return $output; } /** * Output HTML. * * @since 1.0.0 * @access protected * @param string $output Output HTML. * @param boolean $echo Output type. * @return string */ protected function output_method( $output = '', $echo = true ) { if ( ! filter_var( $echo, FILTER_VALIDATE_BOOLEAN ) ) { return $output; } else { echo self::kses( $output ); } } /** * Enqueue javascript and stylesheet interface builder. * * @since 4.0.0 * @access public * @return void */ public function enqueue_assets() { $suffix = ''; if ( defined( 'SCRIPT_DEBUG' ) && false === SCRIPT_DEBUG ) { $suffix = '.min'; } $js_deps = array_unique( $this->deps['js'] ); $css_deps = array_unique( $this->deps['css'] ); wp_enqueue_script( 'cx-interface-builder', $this->url . 'assets/js/cx-interface-builder' . $suffix . '.js', $js_deps, $this->version, true ); wp_localize_script( 'cx-interface-builder', 'cxInterfaceBuilder', array( 'conditions' => $this->conditions, 'fields' => $this->fields_value, ) ); wp_enqueue_style( 'cx-interface-builder', $this->url . 'assets/css/cx-interface-builder.css', $css_deps, $this->version, 'all' ); } public static function kses( $string ) { return apply_filters( 'cx-interface-builder/kses', $string ); } } }