$icon_pack = array( 'font-family' => $new_font_family, ); $new_cls_prefix = uniqid( 'tve-icm-' ) . '-'; /* replace the font-family declaration AND all the class names with a prefix */ $font_css = preg_replace( '#font-family:(.*?)' . preg_quote( $old_font_family, '#' ) . '(.*)$#m', 'font-family:$1' . $new_font_family . '$2', $font_css ); /* replace all the CSS classes with the new cls prefix */ /* do a preg_match do find the icon class. usually this is how the icomoon css file declares its classes: [class^="icon2-"], [class*=" icon2-"] {...} if we cannot find this pattern, there is no point in going forward, we cannot determine the global class selector */ if ( preg_match_all( '#\[class(.*?)("|\')(.+?)("|\')#m', $font_css, $matches ) ) { $old_cls_prefix = $matches[3][0]; $font_css = preg_replace( '#\[class(.*?)("|\')(.+?)("|\')#m', '[class$1$2' . $new_cls_prefix . '$4', $font_css ); } $new_icons = $config['icon_pack']['icons']; if ( isset( $old_cls_prefix ) ) { $font_css = str_replace( '.' . $old_cls_prefix, '.' . $new_cls_prefix, $font_css ); foreach ( $new_icons as $i => $icon ) { $new_icons[ $i ] = str_replace( $old_cls_prefix, $new_cls_prefix, $icon ); } } $icon_pack['icons'] = $new_icons; /* upload the font css file */ tve_wp_upload_bits( trailingslashit( dirname( $font_dir_path ) ) . 'style.css', $font_css ); /* save the URL to the uploaded css file */ $icon_pack['css'] = trailingslashit( $upload_dir['baseurl'] ) . $new_font_family . '/style.css'; /* save the folder path to the saved icon pack */ $icon_pack['folder'] = str_replace( '\\', '\\\\', trailingslashit( $upload_dir['basedir'] ) . $new_font_family ); /* go through the zip contents and grab all the font files - upload them to the fonts folder ($font_dir_path) */ $i = 0; while ( $info = $zip->statIndex( $i ) ) { if ( strpos( $info['name'], 'icon-pack/fonts/' ) === 0 && ! empty( $info['size'] ) ) { $pathinfo = pathinfo( $info['name'] ); if ( empty( $pathinfo['extension'] ) || ! in_array( strtolower( $pathinfo['extension'] ), array( 'woff', 'woff2', 'ttf', 'svg', 'eot', ) ) ) { $i ++; continue; } tve_wp_upload_bits( trailingslashit( $font_dir_path ) . basename( $info['name'] ), $zip->getFromIndex( $i ) ); } $i ++; } return $icon_pack; } /** * import any extra icon_packs that might be existing in the $config array from a previous import * * @param array $config * @param ZipArchive $zip * * @return mixed * * @throws Exception */ protected function importExtraIconPacks( & $config, ZipArchive $zip ) { if ( empty( $config['tve_globals']['extra_icons'] ) ) { return; } foreach ( $config['tve_globals']['extra_icons'] as $i => $icon_pack ) { $family = $icon_pack['font-family']; if ( false === $zip->locateName( $family . '/style.css' ) || false === $zip->locateName( $family . '/fonts/' ) ) { unset( $config['tve_globals']['extra_icons'][ $i ] ); continue; } /* create a new folder in the uploads_dir for the font-pack */ $upload_dir = $this->getUploadDir(); $font_dir_path = trailingslashit( $upload_dir['basedir'] ) . $family . '/fonts'; if ( ! wp_mkdir_p( $font_dir_path ) ) { throw new Exception( __( 'Could not create the folder for the Icon Pack', 'thrive-cb' ) ); } /* upload the font css file */ tve_wp_upload_bits( trailingslashit( dirname( $font_dir_path ) ) . 'style.css', $zip->getFromName( $family . '/style.css' ) ); /* save the URL to the uploaded css file */ $icon_pack['css'] = trailingslashit( $upload_dir['baseurl'] ) . $family . '/style.css'; /* save the folder path to the saved icon pack */ $icon_pack['folder'] = str_replace( '\\', '\\\\', trailingslashit( $upload_dir['basedir'] ) . $family ); /* go through the zip contents and grab all the font files - upload them to the fonts folder ($font_dir_path) */ $zip_index = 0; while ( $info = $zip->statIndex( $zip_index ) ) { if ( strpos( $info['name'], $family . '/fonts/' ) === 0 && ! empty( $info['size'] ) ) { $pathinfo = pathinfo( $info['name'] ); if ( empty( $pathinfo['extension'] ) || ! in_array( strtolower( $pathinfo['extension'] ), array( 'woff', 'ttf', 'svg', 'eot', ) ) ) { $zip_index ++; continue; } tve_wp_upload_bits( trailingslashit( $font_dir_path ) . basename( $info['name'] ), $zip->getFromIndex( $zip_index ) ); } $zip_index ++; } $config['tve_globals']['extra_icons'][ $i ] = $icon_pack; } $config['tve_globals']['extra_icons'] = array_values( $config['tve_globals']['extra_icons'] ); } /** * create new fonts from the exported ones, return a class map in the form of .ttfmx => .ttfmy - to be used to replace occurences of ttfmx with ttfmy in the page / lightbox contents * * @param array $config * @param ZipArchive $zip * * @return array */ public function importFonts( & $config, ZipArchive $zip ) { if ( empty( $config['thrive_tcb_post_fonts'] ) && empty( $config['tve_globals']['extra_fonts'] ) ) { return array(); } $class_map = array(); $config['tve_globals']['font_cls'] = array(); $config['tve_globals']['extra_fonts'] = isset( $config['tve_globals']['extra_fonts'] ) ? $config['tve_globals']['extra_fonts'] : array(); $this->import_config['imported_font_index'] = isset( $this->import_config['imported_font_index'] ) ? $this->import_config['imported_font_index'] : 0; $this->import_config['imported_font_index'] ++; $this->import_config['imported_fonts'] = isset( $this->import_config['imported_fonts'] ) ? $this->import_config['imported_fonts'] : array(); /* check for any extra_fonts included in the import file */ if ( ! empty( $config['tve_globals']['extra_fonts'] ) ) { foreach ( $config['tve_globals']['extra_fonts'] as $i => $font ) { $location = $this->importFontPack( $zip, $font['font_folder'], basename( $font['font_url'] ), $font['font_folder'] ); // use the same folder name if ( empty( $location ) ) { unset( $config['tve_globals']['extra_fonts'][ $i ] ); continue; } $already_imported_index = $this->findFontByProps( $font, $this->import_config['imported_fonts'] ); if ( false === $already_imported_index ) { $font['font_url'] = $location['url']; $font['font_folder'] = str_replace( '\\', '\\\\', $location['path'] ); $font['font_id'] = $this->import_config['imported_font_index'] ++; $new_css_class = 'ttfm-i-' . $font['font_id']; $class_map[ $font['font_class'] ] = $new_css_class; $font['font_class'] = $new_css_class; $this->import_config['imported_fonts'] [] = $font; } else { $font = $this->import_config['imported_fonts'][ $already_imported_index ]; } $config['tve_globals']['extra_fonts'][ $i ] = $font; } } $config['tve_globals']['extra_fonts'] = array_values( $config['tve_globals']['extra_fonts'] ); $all_fonts = $this->getThriveFonts(); foreach ( $config['thrive_tcb_post_fonts'] as $index => $font_data ) { if ( empty( $font_data['imported'] ) ) { $font_index = $this->findFontByProps( $font_data, $all_fonts ); if ( $font_index === false ) { $new_font = $this->addThriveFont( $font_data, $all_fonts ); } else { $new_font = $all_fonts[ $font_index ]; } if ( $font_data['font_class'] != $new_font['font_class'] ) { $class_map[ $font_data['font_class'] ] = $new_font['font_class']; } if ( tve_is_safe_font( $new_font ) ) { /* it does not require an URL */ unset( $config['thrive_tcb_post_fonts'][ $index ] ); } else { $config['thrive_tcb_post_fonts'][ $index ] = tve_custom_font_get_link( $new_font ); } /* add the font class to the global font_cls field */ $config['tve_globals']['font_cls'][] = $new_font['font_class']; continue; } if ( empty( $this->import_config['font_pack'] ) ) { unset( $config['thrive_tcb_post_fonts'][ $index ] ); continue; } /* at this point, the included font was imported using the thrive font import manager */ $location = $this->importFontPack( $zip, $this->import_config['font_pack']['name'], $this->import_config['font_pack']['css'] ); if ( empty( $location ) ) { // nothing we can do here, the archive seems corrupted unset( $config['thrive_tcb_post_fonts'][ $index ] ); continue; } $already_imported_index = $this->findFontByProps( $font_data, $this->import_config['imported_fonts'] ); /* search and see if this has already been imported in this run */ if ( false === $already_imported_index ) { $font_data['font_url'] = $location['url']; $font_data['font_folder'] = str_replace( '\\', '\\\\', $location['path'] ); $font_data['font_id'] = $this->import_config['imported_font_index'] ++; $new_css_class = 'ttfm-i-' . $font_data['font_id']; $class_map[ $font_data['font_class'] ] = $new_css_class; $font_data['font_class'] = $new_css_class; unset( $font_data['imported'] ); $this->import_config['imported_fonts'][] = $font_data; } else { $font_data = $this->import_config['imported_fonts'][ $already_imported_index ]; } $config['tve_globals']['extra_fonts'][] = $font_data; /* this can be removed from here at the moment */ unset( $config['thrive_tcb_post_fonts'][ $index ] ); } /* reorder the array, to skip non-consecutive indexes */ $config['thrive_tcb_post_fonts'] = array_values( $config['thrive_tcb_post_fonts'] ); return $class_map; } /** * import the actual files for the font pack included in the archive * * @param ZipArchive $zip * @param string $folder_name folder name where the font files are stored in the zip archive * @param string $css_file name of the css file from the font folder * @param string $new_folder_name if empty, a random folder name will be generated * * @return array|false * url -> the full url to the css font definition file * path -> the full path to the folder containing the font files * * @throws Exception */ public function importFontPack( $zip, $folder_name, $css_file, $new_folder_name = null ) { if ( ! empty( $this->import_config['font_pack_imported'][ $folder_name ] ) ) { return $this->import_config['font_pack_imported'][ $folder_name ]; } $zip_path = 'font-pack/' . $folder_name; if ( false === $zip->locateName( $zip_path . '/' . $css_file ) ) { throw new Exception( sprintf( __( 'Could not find the main imported font in the archive file (%s)', 'thrive-cb' ), $zip_path ) ); } $folder = null === $new_folder_name ? ( substr( $folder_name, 0, 10 ) . uniqid( '-imp-' ) ) : $new_folder_name; /* create a new folder in the uploads_dir for the font-pack */ $upload_dir = $this->getUploadDir(); $font_dir_path = trailingslashit( $upload_dir['basedir'] ) . $folder; if ( ! wp_mkdir_p( $font_dir_path ) ) { throw new Exception( __( 'Could not create the folder for the Custom Font Pack', 'thrive-cb' ) ); } /* upload the font css file */ tve_wp_upload_bits( trailingslashit( $font_dir_path ) . $css_file, $zip->getFromName( $zip_path . '/' . $css_file ) ); /* save the URL to the uploaded css file */ $this->import_config['font_pack_imported'][ $folder_name ] = array( 'url' => trailingslashit( $upload_dir['baseurl'] ) . $folder . '/' . $css_file, 'path' => $font_dir_path, ); /* go through the zip contents and grab all the font files - upload them to the fonts folder ($font_dir_path) */ $zip_index = 0; while ( $info = $zip->statIndex( $zip_index ) ) { if ( strpos( $info['name'], $folder_name ) === false ) { $zip_index ++; continue; } if ( ! preg_match( '#\.(woff|eot|woff2|ttf|svg)$#i', $info['name'] ) ) { $zip_index ++; continue; } tve_wp_upload_bits( trailingslashit( $font_dir_path ) . basename( $info['name'] ), $zip->getFromIndex( $zip_index ) ); $zip_index ++; } return $this->import_config['font_pack_imported'][ $folder_name ]; } /** * replace all occurences of the array keys of the $class_map with the array values * the problem here is that we cannot go over the string more than once - it's possible that some of the replacements are identical with some of the searches * * @param array $class_map * @param string $content * * @return string */ protected function importReplaceFontClasses( $class_map, $content ) { if ( empty( $class_map ) ) { return $content; } $search = array_keys( $class_map ); /* hold this to be used in the callback function for replacing */ $this->import_temp_replace_map = $class_map; $pattern = '#(' . implode( '|', $search ) . ')#s'; return preg_replace_callback( $pattern, array( $this, '_importReplaceFontClassCallback' ), $content ); } private function _importReplaceFontClassCallback( $matches ) { return $this->import_temp_replace_map[ $matches[0] ]; } /** * check if there is a thumbnail included in the archive and import it * * @param array $config configuration array * @param ZipArchive $zip zip archive containing the exported landing page * * @return string the url to the uploaded thumbnail file */ protected function importThumbnail( $config, ZipArchive $zip ) { if ( empty( $config['thumbnail'] ) ) { return ''; } if ( false === $zip->locateName( 'thumbnail/' . $config['thumbnail'] ) ) { return ''; } try { return $this->insertAttachment( $config['thumbnail'], $zip->getFromName( 'thumbnail/' . $config['thumbnail'] ) ); } catch ( Exception $e ) { return ''; } } /** * wp_upload_bits does not allow creating subfolders in the upload_dir structure * we need to upload some things into a grouped manner - e.g. font files for custom icons * * @param string $full_path full file path * @param string $contents * * @return true on success * @throws Exception * */ protected function wpUploadBits( $full_path, $contents ) { $ifp = @ fopen( $full_path, 'wb' ); if ( ! $ifp ) { throw new Exception( sprintf( __( 'Could not write file %s', 'thrive-cb' ), basename( $full_path ) ) ); } @fwrite( $ifp, $contents ); fclose( $ifp ); clearstatcache(); // Set correct file permissions $stat = @ stat( dirname( $full_path ) ); $perms = $stat['mode'] & 0007777; $perms = $perms & 0000666; @ chmod( $full_path, $perms ); clearstatcache(); return true; } /** * get all fonts that are defined from the font manager * * @return array */ protected function getThriveFonts() { $fonts = json_decode( get_option( 'thrive_font_manager_options', '[]' ), true ); return empty( $fonts ) ? array() : $fonts; } /** * update (save) the fonts (some new fonts might have been added during the import process) * * @param array|string $fonts */ protected function updateThriveFonts( $fonts ) { if ( is_array( $fonts ) ) { $fonts = json_encode( $fonts ); } update_option( 'thrive_font_manager_options', $fonts ); } /** * check if we can find the font defined by $font properties in the $fonts array * * @param array $font font properties * @param array $fonts array to search in * @param array $field_search an array of fields which all need to match * * @return int|false the index of the match, or false otherwise */ protected function findFontByProps( $font, $fonts, $field_search = array( 'font_name', 'font_style', 'font_bold', 'font_color', ) ) { foreach ( $fonts as $i => $f ) { $found = true; foreach ( $field_search as $field ) { if ( $font[ $field ] != $f[ $field ] ) { $found = false; break; } } if ( $found ) { return $i; } } return false; } /** * add new font (same functionality as the thrive font manager) * * @param array $font * @param array $fonts * * @return array the added font (having a new font_id and a new font_class fields) */ protected function addThriveFont( $font, & $fonts ) { $last = end( $fonts ); $font['font_id'] = empty( $last ) ? 1 : ( $last['font_id'] + 1 ); $font['font_class'] = 'ttfm' . $font['font_id']; $fonts [] = $font; $this->updateThriveFonts( $fonts ); return $font; } /** * get the imported font pack metadata, if any * * @return array */ public function getImportedFontPack() { return get_option( Tve_Dash_Font_Import_Manager::OPTION_NAME, array() ); } /** import template from a zip archive that's downloaded from the cloud */ /** * the zip archive is already downloaded from the cloud and stored in the wp-uploads folder * * @param string $zip_file_path * @param string $template_key * @param bool $skip_zip_check whether or not to check the zip file for integrity - from templates downloaded directly from the cloud, this can remain true * * @return string the new template name (with the prefix appended to it) */ public function importFromCloud( $zip_file_path, $template_key, $skip_zip_check = true ) { $old_umask = umask( 0 ); defined( 'FS_METHOD' ) || define( 'FS_METHOD', 'direct' ); /** @var $wp_filesystem WP_Filesystem_Base */ global $wp_filesystem; $upload = $this->getUploadDir(); $wp_uploads_dir = $upload['basedir']; if ( FS_METHOD !== 'direct' ) { WP_Filesystem( array( 'hostname' => defined( 'FTP_HOST' ) ? FTP_HOST : '', 'username' => defined( 'FTP_USER' ) ? FTP_USER : '', 'password' => defined( 'FTP_PASS' ) ? FTP_PASS : '', ) ); if ( FS_METHOD !== 'ssh2' ) { $wp_uploads_dir = str_replace( ABSPATH, '', $wp_uploads_dir ); } } else { WP_Filesystem(); } if ( ! $wp_filesystem->connect() && $wp_filesystem->errors instanceof WP_Error ) { throw new Exception( $wp_filesystem->errors->get_error_message() ); } $folder = trailingslashit( $wp_uploads_dir ) . TVE_CLOUD_LP_FOLDER . '/'; if ( $skip_zip_check ) { /* this means the template archive is coming directly from the Thrive Template Cloud, we can trust it */ $result = unzip_file( $zip_file_path, $folder ); if ( $result instanceof WP_Error ) { umask( $old_umask ); throw new Exception( __( 'Could not extract the archive file', 'thrive-cb' ) ); } } if ( ! $wp_filesystem->is_readable( $folder . $template_key . '.json' ) ) { throw new Exception( __( 'Could not read configuration file for the template', 'thrive-cb' ) ); } $config = json_decode( $wp_filesystem->get_contents( $folder . $template_key . '.json' ), true ); if ( ! isset( $config['check'] ) ) { throw new Exception( __( 'Could not validate the configuration file for this template', 'thrive-cb' ) ); } // validate the checksum of the data in the config array $check = $config['check']; unset( $config['check'] ); if ( $check != md5( TCB_Landing_Page_Cloud_Templates_Api::API_KEY . serialize( $config ) ) ) { throw new Exception( __( 'Could not validate the configuration file for this template', 'thrive-cb' ) ); } $downloaded_templates = tve_get_downloaded_templates(); $config['thumb'] = trailingslashit( $upload['baseurl'] ) . TVE_CLOUD_LP_FOLDER . '/templates/thumbnails/' . $template_key . '.png'; $downloaded_templates[ $template_key ] = $config; tve_save_downloaded_templates( $downloaded_templates ); return $template_key; } /** * Prepares the page for export * * Searches for Global Data (css variables and global styles) and replaces them with their corresponding static data * * @param array $config */ private function prepare_global_data_for_export( & $config ) { if ( ! empty( $config['tve_custom_css'] ) ) { if ( strpos( $config['tve_updated_post'], TVE_GLOBAL_STYLE_CLS_PREFIX ) !== false ) { if ( ! empty( $_POST['global_styles_css'] ) && is_array( $_POST['global_styles_css'] ) ) { foreach ( $_POST['global_styles_css'] as $media => $css ) { $config['tve_custom_css'] = join( '@media ' . $media . '{' . stripslashes( $_POST['global_styles_css'][ $media ] ), explode( '@media ' . $media . '{', $config['tve_custom_css'] ) ); } } $config['tve_updated_post'] = preg_replace( '#(' . TVE_GLOBAL_STYLE_CLS_PREFIX . '[a-zA-Z0-9]+-(?!tpl_)\w+)#', '$2', $config['tve_updated_post'] ); } /** * When using export TAR function */ $config['tve_custom_css'] = tve_prepare_global_variables_for_front( $config['tve_custom_css'], true, false ); } if ( ! empty( $_REQUEST['post_id'] ) && is_numeric( $_REQUEST['post_id'] ) ) { $post = tcb_post( $_REQUEST['post_id'] ); if ( $post->is_landing_page() ) { $config = array_merge( $config, tcb_landing_page( $_REQUEST['post_id'] )->prepare_landing_page_for_export() ); } } } } /** various utility functions for handling file operations */ /** * wp_upload_bits does not allow creating subfolders in the upload_dir structure * we need to upload some things into a grouped manner - e.g. font files for custom icons * * @param string $full_path full file path * @param string $contents * * @return true on success * @throws Exception * */ function tve_wp_upload_bits( $full_path, $contents ) { $ifp = @ fopen( $full_path, 'wb' ); if ( ! $ifp ) { throw new Exception( sprintf( __( 'Could not write file %s', 'thrive-cb' ), basename( $full_path ) ) ); } @fwrite( $ifp, $contents ); fclose( $ifp ); clearstatcache(); // Set correct file permissions $stat = @ stat( dirname( $full_path ) ); $perms = $stat['mode'] & 0007777; $perms = $perms & 0000666; @ chmod( $full_path, $perms ); clearstatcache(); return true; }