: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Define the internationalization functionality
* Loads and defines the internationalization files for this plugin
* so that it is ready for translation.
* @link https://themify.me/
* @subpackage Tbp/includes
* Define the internationalization functionality.
* Loads and defines the internationalization files for this plugin
* so that it is ready for translation.
* @subpackage Tbp/includes
* @author Themify <themify@themify.me>
public static $post_type = 'tbp_theme';
private static $authorLink='https://themify.me';
private static $author='https://themify.me';
private static $defaults=array();
private static $api_base = 'https://themify.me/demo/themes/builder-pro-themes/wp-json/wp/v2/tbp_theme';
/* URL to Pro Theme demos */
private static $demo_base = 'https://themify.me/demo/themes/pro-';
public function __construct() {
self::$author = Tbp::get_plugin_name();
add_action( 'wp_ajax_'.self::$post_type.'_saving', array( __CLASS__, 'save_form' ) );
add_action( 'wp_ajax_'.self::$post_type.'_get_item', array( __CLASS__, 'get_item_data' ) );
add_action( 'admin_init', array( __CLASS__, 'actions' ),15 );
add_action( 'wp_ajax_'.self::$post_type.'_plupload', array( __CLASS__, 'import_theme_action' ) );
add_action( 'delete_post', array( __CLASS__, 'delete_associated_templates' ) );
add_action( 'rest_api_init', array( __CLASS__, 'register_rest_fields' ) );
'tbp_theme_name' => __('New Theme', 'tbp'),
'tbp_theme_description' => '',
'tbp_theme_version' => '1.0.0',
'tbp_theme_screenshot' => '',
'tbp_theme_screenshot_id' => '',
add_action( 'admin_footer', array( __CLASS__, 'enqueue_scripts' ) );
add_filter( 'tbp_theme_export_templates_data', array( 'Tbp_Templates', 'filter_export_template_data' ), 10, 2);
public static function get_options(){
'label' => __('Theme Name', 'tbp'),
if(current_user_can('upload_files') ){
$max_upload_size = (int) wp_max_upload_size() / ( 1024 * 1024 );
'id'=>'tbp_theme_screenshot',
'label' => __('Thumbnail', 'tbp'),
'description'=>sprintf( __( 'Maximum upload file size: %d MB.', 'tbp' ), $max_upload_size )
return apply_filters( 'tbp_theme_fields',$args);
public static function prepare_themes_for_js( $themes = null ) {
$current_theme = Tbp::get_active_theme()->post_name;
* Filter theme data before it is prepared for JavaScript.
* Passing a non-empty array will result in prepare_themes_for_js() returning
* early with that value instead.
* @param array $prepared_themes An associative array of theme data. Default empty array.
* @param null|array $themes An array of tbp_theme objects to prepare, if any.
* @param string $current_theme The current theme slug.
$prepared_themes = (array) apply_filters( 'pre_prepare_tbp_themes_for_js', array(), $themes, $current_theme );
if ( ! empty( $prepared_themes ) ) {
// Make sure the current theme is listed first.
if ( '' !== $current_theme ){
$prepared_themes[ $current_theme ] = array();
if ( null === $themes ) {
'post_type' => self::$post_type,
'ignore_sticky_posts'=>true,
$query = new WP_Query( $args );
$themes = $query->get_posts();
$url = menu_page_url(self::$post_type,false);
$actions = array('activate','deactivate','export','delete');
foreach ( $themes as $theme ) {
$slug = $theme->post_name;
$metadata = wp_parse_args( get_post_meta( $theme->ID, 'theme_info', true ), self::$defaults );
$prepared_themes[ $slug ] = array(
'theme_id' => $theme->ID,
'name' => $theme->post_title,
'screenshot' => array( get_the_post_thumbnail_url( $theme->ID ) ), // @todo multiple
'description' => empty( $metadata['tbp_theme_description'] ) ? '' : $metadata['tbp_theme_description'],
'author' => self::$author,
'authorAndUri' => sprintf( '<a href="%s">%s</a>', self::$authorLink, self::$author ),
'version' => $metadata['tbp_theme_version'],
'active' => $slug === $current_theme,
'hasUpdate' => isset( $updates[ $slug ] ),
foreach($actions as $act){
$item_actions[$act] = wp_nonce_url(add_query_arg(array('action'=>$act,'p'=>$theme->ID),$url), self::$post_type.'_nonce' );
$prepared_themes[ $slug ]['actions'] = $item_actions;
* Filter the themes prepared for JavaScript.
* Could be useful for changing the order, which is by name by default.
* @param array $prepared_themes Array of themes.
$prepared_themes = array_values(apply_filters( 'tbp_prepare_themes_for_js', $prepared_themes ));
return array_filter( $prepared_themes );
public static function render_page() {
include_once TBP_DIR . 'admin/partials/tbp-admin-theme-page.php';
* Save form post data via Hooks
* @param array $post_data
public static function save_form( $post_data ) {
if(!empty($_POST['type']) && $_POST['type']===self::$post_type){
check_ajax_referer('tb_load_nonce', 'tb_load_nonce');
$post_data = $_POST['data'];
$post_data = wp_parse_args( $post_data, self::$defaults );
$post_status = !empty($post_data['is_draft'])?'draft':'publish';
$isNew = empty($_POST['id']);
$id = $isNew===false?(int)$_POST['id']:null;
'post_title' => sanitize_text_field( $post_data['tbp_theme_name'] ),
'post_type' => self::$post_type,
'menu_order' => !empty($post_data['menu_order'])?$post_data['menu_order']:0
$args['post_status'] = 'publish';
unset($args['post_type']);
$args['post_status'] = $post_status;
$args['post_name'] = str_replace('-', '_', sanitize_title( $args['post_title'] ) );
$id = wp_insert_post( $args );
if(! is_wp_error( $id )){
if (isset($post_data['import']) && 'blank' !== $post_data['import'] && '' !== $post_data['import'] ) {
unset( $post_data['tbp_theme_name'] );
if ( !empty($post_data['tbp_theme_screenshot']) && !empty($post_data['tbp_theme_screenshot_id'] )) {
set_post_thumbnail( $id, $post_data['tbp_theme_screenshot_id'] );
if ( ! isset( $metainfo ) ){
update_post_meta( $id, 'theme_info', self::removeEmpty($post_data ));
if ( 'publish' === $post_status && Tbp::get_active_theme()->ID != $id ) {
self::set_active_theme($id);
$resp['redirect'] = admin_url( 'admin.php?page=' . self::$post_type . '&status=activate&id=' . $id );
echo json_encode( $resp );
private static function removeEmpty(array $arr){
* Activate/Deactivate Theme action.
public static function actions() {
if(isset($_GET['p'], $_GET['action'],$_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], self::$post_type.'_nonce') ){
$action = $_GET['action'];
$url = menu_page_url(self::$post_type,false);
$post_id = (int) $_GET['p'];
if($action==='deactivate'){
self::set_active_theme($post_id);
$url = add_query_arg( array( 'status' => $action ), $url );
if(!self::export_theme_bulk(array($post_id))){
wp_redirect( admin_url( 'edit.php?post_type=' . self::$post_type ) );
wp_delete_post( $post_id, true );
public static function set_active_theme( $post_id ){
Tbp_Utils::set_active_theme( $post_id );
private static function export_theme_bulk( $pIds ) {
$data = array('import' => 'Pro_Themes', 'content' => array());
foreach ( $pIds as $pId ) {
$theme = get_post( $pId );
'title' => get_the_title( $theme ),
'theme_info' => get_post_meta( $pId, 'theme_info', true ),
'post_type' =>Tbp_Templates::$post_type,
'ignore_sticky_posts'=>true,
'key' => 'tbp_associated_theme',
'value' => $theme->post_name,
$query = new WP_Query( $args );
$templates = $query->get_posts();
foreach( $templates as $template ) {
$builder_data = $ThemifyBuilder->get_builder_data( $template->ID );
$builder_data = Themify_Builder_Import_Export::prepare_builder_data( $builder_data );
'title' => get_the_title( $template->ID ),
'settings' => $builder_data,
$data_templates['tbp_associated_theme'] = $theme->post_name;
$data_themes['templates'][] = apply_filters( 'tbp_theme_export_templates_data', $data_templates, $template->ID );
$usedGS +=Themify_Global_Styles::used_global_styles( $template->ID );
$data['content'][] = $data_themes;
if ( ! function_exists( 'WP_Filesystem' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
$f = 'pro_theme_' . get_post_field( 'post_name', $pId ) . '_'.date('Y_m_d');
if(class_exists('ZipArchive')){
$datafile = 'export_file.txt';
$wp_filesystem->put_contents( $datafile, json_encode( $data ) );
$files_to_zip = array( $datafile );
// Export used global styles
if ( !empty( $usedGS ) ) {
foreach ( $usedGS as $gsID => $gsPost ) {
unset( $usedGS[ $gsID ]['id'],$usedGS[ $gsID ]['url'] );
$styling = Themify_Builder_Import_Export::prepare_builder_data( $gsPost['data'] );
if ( $gsPost['type'] === 'row' ) {
$styling = $styling['styling'];
} elseif ( $gsPost['type'] === 'column' ) {
$styling = $styling['cols'][0]['styling'];
$styling = $styling['cols'][0]['modules'][0]['mod_settings'];
$usedGS[ $gsID ]['data'] = $styling;
$gs_data = json_encode( $usedGS );
$gs_datafile = 'builder_gs_data_export.txt';
$wp_filesystem->put_contents( $gs_datafile, $gs_data, FS_CHMOD_FILE );
$files_to_zip[] = $gs_datafile;
$result = themify_create_zip( $files_to_zip, $file, true );
if ( ( isset( $file ) ) && ( $wp_filesystem->exists( $file ) ) ) {
header('Pragma: public');
header('Content-type: application/force-download');
header('Content-Disposition: attachment; filename="' . $file . '"');
header('Content-Transfer-Encoding: Binary');
header('Content-length: '.filesize($file));
header('Connection: close');
echo $wp_filesystem->get_contents( $file );
$wp_filesystem->delete( $datafile );
$wp_filesystem->delete( $file );
if ( ini_get( 'zlib.output_compression' ) ) {
ini_set( 'zlib.output_compression', 'Off' );
header('Content-Type: application/force-download');
header('Pragma: public');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private',false);
header('Content-Disposition: attachment; filename="'.$f.'.txt"');
header('Content-Transfer-Encoding: binary');
public static function import_theme_action() {
$imgid = $_POST['imgid'];
! empty( $_POST[ '_ajax_nonce' ] ) && check_ajax_referer($imgid . 'themify-plupload');
/** Handle file upload storing file|url|type. @var Array */
$file = wp_handle_upload($_FILES[$imgid . 'async-upload'], array('test_form' => true,'action' =>self::$post_type.'_plupload'));
// if $file returns error, return it and exit the function
if (! empty( $file['error'] ) ) {
//let's see if it's an image, a zip file or something else
$ext = explode('/', $file['type']);
if( 'zip' === $ext[1] || 'rar' === $ext[1] || 'plain' === $ext[1] ){
$url = wp_nonce_url('edit.php');
if (false === ($creds = request_filesystem_credentials($url) ) ) {
if ( ! WP_Filesystem($creds) ) {
request_filesystem_credentials($url, '', true);
$base_path = wp_upload_dir();
$base_path = trailingslashit( $base_path['path'] );
if( 'zip' === $ext[1] || 'rar' === $ext[1] ) {
unzip_file($file['file'], $base_path);
if( $wp_filesystem->exists( $base_path . 'export_file.txt' ) ) {
$data = $wp_filesystem->get_contents( $base_path . 'export_file.txt' );
$data = is_serialized($data) ? maybe_unserialize($data) : json_decode($data,true);
// Check for importing attached GS data
$gs_path = $base_path . 'builder_gs_data_export.txt';
if ( $wp_filesystem->exists( $gs_path ) ) {
$gs_data = $wp_filesystem->get_contents( $gs_path );
$gs_data = is_serialized( $gs_data ) ? maybe_unserialize( $gs_data ) : json_decode( $gs_data );
Themify_Global_Styles::builder_import( $gs_data );
$wp_filesystem->delete( $gs_path );
$wp_filesystem->delete($base_path . 'export_file.txt');
} elseif( $wp_filesystem->exists( $file['file'] ) ){
$data = $wp_filesystem->get_contents( $file['file'] );
$data = is_serialized($data) ? maybe_unserialize($data) : json_decode($data,true);
$result = self::process_import( $data );
if(!empty($result['err'])){
$file['error'] = $result['err'];
if(!empty($result['id']) && 0 !== (int)$result['id']){
$file['msg'] = __('Theme imported successfully. Would you like to activate the theme?','tbp');
$url = admin_url( 'admin.php?page=' . self::$post_type);
$url = add_query_arg( array(
'_wpnonce' => wp_create_nonce(self::$post_type.'_nonce'),
$wp_filesystem->delete($file['file']);
$file['error'] = __('Data could not be loaded', 'tbp');
// set thumb to true to trigger themify_plupload_selected event
// send the uploaded file url in response
private static function process_import($data){
if(!isset($data['import']) || !isset($data['content']) || !is_array($data['content'])){
$error = __('Incorrect Import File', 'tbp');