Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
wp-content
/
plugins
/
bb-plugin
/
classes
:
class-fl-builder-model.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * Builder data handling class that deals * with all database operations. * * @since 1.0 */ final class FLBuilderModel { /** * An array that contains the sizes for columns * in each row layout. * * @since 1.0 * @var array $row_layouts */ static public $row_layouts = array( '1-col' => array(100), '2-cols' => array(50, 50), '3-cols' => array(33.33, 33.33, 33.33), '4-cols' => array(25, 25, 25, 25), '5-cols' => array(20, 20, 20, 20, 20), '6-cols' => array(16.65, 16.65, 16.65, 16.65, 16.65, 16.65), 'left-sidebar' => array(33.33, 66.66), 'right-sidebar' => array(66.66, 33.33), 'left-right-sidebar' => array(25, 50, 25) ); /** * An array that contains data for each registered settings form. * * @since 1.0 * @var array $settings_forms */ static public $settings_forms = array(); /** * An array used to cache default values for settings forms. * * @since 1.0 * @var array $settings_form_defaults */ static public $settings_form_defaults = array(); /** * An array that instances for each registered module. * * @since 1.0 * @var array $modules */ static public $modules = array(); /** * Cached global settings. * * @access private * @var array $global_settings */ private static $global_settings; /** * The last node id that was generated by the builder. * This is saved to ensure the next node id is unique. * * @since 1.0 * @access private * @var string $last_generated_node_id */ static private $last_generated_node_id = null; /** * Cached post data from either the $_POST array * or from the fl_builder_data post variable. * * @since 1.0 * @access private * @var array $post_data */ static private $post_data = null; /** * An array of cached published layout data by post_id. * * @since 1.0 * @access private * @var array $published_layout_data */ static private $published_layout_data = array(); /** * An array of cached draft layout data by post_id. * * @since 1.0 * @access private * @var array $draft_layout_data */ static private $draft_layout_data = array(); /** * An array of paths to template data files. * * @since 1.8 * @access private * @var array $templates */ static private $templates = array(); /** * An array of cached post IDs for node templates. * * @since 1.7.6 * @access private * @var array $node_template_post_ids */ static private $node_template_post_ids = array(); /** * An array of cached types for user and node templates. * * @since 1.7.9 * @access private * @var array $node_template_types */ static private $node_template_types = array(); /** * Initialize hooks. * * @since 1.8 * @return void */ static public function init() { /* Admin AJAX */ add_action('wp_ajax_fl_builder_disable', __CLASS__ . '::disable'); add_action('wp_ajax_fl_builder_duplicate_wpml_layout', __CLASS__ . '::duplicate_wpml_layout'); /* Actions */ add_action('init', __CLASS__ . '::load_settings', 1); add_action('init', __CLASS__ . '::load_modules', 2); add_action('before_delete_post', __CLASS__ . '::delete_post'); add_action('save_post', __CLASS__ . '::save_revision'); add_action('save_post', __CLASS__ . '::set_node_template_default_type', 10, 3); add_action('wp_restore_post_revision', __CLASS__ . '::restore_revision', 10, 2); /* Filters */ add_filter('heartbeat_received', __CLASS__ . '::lock_post', 10, 2); /* Core Templates */ self::register_templates( FL_BUILDER_DIR . 'data/templates.dat' ); } /** * Returns a builder edit URL for a post. * * @since 1.0 * @param int $post_id The post id to get an edit url for. * @return string */ static public function get_edit_url( $post_id = false ) { if ( false === $post_id ) { global $post; } else { $post = get_post( $post_id ); } return set_url_scheme( add_query_arg( 'fl_builder', '', get_permalink( $post->ID ) ) ); } /** * Returns the URL to upgrade the builder to the premium version. * Can be overridden by theme developers to use their affiliate * link using the fl_builder_upgrade_url filter. * * @since 1.0 * @param array $params An array of key/value params to add to the query string. * @return string */ static public function get_upgrade_url( $params = array() ) { return apply_filters( 'fl_builder_upgrade_url', self::get_store_url( '', $params ) ); } /** * Returns a URL that points to the Beaver Builder store. * * @since 1.8.6 * @param string $path A URL path to append to the store URL. * @param array $params An array of key/value params to add to the query string. * @return string */ static public function get_store_url( $path = '', $params = array() ) { $url = trailingslashit( FL_BUILDER_STORE_URL . $path ) . '?' . http_build_query( $params, '', '&' ); return apply_filters( 'fl_builder_store_url', $url, $path ); } /** * Returns an array of post data from either $_POST['fl_builder_data'] * or $_POST if that is not set. * * @since 1.0 * @return array */ static public function get_post_data() { if(!self::$post_data) { self::$post_data = array(); if(isset($_POST['fl_builder_data'])) { // Decode settings if our ModSecurity fix is enabled. if ( isset( $_POST['fl_builder_data']['settings'] ) ) { $_POST['fl_builder_data']['settings'] = FLBuilderUtils::modsec_fix_decode( $_POST['fl_builder_data']['settings'] ); } if ( isset( $_POST['fl_builder_data']['node_settings'] ) ) { $_POST['fl_builder_data']['node_settings'] = FLBuilderUtils::modsec_fix_decode( $_POST['fl_builder_data']['node_settings'] ); } $data = FLBuilderUtils::json_decode_deep( wp_unslash( $_POST['fl_builder_data'] ) ); foreach($data as $key => $val) { self::$post_data[$key] = $val; } } else if(isset($_POST)) { foreach($_POST as $key => $val) { self::$post_data[$key] = $val; } } } return self::$post_data; } /** * Update a value in the $post_data array. * * @since 1.0 * @param string $key The post data key. * @param mixed $value The value to update. * @return void */ static public function update_post_data($key, $value) { $post_data = self::get_post_data(); $post_data[$key] = $value; self::$post_data = $post_data; } /** * Return an array of post types that the builder * is enabled to work with. * * @since 1.0 * @return array */ static public function get_post_types() { $value = self::get_admin_settings_option( '_fl_builder_post_types', true ); if ( ! $value ) { $value = array( 'page', 'fl-builder-template' ); } else { $value[] = 'fl-builder-template'; } return apply_filters( 'fl_builder_post_types', $value ); } /** * Return an array of post ids that should have their * builder assets loaded globally. * * @since 1.0 * @return array */ static public function get_global_posts() { return apply_filters('fl_builder_global_posts', array()); } /** * Returns the post id for the current post that * is being displayed or worked on. * * @since 1.0 * @since 1.5.9 Trying to use the global $wp_the_query instead of $post to get the post id. * @return int|bool The post id or false. */ static public function get_post_id() { global $wp_the_query; global $post; $post_data = self::get_post_data(); // Get a post ID sent in an AJAX request. if ( isset( $post_data['post_id'] ) ) { return $post_data['post_id']; } // Get a post ID from the main query. else if ( in_the_loop() && is_main_query() && isset( $wp_the_query->post ) ) { return $wp_the_query->post->ID; } // Get a post ID in a query outside of the main loop. else if ( isset( $post ) ) { return $post->ID; } // No post ID found. else { return false; } } /** * Returns the post object for the current post that * is being worked on. * * @since 1.6.3 * @return object */ static public function get_post() { return get_post( self::get_post_id() ); } /** * Checks to see if the site has SSL enabled or not. * * @since 1.0 * @return bool */ static public function is_ssl() { if ( is_ssl() ) { return true; } else if ( 0 === stripos( get_option( 'siteurl' ), 'https://' ) ) { return true; } else if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] ) { return true; } return false; } /** * Checks to see if the builder can be enabled for * the current post in the main query. * * @since 1.0 * @return bool */ static public function is_post_editable() { global $wp_the_query; if ( is_singular() && isset( $wp_the_query->post ) ) { $post = $wp_the_query->post; $post_types = self::get_post_types(); $user_can = current_user_can( 'edit_post', $post->ID ); if ( in_array( $post->post_type, $post_types ) && $user_can ) { return true; } } return false; } /** * Called by the heartbeat API. Lock the current post * so only the current user can edit it. * * @since 1.0 * @return void */ static public function lock_post($response, $data) { if(isset($data['fl_builder_post_lock'])) { require_once ABSPATH . 'wp-admin/includes/post.php'; wp_set_post_lock($data['fl_builder_post_lock']['post_id']); } } /** * Checks to see if the builder layout is enabled * for the current post. * * @since 1.0 * @return bool */ static public function is_builder_enabled() { if(!is_admin() && post_password_required()) { return false; } else if(self::is_builder_active()) { return true; } else { $post_types = self::get_post_types(); $post = get_post(self::get_post_id()); if($post && in_array($post->post_type, $post_types)) { return get_post_meta($post->ID, '_fl_builder_enabled', true); } } return false; } /** * Checks to see if the builder UI is active for * the current post in the main query. * * @since 1.0 * @return bool */ static public function is_builder_active() { if ( self::is_post_editable() && ! is_admin() && ! post_password_required() ) { $post_data = self::get_post_data(); if ( isset( $_GET['fl_builder'] ) ) { return true; } else if ( isset( $post_data['fl_builder'] ) ) { return true; } } return false; } /** * Checks to see if this is the first time * a user has launched the builder. * * @since 1.4.9 * @return bool */ static public function is_new_user() { if ( self::is_builder_active() ) { $current_user = wp_get_current_user(); $launched = get_user_meta( $current_user->ID, '_fl_builder_launched', true ); if ( empty( $launched ) ) { update_user_meta( $current_user->ID, '_fl_builder_launched', 1 ); return true; } } return false; } /** * Gets the status to use for working with nodes in * the database. Returns draft if the builder is active, * otherwise it returns published. * * @since 1.0 * @return string */ static public function get_node_status() { return self::is_builder_active() ? 'draft' : 'published'; } /** * Enable the builder layout for the current post. * * @since 1.0 * @return void */ static public function enable() { update_post_meta(self::get_post_id(), '_fl_builder_enabled', true); } /** * Disable the builder layout for the current post. * * @since 1.0 * @return void */ static public function disable() { update_post_meta(self::get_post_id(), '_fl_builder_enabled', false); } /** * Enable the builder editor for the main post in the query. * * @since 1.0 * @return void */ static public function enable_editing() { global $wp_the_query; if ( self::is_post_editable() ) { $post = $wp_the_query->post; $published = self::get_layout_data( 'published' ); $draft = self::get_layout_data( 'draft' ); // Migrate existing post content to the builder? if ( empty( $published ) && empty( $draft ) && ! empty( $post->post_content ) ) { $row = self::add_row(); $cols = self::get_nodes( 'column' ); $col = array_shift( $cols ); $settings = self::get_module_defaults( 'rich-text' ); $settings->text = wpautop( $post->post_content ); self::add_module( 'rich-text', $settings, $col->node ); } // Create a new draft? else if ( empty( $draft ) ) { self::update_layout_data( $published, 'draft', $post->ID ); self::update_layout_settings( self::get_layout_settings( 'published' ), 'draft', $post->ID ); } // Delete old draft asset cache. self::delete_asset_cache(); // Lock the post. require_once ABSPATH . 'wp-admin/includes/post.php'; wp_set_post_lock( $post->ID ); } } /** * Returns an array of paths for the upload directory * of the current site. * * @since 1.0 * @return array */ static public function get_upload_dir() { $wp_info = wp_upload_dir(); $dir_name = basename( FL_BUILDER_DIR ); // We use bb-plugin for the lite version as well. if ( $dir_name == 'beaver-builder-lite-version' ) { $dir_name = 'bb-plugin'; } // SSL workaround. if ( self::is_ssl() ) { $wp_info['baseurl'] = str_ireplace( 'http://', 'https://', $wp_info['baseurl'] ); } // Build the paths. $dir_info = array( 'path' => $wp_info['basedir'] . '/' . $dir_name . '/', 'url' => $wp_info['baseurl'] . '/' . $dir_name . '/' ); // Create the upload dir if it doesn't exist. if ( ! file_exists( $dir_info['path'] ) ) { // Create the directory. mkdir( $dir_info['path'] ); // Add an index file for security. file_put_contents( $dir_info['path'] . 'index.html', '' ); } return apply_filters( 'fl_builder_get_upload_dir', $dir_info ); } /** * Returns an array of paths for the cache directory * of the current site. * * @since 1.0 * @param string $name The name of the cache directory to get paths for. * @return array */ static public function get_cache_dir( $name = 'cache' ) { $upload_info = self::get_upload_dir(); $allowed = array( 'cache', 'icons' ); // Make sure the dir name is allowed. if ( ! in_array( $name, $allowed ) ) { return false; } // Build the paths. $dir_info = array( 'path' => $upload_info['path'] . $name . '/', 'url' => $upload_info['url'] . $name . '/' ); // Create the cache dir if it doesn't exist. if( ! file_exists( $dir_info['path'] ) ) { // Create the directory. mkdir( $dir_info['path'] ); // Add an index file for security. file_put_contents( $dir_info['path'] . 'index.html', '' ); } return apply_filters( 'fl_builder_get_cache_dir', $dir_info ); } /** * Returns the version number to be applied to the query string * of a CSS or JS asset. If the builder is active a random hash * is returned to prevent caching, otherwise a hash of the post * update time is returned. * * @since 1.0 * @return string */ static public function get_asset_version() { $post_id = self::get_post_id(); $active = self::is_builder_active(); if($active) { return md5(uniqid()); } else { return md5(get_post_modified_time('U', false, $post_id)); } } /** * Returns an array of paths for the CSS and JS assets * of the current post. * * @since 1.0 * @return array */ static public function get_asset_info() { $post_data = self::get_post_data(); $post_id = self::get_post_id(); $cache_dir = self::get_cache_dir(); if(isset($post_data['node_preview'])) { $suffix = '-layout-preview'; } else if(self::is_builder_active()) { $suffix = '-layout-draft'; } else { $suffix = '-layout'; } $info = array( 'css' => $cache_dir['path'] . $post_id . $suffix . '.css', 'css_url' => $cache_dir['url'] . $post_id . $suffix . '.css', 'css_partial' => $cache_dir['path'] . $post_id . $suffix . '-partial.css', 'css_partial_url' => $cache_dir['url'] . $post_id . $suffix . '-partial.css', 'js' => $cache_dir['path'] . $post_id . $suffix . '.js', 'js_url' => $cache_dir['url'] . $post_id . $suffix . '.js', 'js_partial' => $cache_dir['path'] . $post_id . $suffix . '-partial.js', 'js_partial_url' => $cache_dir['url'] . $post_id . $suffix . '-partial.js' ); return $info; } /** * Deletes either the preview, draft or live CSS and/or JS asset cache * for the current post based on the data returned from get_asset_info. * Both the CSS and JS asset cache will be delete if a type is not specified. * * @since 1.0 * @param string $type The type of cache to delete. Either css or js. * @return void */ static public function delete_asset_cache( $type = false ) { $info = self::get_asset_info(); $types = $type ? array( $type ) : array( 'css', 'css_partial', 'js', 'js_partial' ); foreach ( $types as $type ) { if ( isset( $info[ $type ] ) && file_exists( $info[ $type ] ) ) { unlink( $info[ $type ] ); } } } /** * Deletes preview, draft and live CSS/JS asset cache for the current * post. If a post ID is supplied, the asset cache will be deleted for * that post instead. * * @since 1.0 * @param int $post_id * @return void */ static public function delete_all_asset_cache( $post_id = false ) { $post_id = $post_id ? $post_id : self::get_post_id(); $cache_dir = self::get_cache_dir(); if ( $post_id ) { $paths = array( $cache_dir['path'] . $post_id . '-layout.css', $cache_dir['path'] . $post_id . '-layout-draft.css', $cache_dir['path'] . $post_id . '-layout-preview.css', $cache_dir['path'] . $post_id . '-layout-partial.css', $cache_dir['path'] . $post_id . '-layout-draft-partial.css', $cache_dir['path'] . $post_id . '-layout-preview-partial.css', $cache_dir['path'] . $post_id . '-layout.js', $cache_dir['path'] . $post_id . '-layout-draft.js', $cache_dir['path'] . $post_id . '-layout-preview.js', $cache_dir['path'] . $post_id . '-layout-partial.js', $cache_dir['path'] . $post_id . '-layout-draft-partial.js', $cache_dir['path'] . $post_id . '-layout-preview-partial.js' ); foreach ( $paths as $path ) { if ( file_exists( $path ) ) { unlink( $path ); } } } } /** * Deletes the asset cache for all posts that contain the node * template with the supplied post ID. * * @since 1.6.3 * @param int $post_id * @return void */ static public function delete_node_template_asset_cache( $post_id = false ) { $posts = self::get_posts_with_global_node_template( $post_id ); if ( ! empty( $posts ) ) { foreach( $posts as $post ) { self::delete_all_asset_cache( $post->ID ); } } } /** * Deletes preview, draft and live CSS/JS asset cache for all posts. * * @since 1.6.3 * @return void */ static public function delete_asset_cache_for_all_posts() { $cache_dir = self::get_cache_dir(); $css = glob( $cache_dir['path'] . '*.css' ); $js = glob( $cache_dir['path'] . '*.js' ); if ( is_array( $css ) ) { array_map( 'unlink', $css ); } if ( is_array( $js ) ) { array_map( 'unlink', $js ); } } /** * Generates a unique id for a builder node such as a * row, column or module. * * @since 1.0 * @return string */ static public function generate_node_id() { $node_id = uniqid(); if($node_id == self::$last_generated_node_id) { return self::generate_node_id(); } self::$last_generated_node_id = $node_id; return $node_id; } /** * Generates new node ids for an array of nodes. * * @since 1.0 * @param array $data An array of node data. * @return array */ static public function generate_new_node_ids($data) { $map = array(); $nodes = array(); // Map the new node ids to the old. foreach($data as $node_id => $node) { $map[$node_id] = self::generate_node_id(); } // Replace the old node ids. foreach($data as $node_id => $node) { $nodes[$map[$node_id]] = $node; $nodes[$map[$node_id]]->node = $map[$node_id]; if(!empty($node->parent) && isset($map[$node->parent])) { $nodes[$map[$node_id]]->parent = $map[$node->parent]; } } return $nodes; } /** * Returns a single node. * * @since 1.0 * @param string|object $node_id Either a node id or node object. * @param string $status The node status. Either draft or published. * @return object */ static public function get_node( $node_id = null, $status = null ) { if ( is_object( $node_id ) ) { $node = $node_id; } else { $data = self::get_layout_data( $status ); $node = isset( $data[ $node_id ] ) ? $data[ $node_id ] : null; } if ( $node && ! empty( $node->settings ) ) { $node->settings = self::get_node_settings( $node ); } return $node; } /** * Returns an array of nodes. * * @since 1.0 * @param string $type The type of nodes to return. * @param string|object $parent_id Either the parent node id or parent node object. * @param string $status The node status. Either draft or published. * @return array */ static public function get_nodes( $type = null, $parent_id = null, $status = null ) { $parent = is_object( $parent_id ) ? $parent_id : self::get_node( $parent_id ); $nodes = array(); // Get the layout data. if ( ! $parent ) { $data = self::get_layout_data( $status ); } else { $data = self::get_child_nodes( $parent, $status ); } // Return all nodes? if ( ! $type ) { $nodes = $data; } // Return nodes of a certain type. else { foreach ( $data as $node_id => $node ) { if ( $node->type == $type ) { $nodes[ $node_id ] = $node; } } } // Sort the nodes by position. uasort( $nodes, array( 'FLBuilderModel', 'order_nodes' ) ); // Merge default settings. foreach ( $nodes as $node_id => $node ) { if ( ! empty( $node->settings ) ) { $nodes[ $node_id ]->settings = self::get_node_settings( $nodes[ $node_id ] ); } } // Return the nodes. return $nodes; } /** * Returns the direct parent object for a single node. * * @since 1.9 * @param string|object $node_id Either a node id or node object. * @param string $status The node status. Either draft or published. * @return object */ static public function get_node_parent( $node_id = null, $status = null ) { $parent = null; if ( is_object( $node_id ) ) { $node = $node_id; } else { $node = self::get_node( $node_id, $status ); } if ( $node ) { $template_post_id = self::is_node_global( $node ); $post_id = $template_post_id ? $template_post_id : self::get_post_id(); $data = self::get_layout_data( $status, $post_id ); if ( isset( $data[ $node->parent ] ) ) { return $data[ $node->parent ]; } } return $parent; } /** * Returns a node's parent node of the specified type. * * @since 1.8.3 * @param string|object $node The node ID. Can also be a node object. * @param string $type The type of parent to return. Either "column", "column-group" or "row". * @return object The parent node. */ static public function get_node_parent_by_type( $node, $type = '' ) { // Get node object if node ID set if ( ! is_object( $node ) ) { $node = self::get_node( $node ); } // Return early if no node object found or node has no parent if ( empty( $node ) || empty( $node->parent ) ) { return; } // Helper array of parent types and their categories for each node type $parent_types = array( 'module' => array( 'type' => 'column', 'category' => 'columns', ), 'column' => array( 'type' => 'column-group', 'category' => 'groups', ), 'column-group' => array( 'type' => 'row', 'category' => 'rows', ), ); // Helper array of node type hierarchies $hierarchy = array( 'module' => 10, 'column' => 20, 'column-group' => 30, 'row' => 40, ); // Set immediate parent type of the node when: // - type is not of allowed types // - type is the same as node type // - type is lower in hierarchy than the node type if ( ! in_array( $type, array_keys( $hierarchy ) ) || $type == $node->type || $hierarchy[ $parent_types[ $node->type ]['type'] ] > $hierarchy[ $type ] ) { $type = $parent_types[ $node->type ]['type']; } // Get all layout nodes, categorized $nodes = array_filter( self::get_categorized_nodes() ); // Null out the output initially $output = ''; // Parse layout nodes to get the correct output if ( ! empty( $nodes ) ) { while ( empty( $output ) ) { if ( ! empty( $node->parent ) && isset( $nodes[ $parent_types[ $node->type ]['category'] ] ) ) { $break_while = true; foreach ( $nodes[ $parent_types[ $node->type ]['category'] ] as $parent ) { if ( $parent->node == $node->parent ) { $break_while = false; if ( $parent_types[ $node->type ]['type'] == $type ) { // We have got the type we wanted! Set the output and break from while and foreach loops. $output = $parent; break; // From foreach } // We now need node parents to crawl the tree $node = $parent; break; // From foreach } } // If we get this far without changing $break_while, something is wrong if ( $break_while ) { break; // From while } } else { break; // From while } } } return $output; } /** * Returns an array of child nodes for a parent. * * @since 1.0 * @param string|object $parent_id Either the parent node id or parent node object. * @param string $status The node status. Either draft or published. * @return array */ static public function get_child_nodes( $parent_id, $status = null ) { $parent = is_object( $parent_id ) ? $parent_id : self::get_node( $parent_id ); $template_post_id = self::is_node_global( $parent ); $status = $template_post_id && ! self::is_post_node_template() ? 'published' : $status; $data = self::get_layout_data( $status, $template_post_id ); $nodes = array(); foreach ( $data as $node_id => $node ) { if ( $node->parent == $parent->node || ( $template_post_id && $parent->template_node_id == $node->parent ) ) { $nodes[ $node_id ] = $node; } } return $nodes; } /** * Returns all child nodes and children of those children * for a single node. * * @since 1.6.3 * @param string $parent_id The parent node id. * @return array */ static public function get_nested_nodes( $parent_id ) { $children = self::get_child_nodes( $parent_id ); foreach ( $children as $child_id => $child ) { $grand_children = self::get_child_nodes( $child_id ); if ( count( $grand_children ) > 0 ) { $children = array_merge( $children, $grand_children ); foreach ( $grand_children as $grand_child_id => $grand_child ) { $nested = self::get_nested_nodes( $grand_child_id ); if ( count( $nested ) > 0 ) { $children = array_merge( $children, $nested ); } } } } return $children; } /** * Returns an array of all nodes for a layout, categorized by type. * * @since 1.6.3 * @return array */ static public function get_categorized_nodes() { $nodes = array( 'rows' => array(), 'groups' => array(), 'columns' => array(), 'modules' => array(), ); if ( self::is_post_user_template( 'module' ) ) { $nodes['modules'] = self::get_all_modules(); } else { $rows = self::get_nodes( 'row' ); foreach ( $rows as $row ) { $nodes['rows'][ $row->node ] = $row; $groups = self::get_nodes( 'column-group', $row ); foreach ( $groups as $group ) { $nodes['groups'][ $group->node ] = $group; $cols = self::get_nodes( 'column', $group ); foreach ( $cols as $col ) { $nodes['columns'][ $col->node ] = $col; $col_children = self::get_nodes( null, $col ); foreach ( $col_children as $col_child ) { if ( 'module' == $col_child->type ) { $module = self::get_module( $col_child ); if ( $module ) { $nodes['modules'][ $col_child->node ] = $module; } } else if ( 'column-group' == $col_child->type ) { $nodes['groups'][ $col_child->node ] = $col_child; $group_cols = self::get_nodes( 'column', $col_child ); foreach ( $group_cols as $group_col ) { $nodes['columns'][ $group_col->node ] = $group_col; $modules = self::get_modules( $group_col ); foreach ( $modules as $module ) { $nodes['modules'][ $module->node ] = $module; } } } } } } } } return $nodes; } /** * Returns node settings that are merged with the * default or preview settings. * * @since 1.0 * @param object $node A node object. * @return object */ static public function get_node_settings($node) { $post_data = self::get_post_data(); // Get the node settings for a node template's root node? if ( self::is_node_template_root( $node ) && ! self::is_post_node_template() ) { $template_post_id = self::get_node_template_post_id( $node->template_id ); $template_data = self::get_layout_data( 'published', $template_post_id ); $template_node = $template_data[ $node->template_node_id ]; $node->settings = $template_node->settings; } // Get either the preview settings or saved node settings merged with the defaults. if(isset($post_data['node_preview']) && isset($post_data['node_id']) && $post_data['node_id'] == $node->node) { if(!isset($post_data['node_preview_processed_settings'])) { $settings = $post_data['node_preview']; $settings = (object)array_merge((array)$node->settings, (array)$settings); $settings = self::process_node_settings($node, $settings); self::update_post_data('node_preview_processed_settings', $settings); } else { $settings = $post_data['node_preview_processed_settings']; } } else { $defaults = self::get_node_defaults($node); $settings = (object)array_merge((array)$defaults, (array)$node->settings); if ( 'module' == $node->type ) { $settings = self::merge_nested_module_defaults( $node->settings->type, $settings ); } } return $settings; } /** * Returns node settings that have been processed with * specific logic based on the type of node. * * @since 1.0 * @param object $node A node object. * @param object $new_settings The new node settings. * @return object */ static public function process_node_settings($node, $new_settings) { if($node->type == 'row') { $new_settings = self::process_row_settings($node, $new_settings); } if($node->type == 'column') { $new_settings = self::process_col_settings($node, $new_settings); } if($node->type == 'module') { $new_settings = self::process_module_settings($node, $new_settings); } return $new_settings; } /** * Returns the default settings for a node. * * @since 1.0 * @param object $node A node object. * @return object */ static public function get_node_defaults($node) { $defaults = array(); if($node->type == 'row') { $defaults = self::get_row_defaults(); } else if($node->type == 'column') { $defaults = self::get_col_defaults(); } else if($node->type == 'module') { $defaults = self::get_module_defaults($node->settings->type); } return $defaults; } /** * Callback for the uasort function. * * @since 1.0 * @param int $a The first position. * @param int $b The second position. * @return int */ static public function order_nodes($a, $b) { return (int)$a->position - (int)$b->position; } /** * Counts the number of nodes in a parent. * * @since 1.0 * @param string $type The type of nodes to count. * @param string $parent_id The parent node id. * @return int */ static public function count_nodes($type = 'row', $parent_id = null) { return count(self::get_nodes($type, $parent_id)); } /** * Returns the index of the next available * position in a parent node. * * @since 1.0 * @param string $type The type of nodes to count. * @param string $parent_id The parent node id. * @return int */ static public function next_node_position($type = 'row', $parent_id = null) { $nodes = self::get_nodes($type, $parent_id); $last = array_pop($nodes); return $last ? $last->position + 1 : 0; } /** * Deletes a node. * * @since 1.0 * @param string $node_id The ID of the node to delete. * @return void */ static public function delete_node( $node_id = null ) { // Get the layout data. $data = self::get_layout_data(); // Return if the node doesn't exist. if ( ! isset( $data[ $node_id] ) ) { return; } // Get the node. $node = $data[ $node_id ]; // Call the delete method if we're deleting a module. self::call_module_delete( $node ); // Delete the node. unset( $data[ $node_id ] ); // Reorder sibling nodes. $siblings = self::get_nodes( $node->type, $node->parent ); $position = 0; foreach ( $siblings as $sibling_id => $sibling ) { if ( isset( $data[ $sibling_id ] ) ) { $data[ $sibling_id ]->position = $position; $position++; } } // Delete the node's children. self::delete_child_nodes_from_data( $node, $data ); // Update the layout data. self::update_layout_data( $data ); } /** * Deletes all child nodes for a parent. * * @since 1.0 * @param object $parent The parent node object. * @param object $data The data array to delete from. * @return void */ static public function delete_child_nodes_from_data( $parent = null, &$data ) { $children = self::get_nodes( null, $parent ); foreach ( $children as $child_id => $child ) { // Call the delete method if we're deleting a module. self::call_module_delete( $child ); // Delete the node. unset( $data[ $child_id ] ); // Delete the node's children. self::delete_child_nodes_from_data( $child, $data ); } } /** * Calls the delete method for a node * that is a module. * * @since 1.0 * @param object $node A module node. * @return void */ static public function call_module_delete($node) { if($node->type == 'module' && isset(self::$modules[$node->settings->type])) { $class = get_class(self::$modules[$node->settings->type]); $instance = new $class(); $instance->settings = $node->settings; $instance->delete(); $instance->remove(); } } /** * Repositions a node within a parent. * * @since 1.0 * @param string $node_id A node ID. * @param int $position The new position. * @param string $type The type of node to order. * @return void */ static public function reorder_node($node_id = null, $position = 0) { $data = self::get_layout_data(); $node = $data[$node_id]; $type = ! $node->parent ? $node->type : null; $nodes = self::get_nodes($type, $node->parent); $new_pos = 0; // Make sure node positions start at zero. foreach($nodes as $node) { $data[$node->node]->position = $new_pos; $new_pos++; } // Get the node and remove it from the array. $node = $data[$node_id]; $removed = array_splice($nodes, $node->position, 1); $new_pos = 0; // Reposition it in the array. array_splice($nodes, $position, 0, $removed); // Update the position data. foreach($nodes as $node) { $data[$node->node]->position = $new_pos; $new_pos++; } // Update the layout data. self::update_layout_data($data); } /** * Moves a node to another parent. * * @since 1.0 * @param string $node_id ID of the node to move. * @param int $new_parent_id ID of the new parent. * @param int $position The position in the new parent. * @return void */ static public function move_node($node_id = null, $new_parent_id = null, $position = 0) { $data = self::get_layout_data(); $new_parent = self::get_node($new_parent_id); $node = self::get_node($node_id); $siblings = self::get_nodes(null, $node->parent); $sibling_pos = 0; // Set the node's new parent. $data[ $node_id ]->parent = $new_parent->node; // Remove the node from the $siblings array. unset( $siblings[ $node_id ] ); // Reorder old siblings. foreach ( $siblings as $sibling ) { $data[ $sibling->node ]->position = $sibling_pos; $sibling_pos++; } // Update the layout data. self::update_layout_data($data); // Set the node's new order. self::reorder_node($node_id, $position); } /** * Adds a row to the current layout. * * @since 1.0 * @param string $cols The type of column layout to use. * @param int $position The position of the new row. * @return object The new row object. */ static public function add_row($cols = '1-col', $position = false) { $data = self::get_layout_data(); $settings = self::get_row_defaults(); $row_node_id = self::generate_node_id(); // Add the row. $data[$row_node_id] = new StdClass(); $data[$row_node_id]->node = $row_node_id; $data[$row_node_id]->type = 'row'; $data[$row_node_id]->parent = null; $data[$row_node_id]->position = self::next_node_position('row'); $data[$row_node_id]->settings = $settings; // Update the layout data. self::update_layout_data($data); // Position the row. if($position !== false) { self::reorder_node($row_node_id, $position); } // Add a column group. self::add_col_group($row_node_id, $cols, 0); // Return the updated row. return self::get_node($row_node_id); } /** * Copys a row and adds it to the current layout. * * @since 1.0 * @param string $node_id Node ID of the row to copy. * @return void */ static public function copy_row( $node_id = null ) { $layout_data = self::get_layout_data(); $row = self::get_node( $node_id ); $new_row_id = self::generate_node_id(); $col_groups = self::get_nodes( 'column-group', $row ); $new_nodes = array(); // Add the new row. $layout_data[ $new_row_id ] = clone $row; $layout_data[ $new_row_id ]->settings = clone $row->settings; $layout_data[ $new_row_id ]->node = $new_row_id; // Unset row template data. if ( isset( $layout_data[ $new_row_id ]->template_id ) ) { unset( $layout_data[ $new_row_id ]->template_id ); unset( $layout_data[ $new_row_id ]->template_node_id ); unset( $layout_data[ $new_row_id ]->template_root_node ); } // Get the new child nodes. foreach ( $col_groups as $col_group ) { $new_nodes[ $col_group->node ] = clone $col_group; $cols = self::get_nodes( 'column', $col_group ); foreach ( $cols as $col ) { $new_nodes[ $col->node ] = clone $col; $new_nodes[ $col->node ]->settings = clone $col->settings; $nodes = self::get_nodes( null, $col ); foreach ( $nodes as $node ) { $new_nodes[ $node->node ] = clone $node; if ( 'module' == $node->type ) { $new_nodes[ $node->node ]->settings = self::clone_module_settings( $node->settings ); } else if ( 'column-group' == $node->type ) { $nested_cols = self::get_nodes( 'column', $node ); foreach ( $nested_cols as $nested_col ) { $new_nodes[ $nested_col->node ] = clone $nested_col; $new_nodes[ $nested_col->node ]->settings = clone $nested_col->settings; $modules = self::get_nodes( 'module', $nested_col ); foreach ( $modules as $module ) { $new_nodes[ $module->node ] = clone $module; $new_nodes[ $module->node ]->settings = self::clone_module_settings( $module->settings ); } } } } } } // Generate new child ids. $new_nodes = self::generate_new_node_ids( $new_nodes ); // Set col group parent ids to the new row id and unset template data. foreach ( $new_nodes as $child_node_id => $child ) { if ( $child->type == 'column-group' ) { if ( $child->parent == $row->node || ( isset( $row->template_node_id ) && $child->parent == $row->template_node_id ) ) { $new_nodes[ $child_node_id ]->parent = $new_row_id; } } if ( isset( $new_nodes[ $child_node_id ]->template_id ) ) { unset( $new_nodes[ $child_node_id ]->template_id ); unset( $new_nodes[ $child_node_id ]->template_node_id ); } } // Merge the child data. $layout_data = array_merge( $layout_data, $new_nodes ); // Update the layout data. self::update_layout_data( $layout_data ); // Position the new row. self::reorder_node( $new_row_id, $row->position + 1 ); // Return the new row. return self::get_node( $new_row_id ); } /** * Returns the default settings for row nodes. * * @since 1.0 * @return object */ static public function get_row_defaults() { return self::get_settings_form_defaults( 'row' ); } /** * Returns an array of spacing placeholders for row * margins and padding. * * @since 1.9 * @return array */ static public function get_row_spacing_placeholders() { $settings = FLBuilderModel::get_global_settings(); $placeholders = array(); // Default. $placeholders['row_margins'] = $settings->row_margins; $placeholders['row_padding'] = $settings->row_padding; // Medium. $placeholders['row_margins_medium'] = ( '' != $settings->row_margins_medium ) ? $settings->row_margins_medium : $settings->row_margins; $placeholders['row_padding_medium'] = ( '' != $settings->row_padding_medium ) ? $settings->row_padding_medium : $settings->row_padding; // Responsive row margins. if ( '' != $settings->row_margins_responsive ) { $placeholders['row_margins_responsive'] = $settings->row_margins_responsive; } else if ( $settings->auto_spacing ) { $placeholders['row_margins_responsive'] = 0; } else { $placeholders['row_margins_responsive'] = $placeholders['row_margins_medium']; } // Responsive row padding. if ( '' != $settings->row_padding_responsive ) { $placeholders['row_padding_tb_responsive'] = $settings->row_padding_responsive; $placeholders['row_padding_lr_responsive'] = $settings->row_padding_responsive; } else if ( $settings->auto_spacing ) { $placeholders['row_padding_tb_responsive'] = $placeholders['row_padding_medium']; $placeholders['row_padding_lr_responsive'] = 0; } else { $placeholders['row_padding_tb_responsive'] = $placeholders['row_padding_medium']; $placeholders['row_padding_lr_responsive'] = $placeholders['row_padding_medium']; } return $placeholders; } /** * Runs row specific logic on new row settings. * * @since 1.0 * @param object $row A row node. * @param object $new_settings The new settings object. * @return object */ static public function process_row_settings( $row, $new_settings ) { // Cache background video data. if ( $new_settings->bg_type == 'video' ) { // Video Fallback Photo if ( ! empty( $new_settings->bg_video_fallback_src ) ) { $fallback = $new_settings->bg_video_fallback_src; } else { $fallback = ''; } if ( $new_settings->bg_video_source == 'wordpress' ) { // Video MP4 $mp4 = FLBuilderPhoto::get_attachment_data( $new_settings->bg_video ); if ( $mp4 ) { $parts = explode( '.', $mp4->filename ); $mp4->extension = array_pop( $parts ); $new_settings->bg_video_data = $mp4; $new_settings->bg_video_data->fallback = $fallback; } // Video WebM $webm = FLBuilderPhoto::get_attachment_data( $new_settings->bg_video_webm ); if ( $webm ) { $parts = explode( '.', $webm->filename ); $webm->extension = array_pop( $parts ); $new_settings->bg_video_webm_data = $webm; $new_settings->bg_video_webm_data->fallback = $fallback; } } } // Cache background slideshow data. if($new_settings->bg_type == 'slideshow' && $new_settings->ss_source == 'wordpress') { // Make sure we have a photo data object. if(!isset($row->settings->ss_photo_data)) { $row->settings->ss_photo_data = new StdClass(); } // Hijack the slideshow module to get WordPress photo data. $ss = new FLSlideshowModule(); $ss->settings = new StdClass(); $ss->settings->photos = $new_settings->ss_photos; $ss->settings->photo_data = $row->settings->ss_photo_data; $new_settings->ss_photo_data = $ss->get_wordpress_photos(); } return $new_settings; } /** * Returns background data for a row. * * @since 1.0 * @param object $row A row node. * @return object */ static public function get_row_bg_data( $row ) { $data = null; // Background Video if ( $row->settings->bg_type == 'video' ) { if ( isset( $row->settings->bg_video_data ) ) { $data = array(); $data[ 'mp4' ] = $row->settings->bg_video_data; } if ( isset( $row->settings->bg_video_webm_data ) ) { if ( ! $data ) { $data = array(); } $data[ 'webm' ] = $row->settings->bg_video_webm_data; } } // Background Slideshow else if ( $row->settings->bg_type == 'slideshow' && isset( $row->settings->ss_photo_data ) ) { $data = $row->settings->ss_photo_data; } return $data; } /** * Returns the source for a row background slideshow. * * @since 1.0 * @param object $row A row node. * @return string */ static public function get_row_slideshow_source($row) { // Make sure we have a photo data object. if(!isset($row->settings->ss_photo_data)) { $row->settings->ss_photo_data = new StdClass(); } // Hijack the slideshow module to get the source. $ss = new FLSlideshowModule(); $ss->settings = new StdClass(); $ss->settings->source = $row->settings->ss_source; $ss->settings->photos = $row->settings->ss_photos; $ss->settings->feed_url = $row->settings->ss_feed_url; $ss->settings->photo_data = $row->settings->ss_photo_data; // Return the slideshow source. return $ss->get_source(); } /** * Adds a column group to a row in the current layout. * * @since 1.0 * @param string $node_id A row node ID. * @param string $cols The type of column group layout or the ID of an existing column to add. * @param int $position The position of the new column group. * @return object The new column group object. */ static public function add_col_group($node_id = null, $cols = '1-col', $position = false) { $data = self::get_layout_data(); $group_node_id = self::generate_node_id(); $parent = self::get_node( $node_id ); $old_group = null; // Add the column group. $data[$group_node_id] = new StdClass(); $data[$group_node_id]->node = $group_node_id; $data[$group_node_id]->type = 'column-group'; $data[$group_node_id]->parent = $node_id; $data[$group_node_id]->position = self::next_node_position(null, $node_id); $data[$group_node_id]->settings = ''; // Add node template data. if ( self::is_node_global( $parent ) ) { $data[$group_node_id]->template_id = $parent->template_id; $data[$group_node_id]->template_node_id = $group_node_id; } // Add new columns? if ( isset( self::$row_layouts[ $cols ] ) ) { for($i = 0; $i < count(self::$row_layouts[$cols]); $i++) { $col_node_id = self::generate_node_id(); $data[$col_node_id] = new StdClass(); $data[$col_node_id]->node = $col_node_id; $data[$col_node_id]->type = 'column'; $data[$col_node_id]->parent = $group_node_id; $data[$col_node_id]->position = $i; $data[$col_node_id]->settings = new StdClass(); $data[$col_node_id]->settings->size = self::$row_layouts[$cols][$i]; if ( self::is_node_global( $parent ) ) { $data[$col_node_id]->template_id = $parent->template_id; $data[$col_node_id]->template_node_id = $col_node_id; } } } // Add an existing column. else { $old_group = $data[ $cols ]->parent; $siblings = self::get_nodes( 'column', $old_group ); $sibling_pos = 0; // Add the column to the group. $data[ $cols ]->parent = $group_node_id; $data[ $cols ]->position = 0; $data[ $cols ]->settings->size = 100; if ( self::is_node_global( $parent ) ) { $data[ $cols ]->template_id = $parent->template_id; $data[ $cols ]->template_node_id = $data[ $cols ]->node; } // Remove the column from the $siblings array. unset( $siblings[ $cols ] ); // Reorder old siblings. foreach ( $siblings as $sibling ) { $data[ $sibling->node ]->position = $sibling_pos; $sibling_pos++; } } // Update the layout data. self::update_layout_data($data); // Delete an existing column's old group if empty or resize it. if ( $old_group ) { if ( 0 === count( self::get_nodes( 'column', $old_group ) ) ) { self::delete_node( $old_group ); } else { self::reset_col_widths( $old_group ); } } // Position the column group. if($position !== false) { self::reorder_node($group_node_id, $position); } // Return the column group. return self::get_node($group_node_id); } /** * Runs column specific logic on new column settings. * * @since 1.0 * @param object $col A column node. * @param object $new_settings The new settings object. * @return object */ static public function process_col_settings($col, $new_settings) { // Resize sibling cols if needed. $new_settings->size = self::resize_col($col->node, $new_settings->size); // Update other sibling vars as needed. $equal_height = false; $content_alignment = false; $responsive_order = false; // Adjust sibling equal height? if ( $col->settings->equal_height != $new_settings->equal_height ) { $equal_height = $new_settings->equal_height; } // Adjust sibling content alignment? if ( $col->settings->content_alignment != $new_settings->content_alignment ) { $content_alignment = $new_settings->content_alignment; } // Adjust sibling responsive order? if ( $col->settings->responsive_order != $new_settings->responsive_order ) { $responsive_order = $new_settings->responsive_order; } // Update the siblings? if ( false !== $equal_height || false !== $content_alignment || false !== $responsive_order ) { $data = self::get_layout_data(); $cols = self::get_nodes( 'column', $col->parent ); foreach ( $cols as $node_id => $node ) { if ( false !== $equal_height ) { $data[ $node_id ]->settings->equal_height = $equal_height; } if ( false !== $content_alignment ) { $data[ $node_id ]->settings->content_alignment = $content_alignment; } if ( false !== $responsive_order ) { $data[ $node_id ]->settings->responsive_order = $responsive_order; } } self::update_layout_data( $data ); } return $new_settings; } /** * Deletes a column. * * @since 1.0 * @param string $node_id Node ID of the column to delete (can also be a group). * @param int $new_width New width of the remaining columns. * @return void */ static public function delete_col($node_id = null, $new_width = 100) { $col = self::get_node($node_id); // Delete the column. self::delete_node($node_id); // Return if the node we just deleted was a group. if('column-group' == $col->type) { return; } // Get the group $group = self::get_node($col->parent); // Get the group children. $cols = self::get_nodes('column', $group->node); // Delete the group if empty. if(count($cols) === 0) { self::delete_node($group->node); } // Resize the remaining columns. else { // Get the layout data. $data = self::get_layout_data(); // Loop through the columns. foreach($cols as $col_id => $col) { // Set the new size. $data[$col_id]->settings->size = round($new_width, 2); } // Update the layout data. self::update_layout_data($data); } } /** * Moves a column within a group. * * @since 1.9 * @param string $node_id * @param int $position * @return void */ static public function reorder_col( $node_id, $position = 0 ) { $col = self::get_node( $node_id ); self::reorder_node( $node_id, $position ); self::reset_col_widths( $col->parent ); } /** * Moves a column from one group to another. * * @since 1.9 * @param string $col_id * @param string $group_id * @param int $position * @param array $resize * @return void */ static public function move_col( $col_id, $group_id, $position, $resize = array() ) { $col = self::get_node( $col_id ); $old_group = self::get_node( $col->parent ); self::move_node( $col_id, $group_id, $position ); if ( 0 === count( self::get_nodes( 'column', $old_group ) ) ) { self::delete_node( $old_group->node ); self::reset_col_widths( $group_id ); } else { self::reset_col_widths( $resize ); } } /** * Resizes a column. * * @since 1.0 * @param string $node_id Node ID of the column to resize. * @param int $new_width New width of the column. * @return int The new width */ static public function resize_col($node_id = null, $new_width = 100) { $data = self::get_layout_data(); $col = $data[$node_id]; $group = $data[$col->parent]; $cols = array_values(self::get_nodes('column', $group->node)); $pos = $col->position; $siblings = array(); $siblings_width = 0; $num_cols = count($cols); $min_width = 8; $max_width = 100 - $min_width; // Don't resize if only one column or width isn't a number. if($num_cols == 1 || !is_numeric($new_width)) { return $col->settings->size; } // Find the sibling column to absorb this resize. if($pos === 0) { $sibling = $cols[1]; } else if($pos == $num_cols - 1) { $sibling = $cols[$num_cols - 2]; } else { $sibling = $cols[$pos + 1]; } // Find other siblings. foreach($cols as $c) { if($col->node == $c->node) { continue; } if($sibling->node == $c->node) { continue; } $siblings[] = $c; $max_width -= $c->settings->size; $siblings_width += $c->settings->size; } // Make sure the new width isn't too small. if($new_width < $min_width) { $new_width = $min_width; } // Make sure the new width isn't too big. if($new_width > $max_width) { $new_width = $max_width; } // Save new sibling size. $data[$sibling->node]->settings->size = round(100 - $siblings_width - $new_width, 2); // Save new column size. $data[$col->node]->settings->size = $new_width; // Update the layout data. self::update_layout_data($data); // Return the new size. return $new_width; } /** * Resizes a column and its sibling using the provided widths. * * @since 1.6.4 * @param string $col_id Node ID of the column to resize. * @param int $col_width New width of the column. * @param string $sibling_id Node ID of the sibling to resize. * @param int $sibling_width New width of the sibling. * @return void */ static public function resize_cols( $col_id = null, $col_width = null, $sibling_id = null, $sibling_width = null ) { $data = self::get_layout_data(); // Save the column width. $data[ $col_id ]->settings->size = $col_width; // Save the sibling width. $data[ $sibling_id ]->settings->size = $sibling_width; // Update the layout data. self::update_layout_data( $data ); } /** * Resets the widths of all columns in a group. * * @since 1.6.4 * @param string|array $group_id Node ID of the group whose columns to reset or an array of group IDs. * @return void */ static public function reset_col_widths( $group_id = null ) { if ( 'array' == gettype( $group_id ) ) { foreach ( $group_id as $id ) { self::reset_col_widths( $id ); } return; } $data = self::get_layout_data(); $post_data = self::get_post_data(); $cols = self::get_nodes( 'column', $group_id ); $width = round( 100 / count( $cols ), 2 ); foreach ( $cols as $col_id => $col ) { $data[ $col_id ]->settings->size = $width; } self::update_layout_data( $data ); } /** * Adds a column to a column group in the current layout. * * @since 1.9 * @param string $node_id A column group node ID. * @param int $position The position of the new column. * @return object The new column object. */ static public function add_col($node_id = null, $position = false) { $group = self::get_node( $node_id ); $cols = self::get_nodes( 'column', $group ); $num_cols = count( $cols ); $i = 0; $sibling = false; $insert = 'before'; foreach ( $cols as $col ) { if ( $i == $position ) { $sibling = $col; break; } $i++; } if ( ! $sibling ) { $sibling = $col; $insert = 'after'; } self::add_cols( $sibling->node, $insert ); $cols = self::get_nodes( 'column', $group ); $col_ids = array_keys( $cols ); return $cols[ $col_ids[ $position ] ]; } /** * Inserts a column (or columns) before or after another column. * * @since 1.6.4 * @param string $node_id Node ID of the column to insert before or after. * @param string $insert Either before or after. * @param string $type The type of column(s) to insert. * @param boolean $nested Whether these columns are nested or not. * @return object */ static public function add_cols( $col_id, $insert = 'before', $type = '1-col', $nested = false ) { $data = self::get_layout_data(); $col = self::get_node( $col_id ); $parent = self::get_node( $col->parent ); $cols = self::get_nodes( 'column', $col->parent ); $global = self::is_node_global( $parent ); $num_new_cols = count( self::$row_layouts[ $type ] ); $num_cols = count( $cols ); $max_cols = $nested ? 4 : 12; $reposition = false; $position = 0; // Make sure we have 12 columns or less. if ( $num_cols + $num_new_cols > $max_cols ) { $num_new_cols = $num_new_cols - ( $num_cols + $num_new_cols - $max_cols ); $num_cols = $max_cols; } else { $num_cols += $num_new_cols; } // Get the new width. if ( 6 === $num_cols ) { $new_width = 16.65; } elseif ( 7 === $num_cols ) { $new_width = 14.28; } else { $new_width = round( 100 / $num_cols, 2 ); } // Get the new column position. if ( 'before' == $insert ) { $new_col_position = $col->position - 1 < 0 ? 0 : $col->position; } else { $new_col_position = $col->position + 1; } // Add the new columns. for ( $i = 0; $i < $num_new_cols; $i++ ) { $new_col_id = self::generate_node_id(); $data[ $new_col_id ] = new StdClass(); $data[ $new_col_id ]->node = $new_col_id; $data[ $new_col_id ]->type = 'column'; $data[ $new_col_id ]->parent = $parent->node; $data[ $new_col_id ]->position = $new_col_position; $data[ $new_col_id ]->settings = new StdClass(); $data[ $new_col_id ]->settings->size = $new_width; // Add node template data. if ( $global ) { $data[ $new_col_id ]->template_id = $parent->template_id; $data[ $new_col_id ]->template_node_id = $new_col_id; } $new_col_position++; } // Resize sibling columns and set their new position. foreach ( $cols as $sibling_col_id => $sibling_col ) { $data[ $sibling_col_id ]->settings->size = $new_width; if ( $sibling_col_id == $col_id ) { $reposition = true; if ( 'before' == $insert ) { $data[ $sibling_col_id ]->position = $new_col_position; $new_col_position++; } } else if ( $reposition ) { $data[ $sibling_col_id ]->position = $new_col_position; $new_col_position++; } else { $data[ $sibling_col_id ]->position = $position; $position++; } } // Update the layout data. self::update_layout_data( $data ); // Return the column group. return $parent; } /** * Returns the default settings for column nodes. * * @since 1.0 * @return object */ static public function get_col_defaults() { return self::get_settings_form_defaults( 'col' ); } /** * Loads the classes for core builder modules. * * @since 1.0 * @return void */ static public function load_modules() { $path = FL_BUILDER_DIR . 'modules/'; $dir = dir($path); $module_path = ''; while(false !== ($entry = $dir->read())) { if(!is_dir($path . $entry) || $entry == '.' || $entry == '..') { continue; } // Paths to check. $module_path = $entry . '/' . $entry . '.php'; $child_path = get_stylesheet_directory() . '/fl-builder/modules/' . $module_path; $theme_path = get_template_directory() . '/fl-builder/modules/' . $module_path; $builder_path = FL_BUILDER_DIR . 'modules/' . $module_path; // Check for the module class in a child theme. if(is_child_theme() && file_exists($child_path)) { require_once $child_path; } // Check for the module class in a parent theme. else if(file_exists($theme_path)) { require_once $theme_path; } // Check for the module class in the builder directory. else if(file_exists($builder_path)) { require_once $builder_path; } } } /** * Registers a module with the builder. * * @since 1.0 * @param string $class The module's PHP class name. * @param array $form The module's settings form. * @return void */ static public function register_module($class, $form) { if(class_exists($class)) { // Create a new instance of the module. $instance = new $class(); // Log an error if a module with this slug already exists. if ( isset( self::$modules[ $instance->slug ] ) ) { error_log( sprintf( _x( 'A module with the filename %s.php already exists! Please namespace your module filenames to ensure compatibility with Beaver Builder.', '%s stands for the module filename', 'fl-builder' ), $instance->slug ) ); return; } // Filter the enabled flag. $instance->enabled = apply_filters( 'fl_builder_register_module', $instance->enabled, $instance ); // Save the instance in the modules array. self::$modules[$instance->slug] = $instance; // Add the form to the instance. self::$modules[$instance->slug]->form = apply_filters( 'fl_builder_register_settings_form', $form, $instance->slug ); self::$modules[$instance->slug]->form['advanced'] = self::$settings_forms['module_advanced']; } } /** * Checks to see if a module of a certain type has * been registered. * * @since 1.9 * @param array $type The module's type slug. * @return void */ static public function is_module_registered( $type ) { return isset( self::$modules[ $type ] ); } /** * Returns an array of all modules that are enabled. * * @since 1.0 * @return array */ static public function get_enabled_modules() { $default = array_keys( self::$modules ); $default[] = 'all'; $setting = self::get_admin_settings_option( '_fl_builder_enabled_modules', true ); $setting = ( ! $setting || in_array( 'all', $setting ) ) ? $default : $setting; foreach ( self::$modules as $module_slug => $module ) { if ( ! $module->enabled && in_array( $module_slug, $setting ) ) { $key = array_search( $module_slug, $setting ); unset( $setting[ $key ] ); } } return apply_filters( 'fl_builder_enabled_modules', $setting ); } /** * Returns an array of categorized modules. * * @since 1.0 * @param bool $show_disabled Whether to include disabled modules in the result. * @return array */ static public function get_categorized_modules( $show_disabled = false ) { $enabled_modules = self::get_enabled_modules(); $widgets = null; $categories = array(); // Add any predefined custom categories. foreach ( apply_filters( 'fl_builder_module_categories', array() ) as $custom_category ) { $categories[ $custom_category ] = array(); } // Get the core category keys. $basic_key = __('基础元素', 'fl-builder'); $advanced_key = __('高级元素', 'fl-builder'); $other_key = __('其他元素', 'fl-builder'); $widgets_key = __('插件', 'fl-builder'); // Build the default category arrays. $categories[ $basic_key ] = array(); $categories[ $advanced_key ] = array(); $categories[ $other_key ] = array(); // Build the categories array. foreach(self::$modules as $module) { if ( ! $module->enabled ) { continue; } else if(!in_array($module->slug, $enabled_modules) && !$show_disabled) { continue; } else if($module->slug == 'widget') { $widgets = self::get_wp_widgets(); } else if(isset($module->category)) { if(!isset($categories[$module->category])) { $categories[$module->category] = array(); } $categories[$module->category][$module->name] = $module; } else { $categories[$other_key][$module->name] = $module; } } // Add widgets if we have them. if ( $widgets ) { $categories[$widgets_key] = $widgets; } // Sort the modules. foreach($categories as $title => $modules) { if(count($categories[$title]) == 0) { unset($categories[$title]); } else { ksort($categories[$title]); } } // Return sorted categories. return $categories; } /** * Returns the slug for a module category. * * @since 1.0 * @param string $name The category name. * @return string */ static public function get_module_category_slug( $name ) { // Get the core category keys. $basic_key = __('基础元素', 'fl-builder'); $advanced_key = __('高级元素', 'fl-builder'); $other_key = __('其他元素', 'fl-builder'); $widgets_key = __('插件', 'fl-builder'); if ( $name == $basic_key ) { return 'basic'; } if ( $name == $advanced_key ) { return 'advanced'; } if ( $name == $other_key ) { return 'other'; } if ( $name == $widgets_key ) { return 'widgets'; } return sanitize_html_class( $name ); } /** * Returns an instance of a module. * * @since 1.0 * @param string|object $node_id A module node ID or object. * @return object|bool The module or false if it doesn't exist. */ static public function get_module( $node_id ) { $module = is_object( $node_id ) ? $node_id : self::get_node( $node_id ); if( self::is_module_registered( $module->settings->type ) ) { $class = get_class(self::$modules[$module->settings->type]); $instance = new $class(); $instance->node = $module->node; $instance->parent = $module->parent; $instance->position = $module->position; $instance->settings = $module->settings; $instance->type = 'module'; $instance->form = self::$modules[$module->settings->type]->form; if ( isset( $module->template_id ) ) { $instance->template_id = $module->template_id; $instance->template_node_id = $module->template_node_id; } if ( isset( $module->template_root_node ) ) { $instance->template_root_node = true; } return $instance; } return false; } /** * Returns an array of all modules in the current layout * or in a column if a column id or object is supplied. * * @since 1.0 * @param string|object $col_id A column ID or object. * @return array */ static public function get_modules($col_id = null) { $col = is_object( $col_id ) ? $col_id : self::get_node( $col_id ); $modules = self::get_nodes('module', $col); $instances = array(); $i = 0; foreach($modules as $module) { if ( self::is_module_registered( $module->settings->type ) ) { $class = get_class(self::$modules[$module->settings->type]); $instances[$i] = new $class(); $instances[$i]->node = $module->node; $instances[$i]->parent = $module->parent; $instances[$i]->position = $module->position; $instances[$i]->settings = $module->settings; $instances[$i]->type = 'module'; $instances[$i]->form = self::$modules[$module->settings->type]->form; if ( isset( $module->template_id ) ) { $instances[$i]->template_id = $module->template_id; $instances[$i]->template_node_id = $module->template_node_id; } if ( isset( $module->template_root_node ) ) { $instances[$i]->template_root_node = true; } $i++; } } return $instances; } /** * Returns an array of all modules in the current layout. * * @since 1.0 * @return array */ static public function get_all_modules() { return self::get_modules(); } /** * Add a new module to a column in the current layout. * * @since 1.0 * @param string $type The type of module to add. * @param array $settings The new module's settings. * @param string $parent_id The new module's parent node ID. * @param int $position The new module's position. * @return object The new module object. */ static public function add_module($type = null, $settings = array(), $parent_id = null, $position = false ) { $data = self::get_layout_data(); $parent = self::get_node( $parent_id ); $module_node_id = self::generate_node_id(); $settings->type = $type; // Run module update method. $class = get_class(self::$modules[$type]); $instance = new $class(); $instance->node = $module_node_id; $instance->settings = $settings; $settings = $instance->update($settings); // Save the module. $data[$module_node_id] = new StdClass(); $data[$module_node_id]->node = $module_node_id; $data[$module_node_id]->type = 'module'; $data[$module_node_id]->parent = $parent_id; $data[$module_node_id]->position = self::next_node_position('module', $parent_id); $data[$module_node_id]->settings = $settings; // Add node template data. if ( self::is_node_global( $parent ) ) { $data[$module_node_id]->template_id = $parent->template_id; $data[$module_node_id]->template_node_id = $module_node_id; } // Update the layout data. self::update_layout_data($data); // Position the module. if($position !== false) { self::reorder_node($module_node_id, $position); } // Send back the inserted module. return self::get_module($module_node_id); } /** * Adds a parent node for a module if a parent with the supplied * parent ID doesn't exist. * * @since 1.6.3 * @param string $parent_id The node ID of the parent to look for. * @param int $position The position of the parent. * @return string|null The new parent ID or null if none exists. */ static public function add_module_parent( $parent_id = null, $position = null ) { $parent = ! $parent_id ? null : self::get_node( $parent_id ); // Add a new row if we don't have a parent. if ( ! $parent ) { $row = self::add_row( '1-col', $position ); $col_groups = self::get_nodes( 'column-group', $row->node ); $col_group = array_shift( $col_groups ); $cols = self::get_nodes( 'column', $col_group->node ); $parent = array_shift( $cols ); $parent_id = $parent->node; } // Add a new column group if the parent is a row. else if ( $parent->type == 'row' ) { $col_group = self::add_col_group( $parent->node, '1-col', $position ); $cols = self::get_nodes( 'column', $col_group->node ); $parent = array_shift( $cols ); $parent_id = $parent->node; } // Add a new column if the parent is a column group. else if ( $parent->type == 'column-group' ) { $parent = self::add_col( $parent->node, $position ); $parent_id = $parent->node; } return $parent_id; } /** * Returns a module's parent node of the specified type. * * @since 1.7 * @param string $type The type of parent to return. * @param string|object $module_id The module's node ID. Can also be a module object. * @return object The parent node. */ static public function get_module_parent( $type, $module_id ) { $module = is_object( $module_id ) ? $module_id : self::get_module( $module_id ); $nodes = self::get_categorized_nodes(); foreach ( $nodes['columns'] as $column ) { if ( $column->node == $module->parent ) { if ( 'column' == $type ) { return $column; } foreach ( $nodes['groups'] as $group ) { if ( $group->node == $column->parent ) { if ( 'column-group' == $type ) { return $group; } foreach ( $nodes['rows'] as $row ) { if ( $row->node == $group->parent ) { return $row; } } } } } } return null; } /** * Add a new module with default settings to a column * in the current layout. * * @since 1.0 * @param string $parent_id The new module's parent node ID. * @param string $type The type of module to add. * @param int $position The new module's position. * @return object The new module object. */ static public function add_default_module($parent_id = null, $type = null, $position = null) { $parent = $parent_id == 0 ? null : self::get_node($parent_id); $settings = self::get_module_defaults($type); $module_node_id = self::generate_node_id(); // Add a new parent if one is needed. if ( ! $parent || 'row' == $parent->type || 'column-group' == $parent->type ) { $parent_id = self::add_module_parent( $parent_id, $position ); $parent = self::get_node( $parent_id ); $position = null; } // Run module update method. $class = get_class(self::$modules[$type]); $instance = new $class(); $instance->node = $module_node_id; $instance->settings = $settings; $settings = $instance->update($settings); // Save the module. $data = self::get_layout_data(); $data[$module_node_id] = new StdClass(); $data[$module_node_id]->node = $module_node_id; $data[$module_node_id]->type = 'module'; $data[$module_node_id]->parent = $parent_id; $data[$module_node_id]->position = self::next_node_position('module', $parent_id); $data[$module_node_id]->settings = $settings; // Add node template data. if ( self::is_node_global( $parent ) ) { $data[$module_node_id]->template_id = $parent->template_id; $data[$module_node_id]->template_node_id = $module_node_id; } // Update the layout data. self::update_layout_data($data); // Position the module. if(null !== $position) { self::reorder_node($module_node_id, $position); } // Send back the inserted module. return self::get_module($module_node_id); } /** * Make a copy of a module. * * @since 1.0 * @param string $node_id Node ID of the module to copy. * @return object The new module object. */ static public function copy_module( $node_id = null ) { $module = self::get_module( $node_id ); return self::add_module( $module->settings->type, $module->settings, $module->parent, $module->position + 1 ); } /** * Run module specific logic on new node settings. * * @since 1.0 * @param object $module A module node object. * @param object $new_settings The new settings. * @return object */ static public function process_module_settings($module, $new_settings) { // Get a new node instance to work with. $class = get_class(self::$modules[$module->settings->type]); $instance = new $class(); $instance->node = $module->node; // Run node delete to clear any cache. $instance->settings = $module->settings; $instance->delete(); // Run node update. $instance->settings = $new_settings; $new_settings = $instance->update($new_settings); return $new_settings; } /** * Returns a cloned settings object for a module. * * @since 1.9 * @param object $settings * @return object */ static public function clone_module_settings( $settings ) { $new_settings = new stdClass; foreach ( $settings as $key => $val ) { $new_settings->$key = $val; } return $new_settings; } /** * Returns the default settings for a module. * * @since 1.0 * @param string $type The type of module. * @return object */ static public function get_module_defaults($type) { $defaults = new StdClass(); if(isset(self::$modules[$type]->form)) { $defaults = self::get_settings_form_defaults( $type ); $defaults->type = $type; } return $defaults; } /** * Merges the default settings for nested forms in a module. * * @since 1.7 * @param string $type The type of module. * @param object $settings The module settings object. * @return object */ static public function merge_nested_module_defaults( $type, $settings ) { // Make sure the module form exists. if ( isset( self::$modules[ $type ] ) ) { // Get the fields. $fields = self::get_settings_form_fields( self::$modules[ $type ]->form ); // Loop through the settings. foreach ( $settings as $key => $val ) { // Make sure this field is a nested form. if ( ! isset( $fields[ $key ]['form'] ) ) { continue; } // Get the nested form defaults. $nested_defaults = self::get_settings_form_defaults( $fields[ $key ]['form'] ); // Merge the defaults. if ( is_array( $val ) ) { foreach ( $val as $nested_key => $nested_val ) { $settings->{ $key }[ $nested_key ] = ( object )array_merge( ( array )$nested_defaults, ( array )$nested_val ); } } else { $settings->{ $key } = ( object )array_merge( ( array )$nested_defaults, ( array )$settings->{ $key } ); } } } return $settings; } /** * Returns an array of data for each core WordPress widget. * * @since 1.0 * @return array */ static public function get_wp_widgets() { global $wp_widget_factory; $widgets = array(); foreach($wp_widget_factory->widgets as $class => $widget) { $widget->class = $class; $widgets[$widget->name] = $widget; } ksort($widgets); return $widgets; } /** * Returns an array of data for all registered sidebars. * * @since 1.0 * @return array */ static public function get_wp_sidebars() { global $wp_registered_sidebars; $sidebars = array(); foreach($wp_registered_sidebars as $sidebar) { $sidebars[$sidebar['name']] = $sidebar; } ksort($sidebars); return $sidebars; } /** * Loads the files for all core builder settings. * * @since 1.0 * @return void */ static public function load_settings() { require_once FL_BUILDER_DIR . 'includes/global-settings.php'; require_once FL_BUILDER_DIR . 'includes/layout-settings.php'; require_once FL_BUILDER_DIR . 'includes/row-settings.php'; require_once FL_BUILDER_DIR . 'includes/column-settings.php'; require_once FL_BUILDER_DIR . 'includes/module-settings.php'; } /** * Register a settings form with the builder. * * @since 1.0 * @param string $id The form id. * @param array $form The form data. * @return void */ static public function register_settings_form($id, $form) { self::$settings_forms[$id] = apply_filters( 'fl_builder_register_settings_form', $form, $id ); } /** * Returns the data for a settings form. * * @since 1.0 * @param string $id The form id. * @return array */ static public function get_settings_form( $id ) { return self::$settings_forms[ $id ]; } /** * Returns an array of fields in a settings form. * * @since 1.0 * @param array $form The form data array. * @return array */ static public function get_settings_form_fields($form) { $fields = array(); foreach ( $form as $tab ) { if ( isset( $tab['sections'] ) ) { foreach ( $tab['sections'] as $section ) { if ( isset( $section['fields'] ) ) { foreach ( $section['fields'] as $name => $field ) { $fields[ $name ] = $field; } } } } } return $fields; } /** * Returns a settings object with the defaults for a form. * * @since 1.0 * @param string $type The type of form. * @return object */ static public function get_settings_form_defaults( $type ) { // Check to see if the defaults are cached first. if ( isset( self::$settings_form_defaults[ $type ] ) ) { return self::$settings_form_defaults[ $type ]; } // They aren't cached, let's get them. $defaults = new StdClass(); // Check the registered forms first. if ( isset( self::$settings_forms[ $type ] ) ) { $form_type = $type; $tabs = self::$settings_forms[ $type ]['tabs']; } // If it's not a registered form, it must be a module form. else if ( isset( self::$modules[ $type ] ) ) { $form_type = $type . '-module'; $tabs = self::$modules[ $type ]->form; } // The form can't be found. else { return $defaults; } // Get the fields. $fields = self::get_settings_form_fields( $tabs ); // Loop through the fields and get the defaults. foreach($fields as $name => $field) { $default = isset($field['default']) ? $field['default'] : ''; $is_multiple = isset($field['multiple']) && $field['multiple'] === true; $supports_multiple = $field['type'] != 'editor' && $field['type'] != 'photo'; $responsive = isset($field['responsive']) && $field['responsive'] ? $field['responsive'] : false; $responsive_name = ''; if($is_multiple && $supports_multiple) { $defaults->$name = array($default); } else if ( $responsive ) { foreach ( array( 'default', 'medium', 'responsive' ) as $device ) { $responsive_name = $name . ( 'default' == $device ? '' : '_' . $device ); if ( is_array( $responsive ) && isset( $responsive['default'] ) && isset( $responsive['default'][ $device ] ) ) { $defaults->{ $responsive_name } = $responsive['default'][ $device ]; } else if( 'default' == $device ) { $defaults->$name = $default; } else { $defaults->{ $responsive_name } = ''; } } } else { $defaults->$name = $default; } } // Cache the defaults. self::$settings_form_defaults[ $type ] = apply_filters( 'fl_builder_settings_form_defaults', $defaults, $form_type ); return self::$settings_form_defaults[ $type ]; } /** * Save the settings for a node. * * @since 1.0 * @param string $node_id The node ID. * @param object $settings The settings to save. * @return void */ static public function save_settings($node_id = null, $settings = null) { $node = self::get_node($node_id); $new_settings = (object)array_merge((array)$node->settings, (array)$settings); $template_post_id = self::is_node_global( $node ); // Process the settings. $new_settings = self::process_node_settings($node, $new_settings); // Save the settings to the node. $data = self::get_layout_data(); $data[$node_id]->settings = $new_settings; // Update the layout data. self::update_layout_data($data); // Save settings for a global node template? if ( $template_post_id && ! self::is_post_node_template() ) { // Get the template data. $template_data = self::get_layout_data( 'published', $template_post_id ); // Update the template node settings. $template_data[ $node->template_node_id ]->settings = $new_settings; // Save the template data. self::update_layout_data( $template_data, 'published', $template_post_id ); self::update_layout_data( $template_data, 'draft', $template_post_id ); // Delete the template asset cache. self::delete_all_asset_cache( $template_post_id ); self::delete_node_template_asset_cache( $template_post_id ); } // Return the new layout. return FLBuilderAJAXLayout::render(); } /** * Adds slashes to settings going into the database as WordPress * removes them when we save using update_metadata. This is done * to ensure slashes in user input aren't removed. * * @since 1.5.6 * @param mixed $data The data to slash. * @return mixed The slashed data. */ static public function slash_settings( $data ) { if ( is_array( $data ) ) { foreach ( $data as $key => $val ) { $data[ $key ] = self::slash_settings( $val ); } } else if ( is_object( $data ) ) { foreach ( $data as $key => $val ) { $data->$key = self::slash_settings( $val ); } } else if ( is_string( $data ) ) { $data = wp_slash( $data ); } return $data; } /** * Merge defaults into a settings object. * * @since 1.0 * @param object $settings Reference to a settings object. * @param array $defaults The defaults to merge in. * @return void */ static public function default_settings(&$settings, $defaults) { foreach($defaults as $name => $value) { if(!isset($settings->$name)) { $settings->$name = $value; } } } /** * Get the global builder settings. * * @since 1.0 * @return object */ static public function get_global_settings() { if ( null === self::$global_settings ) { $settings = get_option('_fl_builder_settings'); $defaults = self::get_settings_form_defaults( 'global' ); if ( !$settings ) { $settings = new StdClass(); } // Merge in defaults and cache settings self::$global_settings = (object) array_merge((array) $defaults, (array) $settings); } return self::$global_settings; } /** * Save the global builder settings. * * @since 1.0 * @param array $settings The new global settings. * @return object */ static public function save_global_settings($settings = array()) { $old_settings = self::get_global_settings(); $new_settings = (object)array_merge((array)$old_settings, (array)$settings); self::delete_asset_cache_for_all_posts(); self::$global_settings = null; update_option('_fl_builder_settings', $settings); return self::get_global_settings(); } /** * Duplicate the current post. * * @since 1.0 * @return int The new post ID. */ static public function duplicate_post() { global $wpdb; $post_id = self::get_post_id(); $post = get_post($post_id); $current_user = wp_get_current_user(); // Duplicate the post. $data = array( 'comment_status' => $post->comment_status, 'ping_status' => $post->ping_status, 'post_author' => $current_user->ID, 'post_content' => $post->post_content, 'post_excerpt' => $post->post_excerpt, 'post_name' => $post->post_name, 'post_parent' => $post->post_parent, 'post_password' => $post->post_password, 'post_status' => 'draft', 'post_title' => sprintf( _x( 'Copy of %s', '%s stands for post/page title.', 'fl-builder' ), $post->post_title ), 'post_type' => $post->post_type, 'to_ping' => $post->to_ping, 'menu_order' => $post->menu_order ); // Get the new post id. $new_post_id = wp_insert_post($data); // Duplicate post meta. $post_meta = $wpdb->get_results("SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id= {$post_id}"); if(count($post_meta) !== 0) { $sql = "INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value) "; foreach($post_meta as $meta_info) { $meta_key = $meta_info->meta_key; $meta_value = addslashes($meta_info->meta_value); $sql_select[] = "SELECT {$new_post_id}, '{$meta_key}', '{$meta_value}'"; } $sql .= implode(" UNION ALL ", $sql_select); $wpdb->query($sql); } // Duplicate post terms. $taxonomies = get_object_taxonomies($post->post_type); foreach($taxonomies as $taxonomy) { $post_terms = wp_get_object_terms($post_id, $taxonomy); for($i = 0; $i < count($post_terms); $i++) { wp_set_object_terms($new_post_id, $post_terms[$i]->slug, $taxonomy, true); } } // Get the duplicated layout data. $data = self::get_layout_data('published', $new_post_id); // Generate new node ids. $data = self::generate_new_node_ids($data); // Save the duplicated layout data. self::update_layout_data($data, 'published', $new_post_id); // Return the new post id. return $new_post_id; } /** * Deletes all layout data and asset cache for a post. * * @since 1.0 * @param int $post_id The post ID to delete data and cache for. * @return void */ static public function delete_post( $post_id ) { // If this is a global template, unlink it from other posts. self::unlink_global_node_template_from_all_posts( $post_id ); // Delete all published and draft data. self::delete_layout_data( 'published', $post_id ); self::delete_layout_data( 'draft', $post_id ); // Delete all css and js. self::delete_all_asset_cache( $post_id ); } /** * Save a revision of a builder layout. * * @since 1.0 * @param int $post_id * @return void */ static public function save_revision($post_id) { $parent_id = wp_is_post_revision($post_id); if($parent_id) { $parent = get_post($parent_id); $data = self::get_layout_data('published', $parent->ID); $settings = self::get_layout_settings('published', $parent->ID); if(!empty($data)) { self::update_layout_data($data, 'published', $post_id); self::update_layout_settings($settings, 'published', $post_id); } } } /** * Restore a revision of a builder layout. * * @since 1.0 * @param int $post_id * @param int $revision_id * @return void */ static public function restore_revision($post_id, $revision_id) { $post = get_post($post_id); $revision = get_post($revision_id); if($revision) { $data = self::get_layout_data('published', $revision->ID); $settings = self::get_layout_settings('published', $revision->ID); if(!empty($data)) { self::update_layout_data($data, 'published', $post_id); self::update_layout_data($data, 'draft', $post_id); self::update_layout_settings($settings, 'published', $post_id); self::update_layout_settings($settings, 'draft', $post_id); } else { self::delete_layout_data('published', $post_id); self::delete_layout_data('draft', $post_id); self::delete_layout_settings('published', $post_id); self::delete_layout_settings('draft', $post_id); } self::delete_all_asset_cache( $post_id ); } } /** * Get all of the layout data for a post. We use get_metadata * here instead of get_post_meta to ensure revisions are queried accordingly. * * @since 1.0 * @param string $status Either published or draft. * @param int $post_id The ID of the post to get data for. * @return array */ static public function get_layout_data($status = null, $post_id = null) { $post_id = !$post_id ? self::get_post_id() : $post_id; $status = !$status ? self::get_node_status() : $status; // Get published data? if($status == 'published') { if(isset(self::$published_layout_data[$post_id])) { $data = self::$published_layout_data[$post_id]; } else { $data = get_metadata('post', $post_id, '_fl_builder_data', true); self::$published_layout_data[$post_id] = self::clean_layout_data( $data ); } } // Get draft data? else if($status == 'draft') { if(isset(self::$draft_layout_data[$post_id])) { $data = self::$draft_layout_data[$post_id]; } else { $data = get_metadata('post', $post_id, '_fl_builder_draft', true); self::$draft_layout_data[$post_id] = self::clean_layout_data( $data ); } } // Make sure we have an array. if(empty($data)) { $data = array(); } // Clone the layout data to ensure the cache remains intact. foreach($data as $node_id => $node) { $data[$node_id] = clone $node; } // Return the data. return apply_filters( 'fl_builder_layout_data', $data, $status, $post_id ); } /** * Update the layout data for a post. We use update_metadata * here instead of update_post_meta to ensure revisions are updated accordingly. * * @since 1.0 * @param array $data The layout data to update. * @param string $status Either published or draft. * @param int $post_id The ID of the post to update. * @return void */ static public function update_layout_data($data, $status = null, $post_id = null) { $post_id = !$post_id ? self::get_post_id() : $post_id; $status = !$status ? self::get_node_status() : $status; $key = 'published' == $status ? '_fl_builder_data' : '_fl_builder_draft'; $raw_data = get_metadata( 'post', $post_id, $key ); $data = self::slash_settings( self::clean_layout_data( $data ) ); // Update the data. if ( 0 === count( $raw_data ) ) { add_metadata( 'post', $post_id, $key, $data ); } else { update_metadata( 'post', $post_id, $key, $data ); } // Cache the data. if($status == 'published') { self::$published_layout_data[$post_id] = $data; } else if($status == 'draft') { self::$draft_layout_data[$post_id] = $data; } } /** * Delete the layout data for a post. * * @since 1.0 * @param string $status Either published or draft. * @param int $post_id The ID of the post to delete data. * @return void */ static public function delete_layout_data($status = null, $post_id = null) { // Make sure we have a status to delete. if(!$status) { return; } // Get the post id. $post_id = !$post_id ? self::get_post_id() : $post_id; // Get the data to delete. $data = self::get_layout_data($status, $post_id); // Delete the nodes. foreach($data as $node) { self::call_module_delete($node); } // Update the layout data. self::update_layout_data(array(), $status, $post_id); } /** * Ensures the integrity of layout data key/value pairs. * * Also makes sure we're not serializing any FLBuilderModule * instances because those are too big and bloat the data array. * * @since 1.0 * @param array $data An array of layout data. * @return array */ static public function clean_layout_data( $data = array() ) { $cleaned = array(); if ( is_array( $data ) ) { foreach ( $data as $node ) { if ( is_object( $node ) && isset( $node->node ) ) { if ( is_a( $node, 'FLBuilderModule' ) ) { $cleaned[ $node->node ] = new StdClass(); $cleaned[ $node->node ]->node = $node->node; $cleaned[ $node->node ]->type = $node->type; $cleaned[ $node->node ]->parent = $node->parent; $cleaned[ $node->node ]->position = $node->position; $cleaned[ $node->node ]->settings = $node->settings; } else { $cleaned[ $node->node ] = $node; } } } } return $cleaned; } /** * Get the builder settings for a layout. * * @since 1.7 * @param string $status Either published or draft. * @param int $post_id The ID of the post to get settings for. * @return object */ static public function get_layout_settings( $status = null, $post_id = null ) { $status = ! $status ? self::get_node_status() : $status; $post_id = ! $post_id ? self::get_post_id() : $post_id; $key = 'published' == $status ? '_fl_builder_data_settings' : '_fl_builder_draft_settings'; $settings = get_metadata( 'post', $post_id, $key, true ); $defaults = self::get_settings_form_defaults( 'layout' ); if ( ! $settings ) { $settings = new StdClass(); } $settings = (object)array_merge( (array)$defaults, (array)$settings ); return apply_filters( 'fl_builder_layout_settings', $settings, $status, $post_id ); } /** * Updates the layout settings for a post. * * @since 1.7 * @param array $settings The new layout settings. * @param string $status Either published or draft. * @param int $post_id The ID of the post to update. * @return object */ static public function update_layout_settings( $settings = array(), $status = null, $post_id = null ) { $status = ! $status ? self::get_node_status() : $status; $post_id = ! $post_id ? self::get_post_id() : $post_id; $key = 'published' == $status ? '_fl_builder_data_settings' : '_fl_builder_draft_settings'; $raw_settings = get_metadata( 'post', $post_id, $key ); $old_settings = self::get_layout_settings( $status, $post_id ); $new_settings = (object)array_merge( (array)$old_settings, (array)$settings ); if ( 0 === count( $raw_settings ) ) { add_metadata( 'post', $post_id, $key, self::slash_settings( $new_settings ) ); } else { update_metadata( 'post', $post_id, $key, self::slash_settings( $new_settings ) ); } return $new_settings; } /** * Called via AJAX to save the layout settings. * * @since 1.7 * @param array $settings The new layout settings. * @param string $status Either published or draft. * @param int $post_id The ID of the post to update. * @return object */ static public function save_layout_settings( $settings = array(), $status = null, $post_id = null ) { return self::update_layout_settings( $settings, $status, $post_id ); } /** * Delete the layout settings for a post. * * @since 1.7 * @param string $status Either published or draft. * @param int $post_id The ID of a post whose settings to delete. * @return void */ static public function delete_layout_settings( $status = null, $post_id = null ) { $status = ! $status ? self::get_node_status() : $status; $post_id = ! $post_id ? self::get_post_id() : $post_id; $key = 'published' == $status ? '_fl_builder_data_settings' : '_fl_builder_draft_settings'; update_metadata( 'post', $post_id, $key, array() ); } /** * Merge two sets of layout settings together. * * @since 1.7 * @param object $settings The layout settings to merge into. * @param object $merge_settings The layout settings to merge. * @return object */ static public function merge_layout_settings( $settings, $merge_settings ) { $keys = array( 'css', 'js' ); foreach ( $keys as $key ) { if ( empty( $merge_settings->{$key} ) ) { continue; } else if ( strstr( $settings->{$key}, $merge_settings->{$key} ) ) { continue; } else { if ( ! empty( $settings->{$key} ) ) { $settings->{$key} .= "\n"; } $settings->{$key} .= $merge_settings->{$key}; } } return $settings; } /** * Clears a draft layout and saves a new draft using * the currently published layout data. * * @since 1.0 * @return void */ static public function clear_draft_layout() { $post_id = self::get_post_id(); $data = self::get_layout_data('published', $post_id); $settings = self::get_layout_settings('published', $post_id); // Delete the old draft layout. self::delete_layout_data('draft'); // Save the new draft layout. self::update_layout_data($data, 'draft', $post_id); // Save the new draft layout settings. self::update_layout_settings($settings, 'draft', $post_id); // Clear the asset cache. self::delete_all_asset_cache($post_id); } /** * Saves layout data when a user chooses to publish. * * @since 1.0 * @param bool $publish Whether to publish the parent post or not. * @return void */ static public function save_layout( $publish = true ) { $editor_content = FLBuilder::render_editor_content(); $post_id = self::get_post_id(); $data = self::get_layout_data('draft', $post_id); $settings = self::get_layout_settings('draft', $post_id); // Fire the before action. do_action( 'fl_builder_before_save_layout', $post_id, $publish, $data, $settings ); // Delete the old published layout. self::delete_layout_data('published', $post_id); self::delete_layout_settings('published', $post_id); // Save the new published layout. self::update_layout_data($data, 'published', $post_id); self::update_layout_settings($settings, 'published', $post_id); // Clear the asset cache. self::delete_all_asset_cache($post_id); self::delete_node_template_asset_cache($post_id); // Enable the builder to take over the post content. self::enable(); // Get the post status. $post_status = get_post_status($post_id); // Publish the post? if ( $publish ) { $is_draft = strstr($post_status, 'draft'); $is_pending = strstr($post_status, 'pending'); if ( current_user_can( 'publish_posts' ) ) { $post_status = $is_draft || $is_pending ? 'publish' : $post_status; } else if( $is_draft ) { $post_status = 'pending'; } } // Update the post with stripped down content. wp_update_post(array( 'ID' => self::get_post_id(), 'post_status' => $post_status, 'post_content' => $editor_content )); // Fire the after action. do_action( 'fl_builder_after_save_layout', $post_id, $publish, $data, $settings ); } /** * Publishes the current builder layout only if the parent post * is still a draft. The layout will be published but the parent * post will remain a draft so the post can be scheduled and the * layout can be viewed while the builder is not active. If the * parent post is already published, nothing happens. * * @since 1.6.1 * @return void */ static public function save_draft() { $post_id = self::get_post_id(); $post_status = get_post_status( $post_id ); if ( strstr( $post_status, 'draft' ) ) { self::save_layout( false ); } } /** * Duplicates a layout for WPML when the copy from original * button has been clicked. * * @since 1.1.7 * @param int $original_post_id * @param int $new_post_id * @return array */ static public function duplicate_wpml_layout($original_post_id = null, $new_post_id = null) { $post_data = self::get_post_data(); $original_post_id = isset($post_data['original_post_id']) ? $post_data['original_post_id'] : $original_post_id; $new_post_id = isset($post_data['post_id']) ? $post_data['post_id'] : $new_post_id; $enabled = get_post_meta($original_post_id, '_fl_builder_enabled', true); $published = self::get_layout_data('published', $original_post_id); $draft = self::get_layout_data('draft', $original_post_id); $response = array( 'enabled' => false, 'has_layout' => false ); if(!empty($enabled)) { update_post_meta($new_post_id, '_fl_builder_enabled', true); $response['enabled'] = true; } if(!empty($published)) { self::update_layout_data($published, 'published', $new_post_id); $response['has_layout'] = true; } if(!empty($draft)) { self::update_layout_data($draft, 'draft', $new_post_id); $response['has_layout'] = true; } return $response; } /** * Returns the type of templates that are enabled. * * @since 1.1.3 * @return string */ static public function get_enabled_templates() { $value = self::get_admin_settings_option( '_fl_builder_enabled_templates', true ); return ! $value ? 'enabled' : $value; } /** * Returns whether the user templates admin UI is enabled. * * @since 1.5.7 * @return string */ static public function user_templates_admin_enabled() { $value = self::get_admin_settings_option( '_fl_builder_user_templates_admin', true ); return ! $value ? 0 : $value; } /** * Checks to see if the current post is a user template. * * @since 1.6.3 * @param string $type The type of user template to check for. * @return bool */ static public function is_post_user_template( $type = null ) { $post = FLBuilderModel::get_post(); if ( ! $post ) { return false; } else if ( 'fl-builder-template' == $post->post_type ) { if ( null === $type ) { return true; } else { $saved_type = self::get_user_template_type( $post->ID ); if ( $saved_type == $type ) { return true; } } } return false; } /** * Saves a user defined template via AJAX. * * @since 1.1.3 * @return void */ static public function save_user_template( $settings = array() ) { // Save the user template post. $post_id = wp_insert_post(array( 'post_title' => $settings['name'], 'post_type' => 'fl-builder-template', 'post_status' => 'publish', 'ping_status' => 'closed', 'comment_status' => 'closed' )); // Set the template type. wp_set_post_terms( $post_id, 'layout', 'fl-builder-template-type' ); // Get the layout data and settings to copy. $data = self::get_layout_data(); $settings = self::get_layout_settings(); // Generate new node ids. $data = self::generate_new_node_ids($data); // Save the template layout data and settings. self::update_layout_data($data, 'published', $post_id); self::update_layout_settings($settings, 'published', $post_id); // Enable the builder for this template. update_post_meta($post_id, '_fl_builder_enabled', true); } /** * Returns data for all user defined templates. * * @since 1.1.3 * @since 1.5.7 Added support for template categories. * @param string $type The type of user template to return. * @return array */ static public function get_user_templates( $type = 'layout' ) { $categorized = array( 'uncategorized' => array( 'name' => _x( 'Uncategorized', 'Default user template category.', 'fl-builder' ), 'templates' => array() ) ); $posts = get_posts( array( 'post_type' => 'fl-builder-template', 'orderby' => 'menu_order title', 'order' => 'ASC', 'posts_per_page' => '-1', 'tax_query' => array( array( 'taxonomy' => 'fl-builder-template-type', 'field' => 'slug', 'terms' => $type ) ) ) ); $templates = array(); // Loop through templates posts and build the templates array. foreach( $posts as $post ) { if ( has_post_thumbnail( $post->ID ) ) { $image_data = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'medium' ); $image = $image_data[0]; } else { $image = FL_BUILDER_URL . 'img/templates/blank.jpg'; } $templates[] = array( 'id' => $post->ID, 'name' => $post->post_title, 'image' => $image, 'type' => 'user' ); } // Loop through templates and build the categorized array. foreach ( $templates as $template ) { $cats = wp_get_post_terms( $template['id'], 'fl-builder-template-category' ); if ( 0 === count( $cats ) || is_wp_error( $cats ) ) { $categorized['uncategorized']['templates'][] = $template; } else { foreach ( $cats as $cat ) { if ( ! isset( $categorized[ $cat->slug ] ) ) { $categorized[ $cat->slug ] = array( 'name' => $cat->name, 'templates' => array() ); } $categorized[ $cat->slug ]['templates'][] = $template; } } } // Unset the uncategorized cat if no templates. if ( 0 === count( $categorized['uncategorized']['templates'] ) ) { unset( $categorized['uncategorized'] ); } return array( 'templates' => $templates, 'categorized' => $categorized ); } /** * Returns the template type for a user template. * * @since 1.6.3 * @param int $template_id The post ID of the template. * @return string */ static public function get_user_template_type( $template_id = null ) { if ( $template_id && isset( self::$node_template_types[ $template_id ] ) ) { return self::$node_template_types[ $template_id ]; } $post = $template_id ? get_post( $template_id ) : FLBuilderModel::get_post(); if ( 'fl-builder-template' != $post->post_type ) { return ''; } else { $terms = wp_get_post_terms( $post->ID, 'fl-builder-template-type' ); $type = ( 0 === count( $terms ) ) ? 'layout' : $terms[0]->slug; self::$node_template_types[ $template_id ] = $type; return $type; } } /** * Delete a user defined template. * * @since 1.1.3 * @param int $template_id The post ID of the template to delete. * @return void */ static public function delete_user_template($template_id = null) { if(isset($template_id)) { wp_delete_post($template_id, true); } } /** * Apply a user defined template to the current layout. * * @since 1.1.3 * @param int|object $template The post ID of the template to apply or a template data object. * @param bool $append Whether to append the new template or replacing the existing layout. * @return void */ static public function apply_user_template($template = null, $append = false) { if($template) { // Delete existing nodes and settings? if(!$append) { self::delete_layout_data('draft'); self::delete_layout_settings('draft'); } // Insert new nodes if this is not a blank template. if($template != 'blank') { // Get the template data if $template is not an object. if ( ! is_object( $template ) ) { $template_id = $template; $template = new StdClass(); $template->nodes = self::get_layout_data('published', $template_id); $template->settings = self::get_layout_settings('published', $template_id); } // Get new ids for the template nodes. $template->nodes = self::generate_new_node_ids($template->nodes); // Get the existing layout data and settings. $layout_data = self::get_layout_data(); $layout_settings = self::get_layout_settings(); // Reposition rows if we are appending. if($append) { $row_position = self::next_node_position('row'); foreach($template->nodes as $node_id => $node) { if($node->type == 'row') { $template->nodes[$node_id]->position += $row_position; } } } // Merge the layout data and settings. $data = array_merge($layout_data, $template->nodes); $settings = self::merge_layout_settings( $layout_settings, $template->settings ); // Update the layout data and settings. self::update_layout_data($data); self::update_layout_settings( $settings ); } // Delete old asset cache. self::delete_asset_cache(); } } /** * Returns true if the node templates UI is enabled, false if not. * * @since 1.6.3 * @return bool */ static public function node_templates_enabled() { $enabled_templates = self::get_enabled_templates(); if ( true === FL_BUILDER_LITE ) { return false; } if ( 'core' == $enabled_templates || 'disabled' == $enabled_templates ) { return false; } return true; } /** * Checks to see if the current post is a node template. * * @since 1.6.3 * @param int $post_id If supplied, this post will be checked instead. * @return bool */ static public function is_post_node_template( $post_id = false ) { $post_id = $post_id ? $post_id : self::get_post_id(); $post = get_post( $post_id ); if ( ! $post ) { return false; } else if ( 'fl-builder-template' == $post->post_type ) { $saved_type = self::get_user_template_type( $post->ID ); if ( in_array( $saved_type, array( 'row', 'module' ) ) ) { return true; } } return false; } /** * Checks to see if the current post is a global node template. * * @since 1.6.3 * @param int $post_id If supplied, this post will be checked instead. * @return bool */ static public function is_post_global_node_template( $post_id = false ) { $post_id = $post_id ? $post_id : self::get_post_id(); if ( ! self::is_post_node_template( $post_id ) ) { return false; } $global = get_post_meta( $post_id, '_fl_builder_template_global', true ); if ( ! $global ) { return false; } return true; } /** * Checks to see if a node is a global node. * * @since 1.6.3 * @param object $node The node object to check. * @return bool|int */ static public function is_node_global( $node ) { if ( ! isset( $node->template_id ) ) { return false; } return self::get_node_template_post_id( $node->template_id ); } /** * Check the visibility settings that has been sets from any type of node (rows/columns/modules) * This will be applied ONLY when the builder is not active. * * @param object $node The type of object to check * @return bool */ static public function is_node_visible($node) { $is_visible = true; if ( self::is_builder_active() ) { return $is_visible; } if ( isset( $node->settings->visibility_display ) && ('' != $node->settings->visibility_display) ) { // For logged out users if ( $node->settings->visibility_display == 'logged_out' && ! is_user_logged_in() ) { $is_visible = true; } // For logged in users else if ( $node->settings->visibility_display == 'logged_in' && is_user_logged_in() ) { $is_visible = true; // User capability setting if ( isset($node->settings->visibility_user_capability) && ! empty($node->settings->visibility_user_capability) ) { if (self::current_user_has_capability( trim( $node->settings->visibility_user_capability ) )) { $is_visible = true; } else { $is_visible = false; } } } // Never else if ( $node->settings->visibility_display == 0 ) { $is_visible = false; } else { $is_visible = false; } } return apply_filters( 'fl_builder_is_node_visible', $is_visible, $node ); } /** * Checks to see if a node is the root node of a global template. * * @since 1.6.3 * @param object $node The node object to check. * @return bool|int */ static public function is_node_template_root( $node ) { return self::is_node_global( $node ) && isset( $node->template_root_node ); } /** * Get an array of node template info. * * @since 1.6.3 * @param string $type The type of node template to get. * @return array */ static public function get_node_templates( $type = '' ) { $posts = get_posts( array( 'post_type' => 'fl-builder-template', 'orderby' => 'title', 'order' => 'ASC', 'posts_per_page' => '-1', 'tax_query' => array( array( 'taxonomy' => 'fl-builder-template-type', 'field' => 'slug', 'terms' => $type ) ) ) ); $templates = array(); foreach ( $posts as $post ) { $templates[] = array( 'id' => get_post_meta( $post->ID, '_fl_builder_template_id', true ), 'global' => get_post_meta( $post->ID, '_fl_builder_template_global', true ), 'link' => add_query_arg( 'fl_builder', '', get_permalink( $post->ID ) ), 'name' => $post->post_title ); } return $templates; } /** * Returns the root node for a node template. * * @since 1.6.3 * @param string $type The type of node template. * @param array $nodes The node template data. * @return object */ static public function get_node_template_root( $type = '', $nodes = array() ) { foreach ( $nodes as $node ) { if ( $type == $node->type ) { return $node; } } return false; } /** * Uses a node template ID to retrieve its post ID. * * @since 1.6.3 * @param string $template_id The node template ID as stored in the template's post meta. * @return int */ static public function get_node_template_post_id( $template_id ) { if ( isset( self::$node_template_post_ids[ $template_id ] ) ) { return self::$node_template_post_ids[ $template_id ]; } else { $posts = get_posts( array( 'post_type' => 'fl-builder-template', 'post_status' => array( 'any', 'trash' ), 'posts_per_page' => '-1', 'meta_key' => '_fl_builder_template_id', 'meta_value' => $template_id ) ); if ( 0 === count( $posts ) ) { return false; } self::$node_template_post_ids[ $template_id ] = $posts[ 0 ]->ID; return $posts[ 0 ]->ID; } } /** * Returns the edit URL for a node template. * * @since 1.6.3 * @param string $template_id The node template ID as stored in the template's post meta. * @return string */ static public function get_node_template_edit_url( $template_id ) { return self::get_edit_url( self::get_node_template_post_id( $template_id ) ); } /** * Returns an array of posts that have the global node template * with the specified ID. * * @since 1.6.3 * @param int $post_id The post ID of the global node template. * @return array */ static public function get_posts_with_global_node_template( $post_id = false ) { $posts = array(); if ( self::is_post_global_node_template( $post_id ) ) { $template_id = get_post_meta( $post_id, '_fl_builder_template_id', true ); $query = new WP_Query( array( 'meta_query' => array( 'relation' => 'OR', array( 'key' => '_fl_builder_data', 'value' => $template_id, 'compare' => 'LIKE' ), array( 'key' => '_fl_builder_draft', 'value' => $template_id, 'compare' => 'LIKE' ) ), 'post_type' => 'any', 'post_status' => 'any', 'post__not_in' => array( $post_id ) ) ); $posts = $query->posts; } return $posts; } /** * Saves a node template. * * @since 1.6.3 * @param string $template_node_id The ID of the node to save as a template. * @param string $settings The settings for this template. * @return void */ static public function save_node_template( $template_node_id, $settings ) { $root_node = self::get_node( $template_node_id ); $nodes = self::get_nested_nodes( $template_node_id ); $template_id = self::generate_node_id(); $original_parent = $root_node->parent; $original_position = $root_node->position; // Save the node template post. $post_id = wp_insert_post( array( 'post_title' => $settings['name'], 'post_type' => 'fl-builder-template', 'post_status' => 'publish', 'ping_status' => 'closed', 'comment_status' => 'closed' ) ); // Set the template type. wp_set_post_terms( $post_id, $root_node->type, 'fl-builder-template-type' ); // Reset the root node's position. $root_node->position = 0; // Add the root node to the nodes array. $nodes[ $root_node->node ] = $root_node; // Generate new node ids. $nodes = self::generate_new_node_ids( $nodes ); // Get the root node from the template data since its ID changed. $root_node = self::get_node_template_root( $root_node->type, $nodes ); // Add the template ID and template node ID for global templates. if ( $settings['global'] ) { foreach ( $nodes as $node_id => $node ) { $nodes[ $node_id ]->template_id = $template_id; $nodes[ $node_id ]->template_node_id = $node_id; if ( $node_id == $root_node->node ) { $nodes[ $node_id ]->template_root_node = true; } else if ( isset( $nodes[ $node_id ]->template_root_node ) ) { unset( $nodes[ $node_id ]->template_root_node ); } } } // We need to remove the template ID and template node ID for standard templates. else { foreach ( $nodes as $node_id => $node ) { if ( isset( $nodes[ $node_id ]->template_id ) ) { unset( $nodes[ $node_id ]->template_id ); } if ( isset( $nodes[ $node_id ]->template_node_id ) ) { unset( $nodes[ $node_id ]->template_node_id ); } if ( isset( $nodes[ $node_id ]->template_root_node ) ) { unset( $nodes[ $node_id ]->template_root_node ); } } } // Save the template layout data. self::update_layout_data( $nodes, 'published', $post_id ); self::update_layout_data( $nodes, 'draft', $post_id ); // Enable the builder for this template. update_post_meta( $post_id, '_fl_builder_enabled', true ); // Add the template ID post meta. We use a custom ID for node // templates in case templates are imported since their WordPress // IDs will change, breaking global templates. update_post_meta( $post_id, '_fl_builder_template_id', $template_id ); // Add the template global flag post meta. update_post_meta( $post_id, '_fl_builder_template_global', $settings['global'] ); // Delete the existing node and apply the template for global templates. if ( $settings['global'] ) { // Delete the existing node. self::delete_node( $template_node_id ); // Apply the global template. $root_node = self::apply_node_template( $template_id, $original_parent, $original_position ); } // Return an array of template settings. return array( 'id' => $template_id, 'global' => $settings['global'] ? true : false, 'link' => add_query_arg( 'fl_builder', '', get_permalink( $post_id ) ), 'name' => $settings['name'], 'type' => $root_node->type, 'layout' => $settings['global'] ? FLBuilderAJAXLayout::render( $root_node->node, $template_node_id ) : null ); } /** * Sets the default type for a node template when created in wp-admin. * * @since 1.6.3 * @param int $post_ID The post ID for the template. * @param object $post The post object for the template. * @param bool $update Whether this is a new post or an update. * @return void */ static public function set_node_template_default_type( $post_id, $post, $update ) { if ( $update || 'fl-builder-template' != $post->post_type ) { return; } $type = wp_get_post_terms( $post_id, 'fl-builder-template-type' ); if ( 0 === count( $type ) ) { wp_set_post_terms( $post_id, 'layout', 'fl-builder-template-type' ); } } /** * Deletes a node template via AJAX. * * @since 1.6.3 * @param string $template_id The ID of node template to delete. * @return void */ static public function delete_node_template( $template_id ) { // Make sure we have a template ID. if ( ! isset( $template_id ) ) { return; } // Get the post ID for the template. $template_post_id = self::get_node_template_post_id( $template_id ); // Bail if we don't have a post ID. if ( ! $template_post_id ) { return; } // Unlink if this is a global template. self::unlink_global_node_template_from_all_posts( $template_post_id ); // Delete the template post. wp_delete_post( $template_post_id, true ); } /** * Unlinks all instances of a global node template in all posts. * * @since 1.6.3 * @param int $template_post_id The post ID of the template to unlink. * @return void */ static public function unlink_global_node_template_from_all_posts( $template_post_id ) { if ( self::is_post_global_node_template( $template_post_id ) ) { $posts = self::get_posts_with_global_node_template( $template_post_id ); $template_id = get_post_meta( $template_post_id, '_fl_builder_template_id', true ); foreach ( $posts as $post ) { self::unlink_global_node_template_from_post( 'published', $post->ID, $template_post_id, $template_id ); self::unlink_global_node_template_from_post( 'draft', $post->ID, $template_post_id, $template_id ); self::delete_all_asset_cache( $post->ID ); } } } /** * Unlinks all instances of a global node template from a post's * layout data with the specified status. Since only the root node * of a global template is saved to a posts layout data, the child * nodes will be saved to the post when the global template is unlinked. * * @since 1.6.3 * @param string $status The status of the layout data. Either draft or published. * @param int $post_id The ID of the post to unlink from. * @param string $template_post_id The post ID of the template to unlink from the layout data. * @param string $template_id The ID of the template to unlink from the layout data. * @return void */ static public function unlink_global_node_template_from_post( $status, $post_id, $template_post_id, $template_id ) { $template_data = self::get_layout_data( $status, $template_post_id ); $layout_data = self::get_layout_data( $status, $post_id ); $update = false; // Loop through the layout data. foreach ( $layout_data as $node_id => $node ) { // Check to see if this is the global template node to unlink. if ( isset( $node->template_id ) && $node->template_id == $template_id ) { // Generate new node ids for the template data. $new_data = self::generate_new_node_ids( $template_data ); // Get the root node from the template data. $root_node = self::get_node_template_root( $node->type, $new_data ); // Remove the root node from the template data since it's already in the layout. unset( $new_data[ $root_node->node ] ); // Update the settings for the root node in this layout. $layout_data[ $node_id ]->settings = $root_node->settings; // Update children with the new parent node ID. foreach ( $new_data as $i => $n ) { if ( $n->parent == $root_node->node ) { $new_data[ $i ]->parent = $node->node; } } // Add the template data to the layout data. $layout_data = array_merge( $layout_data, $new_data ); // Set the update flag. $update = true; } } // Only update if we need to. if ( $update ) { // Remove template info from the layout data. foreach ( $layout_data as $node_id => $node ) { unset( $layout_data[ $node_id ]->template_id ); unset( $layout_data[ $node_id ]->template_post_id ); unset( $layout_data[ $node_id ]->template_root_node ); } // Update the layout data. self::update_layout_data( $layout_data, $status, $post_id ); } } /** * Deletes all instances of a global node template from all posts. * * @since 1.6.3 * @param int $template_post_id The post ID of the template to delete. * @return void */ static public function delete_global_node_template_from_all_posts( $template_post_id ) { if ( self::is_post_global_node_template( $template_post_id ) ) { $posts = self::get_posts_with_global_node_template( $template_post_id ); $template_id = get_post_meta( $template_post_id, '_fl_builder_template_id', true ); foreach ( $posts as $post ) { self::delete_global_node_template_from_post( 'published', $post->ID, $template_id ); self::delete_global_node_template_from_post( 'draft', $post->ID, $template_id ); self::delete_all_asset_cache( $post->ID ); } } } /** * Deletes all instances of a global node template from a post's * layout data with the specified status. * * @since 1.6.3 * @param string $status The status of the layout data. Either draft or published. * @param int $post_id The ID of the post to delete from. * @param string $template_id The ID of the template to delete from the layout data. * @return void */ static public function delete_global_node_template_from_post( $status, $post_id, $template_id ) { $layout_data = self::get_layout_data( $status, $post_id ); $update = false; // Loop through the nodes. foreach ( $layout_data as $node_id => $node ) { $siblings = array(); $position = 0; // Check to see if this is the global template node to delete. if ( isset( $node->template_id ) && $node->template_id == $template_id ) { // Unset this node in the layout data. unset( $layout_data[ $node_id ] ); // Find sibiling nodes to update their position. foreach ( $layout_data as $i => $n ) { if ( $n->parent == $node->parent ) { $siblings[ $i ] = $n; } } // Sort the sibiling nodes by position. uasort( $siblings, array( 'FLBuilderModel', 'order_nodes' ) ); // Update sibiling node positions. foreach ( $siblings as $i => $n ) { $layout_data[ $i ]->position = $position; $position++; } // Set the update flag. $update = true; } } // Only update if we need to. if ( $update ) { self::update_layout_data( $layout_data, $status, $post_id ); } } /** * Applies a node template to the current layout. * * @since 1.6.3 * @param int $template_id The node template ID. * @param string $parent_id The new parent node ID for the template. * @param int $position The position of the template within the layout. * @param object $template Optional. Template data to use instead of pulling it with the template ID. * @return void */ static public function apply_node_template( $template_id = null, $parent_id = null, $position = 0, $template = null ) { $parent = $parent_id == 0 ? null : self::get_node( $parent_id ); $template_post_id = self::get_node_template_post_id( $template_id ); // Allow extensions to hook into applying a node template. $override = apply_filters( 'fl_builder_override_apply_node_template', false, array( 'template_id' => $template_id, 'parent_id' => $parent_id, 'position' => $position, 'template' => $template, 'template_post_id' => $template_post_id ) ); // Return if we got an override from the filter. if ( $override ) { return $override; } // Get the template data from $template if we have it. if ( is_object( $template ) ) { $template_data = $template->nodes; $template_settings = $template->settings; $type = $template->type; $global = $template->global; } // Get the template data. else { $template_data = self::get_layout_data( 'published', $template_post_id ); $template_settings = self::get_layout_settings( 'published', $template_post_id ); $type = self::get_user_template_type( $template_post_id ); $global = get_post_meta( $template_post_id, '_fl_builder_template_global', true ); } // Generate new node ids. $template_data = self::generate_new_node_ids( $template_data ); // Get the root node from the template data. $root_node = self::get_node_template_root( $type, $template_data ); // Add a new parent for module node templates if needed. if ( 'module' == $root_node->type && ( ! $parent || 'row' == $parent->type || 'column-group' == $parent->type ) ) { $parent_id = self::add_module_parent( $parent_id, $position ); $position = null; } // Update the root node's parent. $template_data[ $root_node->node ]->parent = ! $parent_id ? null : $parent_id; // Get the layout data and settings. $layout_data = self::get_layout_data( 'draft' ); $layout_settings = self::get_layout_settings( 'draft' ); // Only merge the root node for global templates. if ( $global ) { $layout_data[ $root_node->node ] = $template_data[ $root_node->node ]; } // Merge all template data and settings for standard templates. else { // Merge template data. foreach ( $template_data as $node_id => $node ) { unset( $template_data[ $node_id ]->template_id ); unset( $template_data[ $node_id ]->template_post_id ); unset( $template_data[ $node_id ]->template_root_node ); } $layout_data = array_merge( $layout_data, $template_data ); // Merge template settings. $layout_settings = self::merge_layout_settings( $layout_settings, $template_settings ); } // Update the layout data and settings. self::update_layout_data( $layout_data ); self::update_layout_settings( $layout_settings ); // Reorder the main template node. if ( null !== $position ) { self::reorder_node( $root_node->node, $position ); } // Delete old asset cache. self::delete_asset_cache(); // Return the root node. if ( 'module' == $root_node->type ) { return self::get_module( $root_node->node ); } else { return $root_node; } } /** * Registers a template data file with the builder. * * @since 1.8 * @param sting $path The directory path to the template data file. * @return void */ static public function register_templates( $path = false ) { if ( $path && file_exists( $path ) ) { self::$templates[] = $path; } } /** * Apply a core template. * * @since 1.0 * @since 1.5.7. Added logic for overriding core templates. * @param int $index The index of the template to apply. * @param bool $append Whether to append the new template or replacing the existing layout. * @return void */ static public function apply_template($index = 0, $append = false) { // Allow extensions to hook into applying a template. $override = apply_filters( 'fl_builder_override_apply_template', false, array( 'index' => $index, 'append' => $append ) ); // Return if we have an override from the filter. if ( $override ) { return; } // Apply a core template. $template = self::get_template($index); $row_position = self::next_node_position('row'); // Delete existing nodes and settings? if(!$append) { self::delete_layout_data('draft'); self::delete_layout_settings('draft'); } // Only move forward if we have template nodes. if(isset($template->nodes)) { // Get new ids for the template nodes. $template->nodes = self::generate_new_node_ids($template->nodes); // Get the existing layout data and settings. $layout_data = self::get_layout_data(); $layout_settings = self::get_layout_settings(); // Reposition rows? if($append) { foreach($template->nodes as $node_id => $node) { if($node->type == 'row') { $template->nodes[$node_id]->position += $row_position; } } } // Merge and update the layout data. $data = array_merge($layout_data, $template->nodes); self::update_layout_data($data); // Merge and update the layout settings. if ( isset( $template->settings ) ) { $settings = self::merge_layout_settings( $layout_settings, $template->settings ); self::update_layout_settings( $settings ); } } // Delete old asset cache. self::delete_asset_cache(); } /** * Returns data for a core template. * * @since 1.0 * @param int $index The index of the template. * @param string $type The type of template to get. Currently either layout, row or module. * @return object */ static public function get_template( $index, $type = 'layout' ) { $templates = self::get_templates( $type ); return isset( $templates[ $index ] ) ? $templates[ $index ] : false; } /** * Returns data for all core or third party templates. * * @since 1.0 * @param string $type Either layout, row or module * @return array */ static public function get_templates( $type = 'layout' ) { $templates = array(); foreach ( self::$templates as $path ) { if ( file_exists( $path ) ) { if ( stristr( $path, '.php' ) ) { ob_start(); include $path; $unserialized = unserialize( ob_get_clean() ); } else { $unserialized = unserialize( file_get_contents( $path ) ); } if ( is_array( $unserialized ) ) { if ( isset( $unserialized[ $type ] ) ) { $templates = array_merge( $templates, $unserialized[ $type ] ); } else if ( 'layout' == $type ) { $templates = array_merge( $templates, $unserialized ); } } } } return apply_filters( 'fl_builder_get_templates', $templates, $type ); } /** * Checks to see if any templates exist. * * @since 1.8 * @return bool */ static public function has_templates() { return apply_filters( 'fl_builder_has_templates', ( count( self::get_templates() ) > 0 ) ); } /** * Returns template data needed for the template selector. * Can also return data for row and module templates if * a template type is passed. * * @since 1.5.7 * @param string $type Either layout, row or module * @return array */ static public function get_template_selector_data( $type = 'layout' ) { $categorized = array(); $templates = array(); $core_categories = array( 'landing' => __( 'Landing Pages', 'fl-builder' ), 'company' => __( 'Content Pages', 'fl-builder' ) ); // Build the the templates array. foreach( self::get_templates( $type ) as $key => $template ) { if ( 'module' == $type ) { $node = array_shift( $template->nodes ); if ( ! isset( self::$modules[ $node->settings->type ] ) ) { continue; } } if ( strstr( $template->image, '://' ) ) { $image = $template->image; } else { $image = FL_BUILDER_URL . 'img/templates/' . ( empty( $template->image ) ? 'blank.jpg' : $template->image ); } $template_data = array( 'id' => $key, 'name' => $template->name, 'image' => $image, 'category' => isset( $template->category ) ? $template->category : $template->categories, 'type' => 'core' ); $template_data = apply_filters( 'fl_builder_template_selector_data', $template_data, $template ); $templates[] = $template_data; } // Build the categorized templates array. foreach( $templates as $template ) { if ( ! isset( $template['category'] ) ) { continue; } if ( is_array( $template['category'] ) ) { foreach ( $template['category'] as $cat_key => $cat_label ) { if ( ! isset( $categorized[ $cat_key ] ) ) { $categorized[ $cat_key ] = array( 'name' => $cat_label, 'templates' => array() ); } $categorized[ $cat_key ]['templates'][] = $template; } } else { if ( ! isset( $categorized[ $template['category'] ] ) ) { $categorized[ $template['category'] ] = array( 'name' => $core_categories[ $template['category'] ], 'templates' => array() ); } $categorized[ $template['category'] ]['templates'][] = $template; } } // Return both the templates and categorized templates array. return apply_filters( 'fl_builder_template_selector_data', array( 'templates' => $templates, 'categorized' => $categorized ), $type ); } /** * Returns data needed for the template selector's category filter. * * @since 1.8 * @return array */ static public function get_template_selector_filter_data() { $templates = self::get_template_selector_data(); $data = array(); foreach ( $templates['categorized'] as $slug => $category ) { $data[ $slug ] = $category['name']; } return apply_filters( 'fl_builder_template_selector_filter_data', $data ); } /** * Returns data for row templates to be shown in the UI panel. * * @since 1.8 * @return array */ static public function get_row_templates_data() { return apply_filters( 'fl_builder_row_templates_data', self::get_template_selector_data( 'row' ) ); } /** * Returns data for module templates to be shown in the UI panel. * * @since 1.8 * @return array */ static public function get_module_templates_data() { return apply_filters( 'fl_builder_module_templates_data', self::get_template_selector_data( 'module' ) ); } /** * Get color presets. * * @since 1.6.4 * @return object */ static public function get_color_presets() { $settings = get_option( '_fl_builder_color_presets', array() ); return apply_filters( 'fl_builder_color_presets', $settings ); } /** * Save color presets. * * @since 1.6.4 * @param array $presets The new color presets collection. * @return object */ static public function save_color_presets( $presets = array() ) { return update_option( '_fl_builder_color_presets', $presets ); } /** * Returns the custom branding string. * * @since 1.3.1 * @return string */ static public function get_branding() { if ( class_exists( 'FLBuilderWhiteLabel' ) ) { return FLBuilderWhiteLabel::get_branding(); } return __( 'Page Builder', 'fl-builder' ); } /** * Returns the custom branding icon URL. * * @since 1.3.7 * @return string */ static public function get_branding_icon() { if ( class_exists( 'FLBuilderWhiteLabel' ) ) { return FLBuilderWhiteLabel::get_branding_icon(); } return FL_BUILDER_URL . 'img/beaver.png'; } /** * Returns an array of slugs for all enabled icon sets. * * @since 1.4.6 * @return array */ static public function get_enabled_icons() { $value = self::get_admin_settings_option( '_fl_builder_enabled_icons', true ); return ! $value ? array( 'font-awesome', 'foundation-icons', 'dashicons' ) : $value; } /** * Returns the capability necessary for a user to access all * editing features in the builder interface. * * @since 1.3.9 * @return string */ static public function get_editing_capability() { $value = self::get_admin_settings_option( '_fl_builder_editing_capability', true ); return ! $value ? 'edit_posts' : $value; } /** * Checks to see if the current user has the capability necessary * to use the builders advanced editing features. * * @since 1.7 * @return bool */ static public function current_user_has_editing_capability() { $cap = self::get_editing_capability(); return self::current_user_has_capability($cap); } /** * Check if the current user has the specific capabilities * * @param string $cap The capability to evaluate if it's single or multiple (comma separated) value * @return bool */ static public function current_user_has_capability($cap) { if ( strstr( $cap, ',' ) ) { $parts = explode( ',', $cap ); foreach( $parts as $part ) { if ( current_user_can( trim( $part ) ) ) { return true; } } return false; } else { return current_user_can( $cap ); } } /** * Returns the capability necessary for a user to edit global templates. * * @since 1.6.3 * @return string */ static public function get_global_templates_editing_capability() { $value = self::get_admin_settings_option( '_fl_builder_global_templates_editing_capability', true ); return ! $value ? 'edit_posts' : $value; } /** * Returns the default settings for the builder's help button. * * @since 1.4.9 * @return array */ static public function get_help_button_defaults() { $defaults = array( 'enabled' => true, 'tour' => true, 'video' => true, 'video_embed' => '<iframe src="https://player.vimeo.com/video/124230072?autoplay=1" width="420" height="315" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>', 'knowledge_base' => true, 'knowledge_base_url' => self::get_store_url( 'knowledge-base', array( 'utm_medium' => ( true === FL_BUILDER_LITE ? 'bb-lite' : 'bb-pro' ), 'utm_source' => 'builder-ui', 'utm_campaign' => 'kb-help-button' ) ), 'forums' => true, 'forums_url' => self::get_store_url( 'knowledge-base', array( 'utm_medium' => ( true === FL_BUILDER_LITE ? 'bb-lite' : 'bb-pro' ), 'utm_source' => 'builder-ui', 'utm_campaign' => 'forums-help-button' ) ) ); return $defaults; } /** * Returns the settings for the builder's help button. * * @since 1.4.9 * @return array */ static public function get_help_button_settings() { if ( class_exists( 'FLBuilderWhiteLabel' ) ) { return FLBuilderWhiteLabel::get_help_button_settings(); } return self::get_help_button_defaults(); } /** * Returns an array of account data for all integrated services. * * @since 1.5.4 * @return array */ static public function get_services() { return get_option( '_fl_builder_services', array() ); } /** * Updates the account data for an integrated service. * * @since 1.5.4 * @param string $service The service id. * @param string $account The account name. * @param array $data The account data. * @return void */ static public function update_services( $service, $account, $data ) { $services = self::get_services(); $account = sanitize_text_field( $account ); if ( ! isset( $services[ $service ] ) ) { $services[ $service ] = array(); } $services[ $service ][ $account ] = $data; update_option( '_fl_builder_services', $services ); } /** * Deletes an account for an integrated service. * * @since 1.5.4 * @param string $service The service id. * @param string $account The account name. * @return void */ static public function delete_service_account( $service, $account ) { $services = self::get_services(); if ( isset( $services[ $service ][ $account ] ) ) { unset( $services[ $service ][ $account ] ); } if ( 0 === count( $services[ $service ] ) ) { unset( $services[ $service ] ); } update_option( '_fl_builder_services', $services ); } /** * Returns an option from the database for * the admin settings page. * * @since 1.5.7 * @param string $key The option key. * @param bool $network_override Whether to allow the network admin setting to be overridden on subsites. * @return mixed */ static public function get_admin_settings_option( $key, $network_override = true ) { // Get the site-wide option if we're in the network admin. if ( is_network_admin() ) { $value = get_site_option( $key ); } // Get the site-wide option if network overrides aren't allowed. else if ( ! $network_override && class_exists( 'FLBuilderMultisiteSettings' ) ) { $value = get_site_option( $key ); } // Network overrides are allowed. Return the subsite option if it exists. else if ( class_exists( 'FLBuilderMultisiteSettings' ) ) { $value = get_option( $key ); $value = false === $value ? get_site_option( $key ) : $value; } // This must be a single site install. Get the single site option. else { $value = get_option( $key ); } return $value; } /** * Updates an option from the admin settings page. * * @since 1.5.7 * @param string $key The option key. * @param mixed $value The value to update. * @param bool $network_override Whether to allow the network admin setting to be overridden on subsites. * @return mixed */ static public function update_admin_settings_option( $key, $value, $network_override = true ) { // Update the site-wide option since we're in the network admin. if ( is_network_admin() ) { update_site_option( $key, $value ); } // Delete the option if network overrides are allowed and the override checkbox isn't checked. else if ( $network_override && FLBuilderAdminSettings::multisite_support() && ! isset( $_POST['fl-override-ms'] ) ) { delete_option( $key ); } // Update the option for single install or subsite. else { update_option( $key, $value ); } } /** * Returns the plugin basename for Beaver Builder. * * @since 1.0 * @return string */ static public function plugin_basename() { return plugin_basename( FL_BUILDER_DIR . 'fl-builder.php' ); } /** * Deletes almost all database data and asset cache for the builder. * We don't delete _fl_builder_enabled, _fl_builder_data and _fl_builder_draft * so layouts can be recovered should the plugin be installed again. * * @since 1.0 * @return void */ static public function uninstall_database() { if(current_user_can('delete_plugins')) { // Delete builder options. delete_option('_fl_builder_settings'); delete_option('_fl_builder_enabled_modules'); delete_option('_fl_builder_enabled_templates'); delete_option('_fl_builder_user_templates_admin'); delete_option('_fl_builder_template_data_exporter'); delete_option('_fl_builder_templates_override'); delete_option('_fl_builder_templates_override_rows'); delete_option('_fl_builder_templates_override_modules'); delete_option('_fl_builder_post_types'); delete_option('_fl_builder_enabled_icons'); delete_option('_fl_builder_branding'); delete_option('_fl_builder_branding_icon'); delete_option('_fl_builder_theme_branding'); delete_option('_fl_builder_editing_capability'); delete_option('_fl_builder_global_templates_editing_capability'); delete_option('_fl_builder_help_button'); delete_option('_fl_builder_color_presets'); // Delete builder user meta. delete_metadata('user', 0, '_fl_builder_launched', 1, true); // Delete uploaded files and folders. $upload_dir = self::get_upload_dir(); $filesystem = FLBuilderUtils::get_filesystem(); $filesystem->rmdir( $upload_dir['path'], true ); // Deactivate and delete the plugin. if (!function_exists('deactivate_plugins')) { require_once(ABSPATH . 'wp-admin/includes/plugin.php'); } deactivate_plugins(array(self::plugin_basename()), false, is_network_admin()); delete_plugins(array(self::plugin_basename())); // Redirect to the plugins page. wp_redirect(admin_url('plugins.php?deleted=true&plugin_status=all&paged=1&s=')); exit; } } /** * @since 1.6.4.3 * @deprecated 1.8 */ static public function get_theme_branding() { _deprecated_function( __METHOD__, '1.8', 'FLBuilderWhiteLabel::get_theme_branding()' ); if ( class_exists( 'FLBuilderWhiteLabel' ) ) { return FLBuilderWhiteLabel::get_theme_branding(); } } /** * @since 1.0 * @deprecated 1.8 */ static public function save_templates( $templates = array() ) { _deprecated_function( __METHOD__, '1.8', 'FLBuilderCoreTemplatesAdmin::save_templates()' ); if ( class_exists( 'FLBuilderCoreTemplatesAdmin' ) ) { FLBuilderCoreTemplatesAdmin::save_templates( $templates ); } } /** * @since 1.0 * @deprecated 1.8 */ static public function save_template( $settings ) { _deprecated_function( __METHOD__, '1.8', 'FLBuilderCoreTemplatesAdmin::save_template()' ); if ( class_exists( 'FLBuilderCoreTemplatesAdmin' ) ) { FLBuilderCoreTemplatesAdmin::save_template( $settings ); } } /** * @since 1.0 * @deprecated 1.8 */ static public function update_template( $old_index, $settings ) { _deprecated_function( __METHOD__, '1.8', 'FLBuilderCoreTemplatesAdmin::update_template()' ); if ( class_exists( 'FLBuilderCoreTemplatesAdmin' ) ) { FLBuilderCoreTemplatesAdmin::update_template( $old_index, $settings ); } } /** * @since 1.0 * @deprecated 1.8 */ static public function delete_template( $index ) { _deprecated_function( __METHOD__, '1.8', 'FLBuilderCoreTemplatesAdmin::delete_template()' ); if ( class_exists( 'FLBuilderCoreTemplatesAdmin' ) ) { FLBuilderCoreTemplatesAdmin::delete_template( $index ); } } } FLBuilderModel::init();