File "class-fl-builder-model.php"
Full Path: /www/wwwroot/shphe-en.com/wp-content/plugins/bb-plugin/classes/class-fl-builder-model.php
File size: 146.41 KB
MIME-type: --
Charset: utf-8
<?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();