8889841cassets-manager/module.php000064400000002131150432040170011445 0ustar00asset_managers[ $name ] = $instance; } public function get_assets_manager( $id = null ) { if ( $id ) { if ( ! isset( $this->asset_managers[ $id ] ) ) { return null; } return $this->asset_managers[ $id ]; } return $this->asset_managers; } /** * @deprecated 3.1.0 */ public function localize_settings() { Plugin::elementor()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0' ); return []; } public function __construct() { parent::__construct(); $this->add_asset_manager( 'font', new AssetTypes\Fonts_Manager() ); $this->add_asset_manager( 'icon', new AssetTypes\Icons_Manager() ); } } assets-manager/classes/assets-base.php000064400000030306150432040170014034 0ustar00
get_metabox_field_html( $field, $field['saved'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped endforeach; ?>
get_html_field( $field ); return $html; break; case 'html_tag': $html = $this->get_html_tag( $field ); return $html; break; case 'toolbar': $html = $this->get_repeater_tools( $field ); break; case 'input': $html = $this->get_input_field( $field ); break; case 'select': $html = $this->get_select_field( $field, $saved ); break; case 'textarea': $html = $this->get_textarea_field( $field, $saved ); break; case 'file': $html = $this->get_file_field( $field, $saved ); break; case 'repeater': $html = $this->get_repeater_field( $field, $saved ); break; case 'dropzone': $html = $this->get_dropzone_field( $field, $saved ); break; case 'checkbox': return $this->get_checkbox_field( $field, $saved ); default: $method = 'get_' . $field['field_type'] . 'field'; if ( method_exists( $this, $method ) ) { $html = call_user_func( [ $this, $method ], $field, $saved ); } break; } return $this->get_field_row( $field, $html ); } public function get_field_label( $field ) { if ( ! isset( $field['label'] ) || false === $field['label'] ) { return ''; } $id = $field['id']; if ( 'file' === $field['field_type'] ) { $id .= $field['field_type']; } return '

'; } public function get_input_field( $attributes ) { if ( isset( $attributes['input_type'] ) ) { $attributes['type'] = $attributes['input_type']; unset( $attributes['input_type'] ); } $input = 'get_attribute_string( $attributes ) . '>'; return $input; } public function get_attribute_string( $attributes, $field = [] ) { if ( isset( $field['extra_attributes'] ) && is_array( $field['extra_attributes'] ) ) { $attributes = array_merge( $attributes, $field['extra_attributes'] ); } $attributes_array = []; foreach ( $attributes as $name => $value ) { $attributes_array[] = sprintf( '%s="%s"', $name, esc_attr( $value ) ); } return implode( ' ', $attributes_array ); } public function get_select_field( $field, $selected = '' ) { $input = ''; } public function get_textarea_field( $field, $html ) { $input = ''; return $input; } public function get_file_field( $field, $saved ) { $value = [ 'id' => '', 'url' => '', ]; if ( isset( $saved['id'] ) && isset( $saved['url'] ) ) { $value = $saved; } $html = ''; $html .= $this->get_input_field( [ 'type' => 'hidden', 'name' => $field['id'] . '[id]', 'value' => $value['id'], ] ); $html .= $this->get_input_field( [ 'type' => 'text', 'name' => $field['id'] . '[url]', 'value' => $value['url'], 'placeholder' => $field['description'], 'class' => 'elementor-field-input', ] ); $html .= $this->get_input_field( [ 'type' => 'button', 'class' => 'button elementor-button elementor-upload-btn', 'name' => $field['id'], 'id' => $field['id'], 'value' => '', 'data-preview_anchor' => isset( $field['preview_anchor'] ) ? $field['preview_anchor'] : 'none', 'data-mime_type' => isset( $field['mine'] ) ? $field['mine'] : '', 'data-ext' => isset( $field['ext'] ) ? $field['ext'] : '', 'data-upload_text' => esc_html__( 'Upload', 'elementor-pro' ), 'data-remove_text' => esc_html__( 'Delete', 'elementor-pro' ), 'data-box_title' => isset( $field['box_title'] ) ? $field['box_title'] : '', 'data-box_action' => isset( $field['box_action'] ) ? $field['box_action'] : '', ] ); return $html; } public function get_html_field( $field ) { return $field['raw_html']; } public function get_dropzone_field( $field ) { ob_start(); $input_attributes = [ 'type' => 'file', 'name' => $field['id'], 'id' => $field['id'], 'accept' => $field['accept'], 'class' => 'box__file', ]; if ( ! empty( $field['multiple'] ) ) { $input_attributes['multiple'] = true; } $input_html = $this->get_input_field( $input_attributes ); $field['label'] = '

' . esc_html__( 'Drag & Drop to Upload', 'elementor-pro' ) . '

'; if ( ! empty( $field['sub-label'] ) ) { $field['label'] .= '
' . $field['sub-label'] . '
'; } ?>
get_field_label( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
.
'row_label_' . $js_id, 'class' => 'repeater-title hidden', ]; if ( is_array( $row_label ) ) { $label = $row_label['default']; $row_label_html_args['data-default'] = $row_label['default']; $row_label_html_args['data-selector'] = $row_label['selector']; } else { $label = $row_label; $row_label_html_args['data-default'] = $row_label; } $row_label_html = 'get_attribute_string( $row_label_html_args ) . '>' . $label . ''; ob_start(); ?> get_attribute_string( $row_label_html_args ) . '>' . $label . ''; if ( is_array( $saved ) && count( $saved ) > 0 ) { foreach ( (array) $saved as $key => $item ) { echo '
'; echo $row_label_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $this->get_repeater_tools( $field ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo ''; // end table echo '
'; $counter++; } } echo ''; return ob_get_clean(); } public function get_checkbox_field( $field, $saved ) { Utils::print_unescaped_internal_string( $this->get_field_row( $field, '' ) ); echo '
'; foreach ( $field['options'] as $checkbox_key => $label ) { $name = $field['id'] . '_' . $checkbox_key; $checked = ! empty( $saved ) && in_array( $checkbox_key, $saved, true ) ? 'checked' : ''; echo '' . esc_html( $label ) . ''; } echo '
'; } private function get_html_tag( $field ) { $tag = isset( $field['tag'] ) ? $field['tag'] : 'div'; if ( isset( $field['close'] ) && true === $field['close'] ) { return ''; } return '<' . $tag . ' ' . $this->get_attribute_string( $field['attributes'] ) . '>'; } private function get_repeater_tools( $field ) { $confirm = isset( $field['confirm'] ) ? $field['confirm'] : esc_html__( 'Are you sure?', 'elementor-pro' ); $remove_title = isset( $field['remove_title'] ) ? $field['remove_title'] : esc_html__( 'Delete', 'elementor-pro' ); $toggle_title = isset( $field['toggle_title'] ) ? $field['toggle_title'] : esc_html__( 'Edit', 'elementor-pro' ); $close_title = isset( $field['close_title'] ) ? $field['close_title'] : esc_html__( 'Close', 'elementor-pro' ); return ' ' . $close_title . ' ' . $toggle_title . ' ' . $remove_title . ' '; } public function get_field_row( $field, $field_html ) { $description = ''; $css_id = isset( $field['id'] ) ? ' ' . $field['id'] : ''; if ( isset( $field['real_id'] ) ) { $css_id = ' ' . $field['real_id']; } $css_id .= ' elementor-field-' . $field['field_type']; return '
' . $this->get_field_label( $field ) . $field_html . $description . '
'; } public function sanitize_text_field_recursive( $data ) { if ( is_array( $data ) ) { foreach ( $data as $key => $value ) { $data[ $key ] = $this->sanitize_text_field_recursive( $value ); } return $data; } return sanitize_text_field( $data ); } public function __construct() { $this->actions(); } } assets-manager/classes/font-base.php000064400000002040150432040170013472 0ustar00font_preview_phrase = esc_html__( 'Elementor Is Making the Web Beautiful!!!', 'elementor-pro' ); } public function get_name() { return ''; } public function get_type() { return ''; } public function handle_panel_request( array $data ) { return []; } public function get_fonts( $force = false ) {} public function enqueue_font( $font_family, $font_data, $post_css ) {} public function get_font_family_type( $post_id, $post_title ) {} public function get_font_data( $post_id, $post_title ) {} public function render_preview_column( $post_id ) {} public function get_font_variations_count( $post_id ) {} public function save_meta( $post_id, $data ) {} } assets-manager/asset-types/icons-manager.php000064400000016723150432040170015200 0ustar00icon_types; } if ( isset( $this->icon_types[ $type ] ) ) { return $this->icon_types[ $type ]; } return false; } /** * Add a font type to the font manager * * @param string $icon_type * @param Classes\Assets_Base $instance */ public function add_icon_type( $icon_type, $instance ) { $this->icon_types[ $icon_type ] = $instance; } /** * Register elementor icon set custom post type */ public function register_post_type() { $labels = [ 'name' => _x( 'Custom Icons', 'CPT Name', 'elementor-pro' ), 'singular_name' => _x( 'Icon Set', 'CPT Singular Name', 'elementor-pro' ), 'add_new' => esc_html__( 'Add New', 'elementor-pro' ), 'add_new_item' => esc_html__( 'Add New Icon Set', 'elementor-pro' ), 'edit_item' => esc_html__( 'Edit Icon Set', 'elementor-pro' ), 'new_item' => esc_html__( 'New Icon Set', 'elementor-pro' ), 'all_items' => esc_html__( 'All Icons', 'elementor-pro' ), 'view_item' => esc_html__( 'View Icon', 'elementor-pro' ), 'search_items' => esc_html__( 'Search Icon Set', 'elementor-pro' ), 'not_found' => esc_html__( 'No icons found', 'elementor-pro' ), 'not_found_in_trash' => esc_html__( 'No icons found in trash', 'elementor-pro' ), 'parent_item_colon' => '', 'menu_name' => _x( 'Custom Icons', 'CPT Menu Name', 'elementor-pro' ), ]; $args = [ 'labels' => $labels, 'public' => false, 'rewrite' => false, 'show_ui' => true, 'show_in_menu' => false, 'show_in_nav_menus' => false, 'exclude_from_search' => true, 'capability_type' => 'post', 'hierarchical' => false, 'supports' => [ 'title' ], ]; $this->post_type_object = register_post_type( self::CPT, $args ); } public function post_updated_messages( $messages ) { $messages[ self::CPT ] = [ 0 => '', // Unused. Messages start at index 1. 1 => esc_html__( 'Icon Set updated.', 'elementor-pro' ), 2 => esc_html__( 'Custom field updated.', 'elementor-pro' ), 3 => esc_html__( 'Custom field deleted.', 'elementor-pro' ), 4 => esc_html__( 'Icon Set updated.', 'elementor-pro' ), /* translators: %s: Date and time of the revision. */ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Icon Set restored to revision from %s', 'elementor-pro' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, 6 => esc_html__( 'Icon Set saved.', 'elementor-pro' ), 7 => esc_html__( 'Icon Set saved.', 'elementor-pro' ), 8 => esc_html__( 'Icon Set submitted.', 'elementor-pro' ), 9 => esc_html__( 'Icon Set updated.', 'elementor-pro' ), 10 => esc_html__( 'Icon Set draft updated.', 'elementor-pro' ), ]; return $messages; } /** * Add Font manager link to admin menu */ private function register_admin_menu( Admin_Menu_Manager $admin_menu_manager ) { if ( $this->can_use_custom_icons() ) { $admin_menu_manager->register( static::MENU_SLUG, new Custom_Icons_Menu_Item() ); } else { $admin_menu_manager->register( static::PROMOTION_MENU_SLUG, new Custom_Icons_Promotion_Menu_Item() ); } } private function can_use_custom_icons() { return ( API::is_license_active() || $this->has_icons() ); } private function has_icons() { if ( null !== $this->has_icons ) { return $this->has_icons; } $existing_icons = new \WP_Query( [ 'post_type' => static::CPT, 'posts_per_page' => 1, ] ); $this->has_icons = $existing_icons->post_count > 0; return $this->has_icons; } public function redirect_admin_old_page_to_new() { if ( ! empty( $_GET['page'] ) && 'elementor_custom_icons' === $_GET['page'] ) { wp_safe_redirect( admin_url( static::MENU_SLUG ) ); die; } } /** * Clean up admin Font manager admin listing */ public function clean_admin_listing_page() { global $typenow; if ( self::CPT !== $typenow ) { return; } add_filter( 'months_dropdown_results', '__return_empty_array' ); add_filter( 'screen_options_show_screen', '__return_false' ); } public function post_row_actions( $actions, $post ) { if ( self::CPT !== $post->post_type ) { return $actions; } unset( $actions['inline hide-if-no-js'] ); return $actions; } public function add_finder_item( array $categories ) { $categories['settings']['items']['custom-icons'] = [ 'title' => esc_html__( 'Custom Icons', 'elementor-pro' ), 'icon' => 'favorite', 'url' => admin_url( static::MENU_SLUG ), 'keywords' => [ 'custom', 'icons', 'elementor' ], ]; if ( ! $this->can_use_custom_icons() ) { $lock = new Feature_Lock( [ 'type' => 'custom-icon' ] ); $categories['settings']['items']['custom-icons']['lock'] = $lock->get_config(); } return $categories; } /** * Register Font Manager action and filter hooks */ protected function actions() { add_action( 'init', [ $this, 'register_post_type' ] ); if ( is_admin() ) { add_action( 'init', [ $this, 'redirect_admin_old_page_to_new' ] ); add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu_manager ) { $this->register_admin_menu( $admin_menu_manager ); } ); // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. add_action( 'admin_menu', function () { if ( did_action( 'elementor/admin/menu/register' ) ) { return; } $menu_title = _x( 'Custom Icons', 'Elementor Font', 'elementor-pro' ); add_submenu_page( Settings::PAGE_ID, $menu_title, $menu_title, self::CAPABILITY, static::MENU_SLUG ); }, 50 ); add_action( 'admin_head', [ $this, 'clean_admin_listing_page' ] ); } // TODO: Maybe just ignore all of those when the user can't use custom icons? add_filter( 'post_updated_messages', [ $this, 'post_updated_messages' ] ); add_filter( 'post_row_actions', [ $this, 'post_row_actions' ], 10, 2 ); add_filter( 'elementor/finder/categories', [ $this, 'add_finder_item' ] ); /** * Elementor icons manager loaded. * * Fires after the icons manager was fully loaded and instantiated. * * @since 2.0.0 * * @param Fonts_Manager $this An instance of icons manager. */ do_action( 'elementor_pro/icons_manager_loaded', $this ); } /** * Fonts_Manager constructor. */ public function __construct() { $this->actions(); $this->add_icon_type( 'custom', new Icons\Custom_Icons() ); $this->add_icon_type( 'font-awesome-pro', new Icons\Font_Awesome_Pro() ); } } assets-manager/asset-types/icons/icon-sets/fontello.php000064400000004123150432040170017305 0ustar00remove_fontello_styling(); $this->dir_name = $this->get_unique_name(); } public function get_type() { return esc_html__( 'Fontello', 'elementor-pro' ); } public function is_valid() { if ( ! file_exists( $this->directory . $this->data_file ) ) { return false; // missing data file } return true; } private function remove_fontello_styling() { $filename = $this->directory . 'css/' . $this->get_name() . '.css'; $stylesheet = Utils::_unstable_file_get_contents( $filename ); $stylesheet = str_replace( [ 'margin-left: .2em;', 'margin-right: .2em;' ], [ '', '' ], $stylesheet ); file_put_contents( $filename, $stylesheet ); } private function get_json() { return json_decode( Utils::_unstable_file_get_contents( $this->directory . $this->data_file ) ); } protected function extract_icon_list() { $config = $this->get_json(); if ( ! isset( $config->glyphs ) ) { return false; // missing icons list } $icons = []; foreach ( $config->glyphs as $icon ) { $icons[] = $icon->css; } return $icons; } protected function get_prefix() { $config = $this->get_json(); if ( ! isset( $config->css_prefix_text ) ) { return false; // missing css_prefix_text } return $config->css_prefix_text; } public function get_name() { $config = $this->get_json(); if ( ! isset( $config->name ) ) { return false; // missing name } return $config->name; } protected function get_stylesheet() { $name = $this->get_name(); if ( ! $name ) { return false; // missing name } return $this->get_url() . '/css/' . $name . '.css'; } } assets-manager/asset-types/icons/icon-sets/fontastic.php000064400000003762150432040170017465 0ustar00data = Utils::_unstable_file_get_contents( $this->directory . $this->stylesheet_file ); $this->dir_name = $this->get_unique_name(); } public function get_type() { return esc_html__( 'Fontastic', 'elementor-pro' ); } public function is_valid() { if ( ! file_exists( $this->directory . $this->data_file ) ) { return false; // missing data file } return true; } protected function extract_icon_list() { $pattern = '/\.' . $this->get_prefix() . '(.*)\:before\s\{/'; preg_match_all( $pattern, $this->data, $icons_matches ); if ( empty( $icons_matches[1] ) ) { return false; // missing icons list } $icons = []; foreach ( $icons_matches[1] as $icon ) { $icons[] = $icon; } return $icons; } protected function get_prefix() { static $set_prefix = null; if ( null === $set_prefix ) { $pattern = '/class\^="(.*)?"/'; preg_match_all( $pattern, $this->data, $prefix ); if ( ! isset( $prefix[1][0] ) ) { return false; // missing css_prefix_text } $set_prefix = $prefix[1][0]; } return $set_prefix; } public function get_name() { static $set_name = null; if ( null === $set_name ) { $pattern = '/font-family: "(.*)"/'; preg_match_all( $pattern, $this->data, $name ); if ( ! isset( $name[1][0] ) ) { return false; // missing name } $set_name = $name[1][0]; } return $set_name; } protected function get_stylesheet( $unique_name = '' ) { return $this->get_url() . '/' . $this->stylesheet_file; } } assets-manager/asset-types/icons/icon-sets/icomoon.php000064400000003744150432040170017136 0ustar00dir_name = $this->get_unique_name(); return []; } public function get_type() { return esc_html__( 'Icomoon', 'elementor-pro' ); } public function is_valid() { if ( ! file_exists( $this->directory . $this->data_file ) ) { return false; // missing data file } return true; } private function get_json() { return json_decode( Utils::_unstable_file_get_contents( $this->directory . $this->data_file ) ); } protected function extract_icon_list() { $config = $this->get_json(); if ( ! isset( $config->icons ) ) { return false; // missing icons list } $icons = []; foreach ( $config->icons as $icon ) { $icons[] = $icon->properties->name; } return $icons; } protected function get_prefix() { $config = $this->get_json(); if ( ! isset( $config->preferences->fontPref->prefix ) ) { return false; // missing css_prefix_text } return $config->preferences->fontPref->prefix; } protected function get_display_prefix() { $config = $this->get_json(); if ( ! isset( $config->preferences->fontPref->classSelector ) ) { return false; // missing css_prefix_text } return str_replace( '.', '', $config->preferences->fontPref->classSelector ); } public function get_name() { $config = $this->get_json(); if ( ! isset( $config->metadata->name ) ) { return false; // missing name } return $config->metadata->name; } protected function get_stylesheet() { return $this->get_url( '/' . $this->stylesheet_file ); } } assets-manager/asset-types/icons/icon-sets/icon-set-base.php000064400000015034150432040170020117 0ustar00directory . $path_name; if ( ! file_exists( $check ) ) { return false; } if ( $this->is_path_dir( $path_name ) ) { return is_dir( $check ); } return true; } /** * is icon set * * validate that the current uploaded zip is in this icon set format * @return bool */ public function is_icon_set() { foreach ( $this->allowed_zipped_files as $file ) { if ( ! $this->is_file_allowed( $file ) ) { return false; } } return true; } public function is_valid() { return false; } protected function get_display_prefix() { return ''; } protected function get_prefix() { return ''; } public function handle_new_icon_set() { return $this->prepare(); } /** * cleanup_temp_files * @param \WP_Filesystem_Base $wp_filesystem */ protected function cleanup_temp_files( $wp_filesystem ) { $wp_filesystem->rmdir( $this->directory, true ); } /** * Gets the URL to uploaded file. * * @param $file_name * * @return string */ protected function get_file_url( $file_name ) { $wp_upload_dir = wp_upload_dir(); $url = $wp_upload_dir['baseurl'] . '/elementor/custom-icons/' . $file_name; /** * Upload file URL. * * Filters the URL to a file uploaded using custom icons. * * By default URL to a file uploaded is set to `/elementor/custom-icons/{file_name}` * inside the WordPress uploads folder. This hook allows developers to change this URL. * * @since 1.0.0 * * @param string $url File URL. * @param string $file_name File name. */ $url = apply_filters( 'elementor_pro/icons_manager/custom_icons/url', $url, $file_name ); return $url; } protected function get_icon_sets_dir() { $wp_upload_dir = wp_upload_dir(); $path = $wp_upload_dir['basedir'] . '/elementor/custom-icons'; /** * Upload file path. * * Filters the path to a folder uploaded using custom icons. * * By default the folder path to custom icon files is set to `/elementor/custom-icons` * inside the WordPress uploads folder. This hook allows developers to change this path. * * @param string $path Path to custom icons uploads directory. */ $path = apply_filters( 'elementor_pro/icons_manager/custom_icons/dir', $path ); Utils::get_ensure_upload_dir( $path ); return $path; } protected function get_ensure_upload_dir( $dir = '' ) { $path = $this->get_icon_sets_dir(); if ( ! empty( $dir ) ) { $path .= '/' . $dir; } return Utils::get_ensure_upload_dir( $path ); } public function move_files( $post_id ) { // @todo: save only needed files $wp_filesystem = Custom_Icons::get_wp_filesystem(); $to = $this->get_ensure_upload_dir( $this->dir_name ) . '/'; foreach ( $wp_filesystem->dirlist( $this->directory, false, true ) as $file ) { $full_path = $this->directory . $file['name']; if ( $wp_filesystem->is_dir( $full_path ) ) { $wp_filesystem->mkdir( $to . $file['name'] ); foreach ( $file['files'] as $filename => $sub_file ) { $new_path = $to . $file['name'] . DIRECTORY_SEPARATOR . $filename; $wp_filesystem->move( $full_path . DIRECTORY_SEPARATOR . $filename, $new_path ); $this->insert_attachment( $this->get_url() . '/' . $file['name'] . '/' . $filename, $new_path, $post_id ); } } else { $new_path = $to . $file['name']; $wp_filesystem->move( $full_path, $new_path ); $this->insert_attachment( $this->get_url() . '/' . $file['name'], $new_path, $post_id ); } } $this->cleanup_temp_files( $wp_filesystem ); update_post_meta( $post_id, '_elementor_icon_set_path', $to ); $this->directory = $to; } private function insert_attachment( $file_url, $filename, $post_id = 0 ) { $attachment = [ 'file' => $filename, 'guid' => $file_url, 'post_parent' => $post_id, 'post_type' => 'attachment', ]; $id = wp_insert_attachment( $attachment ); return $id; } public function get_unique_name() { $name = $this->get_name(); $basename = $name; $counter = 1; while ( ! $this->is_name_unique( $name ) ) { $name = $basename . '-' . $counter; $counter++; } return $name; } private function is_name_unique( $name ) { return ! is_dir( $this->get_icon_sets_dir() . '/' . $name ); } protected function get_url( $filename = '' ) { return $this->get_file_url( $this->dir_name . $filename ); } protected function get_stylesheet() { return ''; } protected function get_version() { return '1.0.0'; } protected function get_enqueue() { return false; } public function build_config() { $icon_set_config = [ 'name' => $this->dir_name, 'label' => ucwords( str_replace( [ '-', '_' ], ' ', $this->dir_name ) ), 'url' => $this->get_stylesheet(), 'enqueue' => $this->get_enqueue(), 'prefix' => $this->get_prefix(), 'displayPrefix' => $this->get_display_prefix(), 'labelIcon' => 'eicon eicon-folder', 'ver' => $this->get_version(), 'custom_icon_type' => $this->get_type(), ]; $icons = $this->extract_icon_list(); $icon_set_config['count'] = count( $icons ); $icon_set_config['icons'] = $icons; if ( 25 < $icon_set_config['count'] ) { $icon_set_config['fetchJson'] = $this->store_icon_list_json( $icons ); } return $icon_set_config; } private function store_icon_list_json( $icons ) { $wp_filesystem = Custom_Icons::get_wp_filesystem(); $json_file = $this->get_ensure_upload_dir( $this->dir_name ) . '/e_icons.js'; $wp_filesystem->put_contents( $json_file, json_encode( [ 'icons' => $icons ] ) ); return $this->get_url() . '/e_icons.js'; } /** * Icon Set Base constructor. * * @param $directory */ public function __construct( $directory ) { $this->directory = $directory; return $this->is_icon_set() ? $this : false; } } assets-manager/asset-types/icons/font-awesome-pro.php000064400000011630150432040170016762 0ustar00 'fa-regular', 'label' => esc_html__( 'Font Awesome - Regular Pro', 'elementor-pro' ), 'url' => false, 'enqueue' => false, 'prefix' => 'fa-', 'displayPrefix' => 'far', 'labelIcon' => 'fab fa-font-awesome-alt', 'ver' => '5.15.1-pro', 'fetchJson' => sprintf( $json_url, 'regular' ), 'native' => true, ]; $icons['fa-solid'] = [ 'name' => 'fa-solid', 'label' => esc_html__( 'Font Awesome - Solid Pro', 'elementor-pro' ), 'url' => false, 'enqueue' => false, 'prefix' => 'fa-', 'displayPrefix' => 'fas', 'labelIcon' => 'fab fa-font-awesome', 'ver' => '5.15.1-pro', 'fetchJson' => sprintf( $json_url, 'solid' ), 'native' => true, ]; $icons['fa-brands'] = [ 'name' => 'fa-brands', 'label' => esc_html__( 'Font Awesome - Brands Pro', 'elementor-pro' ), 'url' => false, 'enqueue' => false, 'prefix' => 'fa-', 'displayPrefix' => 'fab', 'labelIcon' => 'fab fa-font-awesome-flag', 'ver' => '5.15.1-pro', 'fetchJson' => sprintf( $json_url, 'brands' ), 'native' => true, ]; $icons['fa-light'] = [ 'name' => 'fa-light', 'label' => esc_html__( 'Font Awesome - Light Pro', 'elementor-pro' ), 'url' => false, 'enqueue' => false, 'prefix' => 'fa-', 'displayPrefix' => 'fal', 'labelIcon' => 'fal fa-flag', 'ver' => '5.15.1-pro', 'fetchJson' => sprintf( $json_url, 'light' ), 'native' => true, ]; $icons['fa-duotone'] = [ 'name' => 'fa-duotone', 'label' => esc_html__( 'Font Awesome - Duotone Pro', 'elementor-pro' ), 'url' => false, 'enqueue' => false, 'prefix' => 'fa-', 'displayPrefix' => 'fad', 'labelIcon' => 'fad fa-flag', 'ver' => '5.15.1-pro', 'fetchJson' => sprintf( $json_url, 'duotone' ), 'native' => true, ]; // remove Free unset( $settings['fa-solid'], $settings['fa-regular'], $settings['fa-brands'] ); return array_merge( $icons, $settings ); } public function register_admin_fields( Settings $settings ) { $settings->add_section( Settings::TAB_INTEGRATIONS, 'font_awesome_pro', [ 'callback' => function() { echo '

' . esc_html__( 'Font Awesome Pro', 'elementor-pro' ) . '

'; esc_html_e( 'Font Awesome, the web\'s most popular icon set and toolkit, Pro Integration', 'elementor-pro' ); }, 'fields' => [ self::FA_KIT_ID_OPTION_NAME => [ 'label' => esc_html__( 'Kit ID', 'elementor-pro' ), 'field_args' => [ 'type' => 'text', 'desc' => sprintf( /* translators: 1: Link opening tag, 2: Link closing tag. */ esc_html__( 'Enter Your %1$sFont Awesome Pro Kit ID%2$s.', 'elementor-pro' ), '', '' ), ], 'setting_args' => [ 'sanitize_callback' => [ $this, 'sanitize_kit_id_settings' ], ], ], 'validate_api_data' => [ 'field_args' => [ 'type' => 'raw_html', 'html' => sprintf( '

', self::FA_KIT_ID_OPTION_NAME . '_fetch', wp_create_nonce( self::FA_KIT_ID_OPTION_NAME ), __( 'Validate Kit ID', 'elementor-pro' ) ), ], ], ], ] ); } public function enqueue_kit_js() { wp_enqueue_script( 'font-awesome-pro', sprintf( self::FA_KIT_SCRIPT_LINK, $this->get_kit_id() ), [], ELEMENTOR_PRO_VERSION ); } public function sanitize_kit_id_settings( $input ) { if ( empty( $input ) ) { delete_option( 'elementor_' . self::FA_KIT_ID_OPTION_NAME ); } return $input; } protected function actions() { parent::actions(); if ( is_admin() ) { add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 100 ); } if ( $this->get_kit_id() ) { add_filter( 'elementor/icons_manager/native', [ $this, 'replace_font_awesome_pro' ] ); add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_kit_js' ] ); add_action( 'elementor/frontend/after_enqueue_scripts', [ $this, 'enqueue_kit_js' ] ); } } } assets-manager/asset-types/icons/templates.php000064400000002506150432040170015560 0ustar00 assets-manager/asset-types/icons/custom-icons.php000064400000034662150432040170016215 0ustar00ID ); $fields = [ [ 'id' => 'open_div', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'elementor-custom-icons-metabox', ], ], [ 'id' => 'zip_upload', 'field_type' => 'dropzone', 'accept' => 'zip,application/octet-stream,application/zip,application/x-zip,application/x-zip-compressed', 'label' => false, 'sub-label' => esc_html__( 'Your Fontello, IcoMoon or Fontastic .zip file', 'elementor-pro' ), ], [ 'id' => 'close_div', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], [ 'id' => self::META_KEY, 'name' => self::META_KEY, 'field_type' => 'input', 'input_type' => 'hidden', 'label' => false, 'value' => $save_data, 'saved' => $save_data, ], [ 'id' => Icons_Manager::CPT . '_nonce', 'name' => Icons_Manager::CPT . '_nonce', 'field_type' => 'input', 'input_type' => 'hidden', 'label' => false, 'value' => wp_create_nonce( Icons_Manager::CPT ), ], ]; foreach ( $fields as $field ) { $field['saved'] = isset( $field['saved'] ) ? $field['saved'] : ''; } $this->print_metabox( $fields ); } public function save_post_meta( $post_id, $post, $update ) { // If this is an autosave, our form has not been submitted, // so we don't want to do anything. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return $post_id; } // Check the user's permissions. if ( ! current_user_can( 'edit_post', $post_id ) ) { return $post_id; } // Check if our nonce is set. if ( ! isset( $_POST[ Icons_Manager::CPT . '_nonce' ] ) ) { return $post_id; } // Verify that the nonce is valid. if ( ! wp_verify_nonce( Utils::_unstable_get_super_global_value( $_POST, Icons_Manager::CPT . '_nonce' ), Icons_Manager::CPT ) ) { return $post_id; } if ( ! isset( $_POST[ self::META_KEY ] ) ) { return delete_post_meta( $post_id, self::META_KEY ); } // PHPCS - It will be sanitized in the next line. $json = json_decode( stripslashes_deep( $_POST[ self::META_KEY ] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized foreach ( $json as $property => $value ) { $json[ $property ] = $this->sanitize_text_field_recursive( $value ); } // All good save the files array update_post_meta( $post_id, self::META_KEY, json_encode( $json ) ); // Force refresh of list in Options Table self::clear_icon_list_option(); } public static function get_supported_icon_sets() { $icon_sets = [ 'fontastic' => __NAMESPACE__ . '\IconSets\Fontastic', 'fontello' => __NAMESPACE__ . '\IconSets\Fontello', 'icomoon' => __NAMESPACE__ . '\IconSets\Icomoon', ]; $additional_icon_sets = []; /** * Additional icon sets. * * Filters the icon types supported by Elementor Pro. * * By default Elementor Pro supports 'fontastic', 'fontello' and 'icomoon'. * This hook allows developers to add additional icon sets. * * @param array $additional_icon_sets Additional icon sets. */ $additional_icon_sets = apply_filters( 'elementor_pro/icons_manager/custom_icons/additional_supported_types', $additional_icon_sets ); return array_merge( $additional_icon_sets, $icon_sets ); } private function get_active_icon_sets() { $icons = new \WP_Query( [ 'post_type' => Icons_Manager::CPT, 'posts_per_page' => -1, ] ); $custom_icon_sets = []; foreach ( $icons->posts as $icon_set ) { $set_config = json_decode( self::get_icon_set_config( $icon_set->ID ), true ); $set_config['custom_icon_post_id'] = $icon_set->ID; $set_config['label'] = $icon_set->post_title; $custom_icon_sets[ $set_config['name'] ] = $set_config; } return $custom_icon_sets; } /** * get_wp_filesystem * @return \WP_Filesystem_Base */ public static function get_wp_filesystem() { global $wp_filesystem; if ( empty( $wp_filesystem ) ) { require_once ABSPATH . '/wp-admin/includes/file.php'; WP_Filesystem(); } return $wp_filesystem; } private function upload() { $file = Utils::_unstable_get_super_global_value( $_FILES, 'zip_upload' ); $filename = $file['name']; $ext = pathinfo( $filename, PATHINFO_EXTENSION ); if ( 'zip' !== $ext ) { unlink( $filename ); return new \WP_Error( 'unsupported_file', esc_html__( 'Only zip files are allowed', 'elementor-pro' ) ); } if ( ! function_exists( 'wp_handle_upload' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } // Handler upload archive file. $upload_result = wp_handle_upload( $file, [ 'test_form' => false ] ); if ( isset( $upload_result['error'] ) ) { unlink( $filename ); return new \WP_Error( 'upload_error', $upload_result['error'] ); } return $upload_result['file']; } private function extract_zip( $file, $to ) { // TODO: Move to core as a util. $valid_field_types = [ 'css', 'eot', 'html', 'json', 'otf', 'svg', 'ttf', 'txt', 'woff', 'woff2', ]; $zip = new \ZipArchive(); $zip->open( $file ); $valid_entries = []; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase for ( $i = 0; $i < $zip->numFiles; $i++ ) { $zipped_file_name = $zip->getNameIndex( $i ); $dirname = pathinfo( $zipped_file_name, PATHINFO_DIRNAME ); // Skip the OS X-created __MACOSX directory. if ( '__MACOSX/' === substr( $dirname, 0, 9 ) ) { continue; } $zipped_extension = pathinfo( $zipped_file_name, PATHINFO_EXTENSION ); // Skip files with transversal paths. if ( strpos( $zipped_file_name, '..' ) !== false ) { continue; } if ( in_array( $zipped_extension, $valid_field_types, true ) ) { $valid_entries[] = $zipped_file_name; } } $unzip_result = false; if ( ! empty( $valid_entries ) ) { $unzip_result = $zip->extractTo( $to, $valid_entries ); } if ( ! $unzip_result ) { $unzip_result = new \WP_Error( 'error', esc_html__( 'Could not unzip or empty archive.', 'elementor-pro' ) ); } @unlink( $file ); return $unzip_result; // TRUE | WP_Error instance. } private function upload_and_extract_zip() { $zip_file = $this->upload(); if ( is_wp_error( $zip_file ) ) { return $zip_file; } $filesystem = self::get_wp_filesystem(); $extract_to = trailingslashit( get_temp_dir() . pathinfo( $zip_file, PATHINFO_FILENAME ) ); $unzipped = $this->extract_zip( $zip_file, $extract_to ); if ( is_wp_error( $unzipped ) ) { return $unzipped; } // Find the right folder. $source_files = array_keys( $filesystem->dirlist( $extract_to ) ); if ( count( $source_files ) === 0 ) { return new \WP_Error( 'incompatible_archive', esc_html__( 'Incompatible archive', 'elementor-pro' ) ); } if ( 1 === count( $source_files ) && $filesystem->is_dir( $extract_to . $source_files[0] ) ) { $directory = $extract_to . trailingslashit( $source_files[0] ); } else { $directory = $extract_to; } return [ 'directory' => $directory, 'extracted_to' => $extract_to, ]; } public function custom_icons_upload_handler( $data ) { if ( ! current_user_can( Icons_Manager::CAPABILITY ) ) { return new \WP_Error( Exceptions::FORBIDDEN, 'Access denied.' ); } $this->current_post_id = $data['post_id']; $results = $this->upload_and_extract_zip(); if ( is_wp_error( $results ) ) { return $results; } $supported_icon_sets = self::get_supported_icon_sets(); foreach ( $supported_icon_sets as $key => $handler ) { /** * @var IconSets\Icon_Set_Base $icon_set_handler */ $icon_set_handler = new $handler( $results['directory'] ); if ( ! $icon_set_handler ) { continue; } if ( ! $icon_set_handler->is_valid() ) { continue; } $icon_set_handler->handle_new_icon_set(); $icon_set_handler->move_files( $this->current_post_id ); $config = $icon_set_handler->build_config(); // Notify about duplicate prefix if ( self::icon_set_prefix_exists( $config['prefix'] ) ) { $config['duplicate_prefix'] = true; } return [ 'config' => $config, ]; } return new \WP_Error( 'unsupported_zip_format', esc_html__( 'The zip file provided is not supported!', 'elementor-pro' ) ); } public function handle_delete_icon_set( $post_id ) { if ( Icons_Manager::CPT !== get_post_type( $post_id ) ) { return; } // remove all assets related to this icon set $attachments = get_attached_media( '', $post_id ); foreach ( $attachments as $attachment ) { wp_delete_attachment( $attachment->ID, 'true' ); } // remove icon set assets directory $icon_set_dir = get_post_meta( $post_id, '_elementor_icon_set_path', true ); if ( ! empty( $icon_set_dir ) && is_dir( $icon_set_dir ) ) { $this::get_wp_filesystem()->rmdir( $icon_set_dir, true ); } // Force refresh of list in Options Table self::clear_icon_list_option(); } public static function clear_icon_list_option() { delete_option( self::OPTION_NAME ); } public function display_post_states( $post_states, $post ) { if ( 'publish' !== $post->post_status || Icons_Manager::CPT !== $post->post_type ) { return $post_states; } $data = json_decode( self::get_icon_set_config( $post->ID ) ); if ( ! empty( $data->count ) ) { echo sprintf( '%d', esc_html( $data->count ) ); } return $post_states; } /** * Render preview column in font manager admin listing * * @param $column * @param $post_id */ public function render_columns( $column, $post_id ) { if ( 'icons_prefix' === $column ) { $data = json_decode( self::get_icon_set_config( $post_id ) ); if ( ! empty( $data->prefix ) ) { echo '
' . esc_html( '.' . $data->prefix ) . '
'; } } } /** * Define which columns to display in font manager admin listing * * @param $columns * * @return array */ public function manage_columns( $columns ) { return [ 'cb' => '', 'title' => esc_html__( 'Icon Set', 'elementor-pro' ), 'icons_prefix' => esc_html__( 'CSS Prefix', 'elementor-pro' ), ]; } public function update_enter_title_here( $title, $post ) { if ( isset( $post->post_type ) && Icons_Manager::CPT === $post->post_type ) { return esc_html__( 'Enter Icon Set Name', 'elementor-pro' ); } return $title; } public function register_ajax_actions( Ajax $ajax ) { $ajax->register_ajax_action( 'pro_assets_manager_custom_icon_upload', [ $this, 'custom_icons_upload_handler' ] ); } public function register_icon_libraries_control( $additional_sets ) { return array_replace( $additional_sets, self::get_custom_icons_config() ); } public function add_custom_icon_templates( $current_screen ) { if ( 'elementor_icons' !== $current_screen->id || 'post' !== $current_screen->base ) { return; } Plugin::elementor()->common->add_template( __DIR__ . '/templates.php' ); } public function add_custom_icons_url( $config ) { $config['customIconsURL'] = admin_url( 'edit.php?post_type=' . Icons_Manager::CPT ); return $config; } public static function get_custom_icons_config() { $config = get_option( self::OPTION_NAME, false ); if ( false === $config ) { $icons = new \WP_Query( [ 'post_type' => Icons_Manager::CPT, 'posts_per_page' => -1, 'post_status' => 'publish', ] ); $config = []; foreach ( $icons->posts as $icon_set ) { $set_config = json_decode( self::get_icon_set_config( $icon_set->ID ), true ); $set_config['custom_icon_post_id'] = $icon_set->ID; $set_config['label'] = $icon_set->post_title; if ( isset( $set_config['fetchJson'] ) ) { unset( $set_config['icons'] ); } $config[ $set_config['name'] ] = $set_config; } update_option( self::OPTION_NAME, $config ); } return $config; } public static function icon_set_prefix_exists( $prefix ) { $config = self::get_custom_icons_config(); if ( empty( $config ) ) { return false; } foreach ( $config as $icon_set_name => $icon_config ) { if ( $prefix === $icon_config['prefix'] ) { return true; } } return false; } public function transition_post_status( $new_status, $old_status, $post ) { if ( Icons_Manager::CPT !== $post->post_type ) { return; } if ( 'publish' === $old_status && 'publish' !== $new_status ) { $this->clear_icon_list_option(); } } protected function actions() { parent::actions(); if ( is_admin() ) { add_action( 'add_meta_boxes_' . Icons_Manager::CPT, [ $this, 'add_meta_box' ] ); add_action( 'save_post_' . Icons_Manager::CPT, [ $this, 'save_post_meta' ], 10, 3 ); add_filter( 'display_post_states', [ $this, 'display_post_states' ], 10, 2 ); add_action( 'manage_' . Icons_Manager::CPT . '_posts_custom_column', [ $this, 'render_columns' ], 10, 2 ); add_filter( 'enter_title_here', [ $this, 'update_enter_title_here' ], 10, 2 ); add_filter( 'manage_' . Icons_Manager::CPT . '_posts_columns', [ $this, 'manage_columns' ], 100 ); add_action( 'current_screen', [ $this, 'add_custom_icon_templates' ] ); } add_action( 'transition_post_status', [ $this, 'transition_post_status' ], 10, 3 ); add_action( 'before_delete_post', [ $this, 'handle_delete_icon_set' ] ); add_filter( 'elementor/icons_manager/additional_tabs', [ $this, 'register_icon_libraries_control' ] ); add_filter( 'elementor/editor/localize_settings', [ $this, 'add_custom_icons_url' ] ); // Ajax. add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); } } assets-manager/asset-types/admin-menu-items/custom-fonts-menu-item.php000064400000001314150432040170022153 0ustar00license_admin->get_connect_url( [ 'utm_source' => 'wp-custom-fonts', 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-and-activate-license', ] ); $renew_url = 'https://go.elementor.com/renew-custom-fonts/'; return API::is_license_expired() ? $renew_url : $connect_url; } public function get_cta_text() { return API::is_license_expired() ? esc_html__( 'Renew now', 'elementor-pro' ) : esc_html__( 'Connect & Activate', 'elementor-pro' ); } public function get_label() { return esc_html__( 'Custom Fonts', 'elementor-pro' ); } public function get_page_title() { return esc_html__( 'Custom Fonts', 'elementor-pro' ); } public function get_promotion_title() { return esc_html__( 'Add Your Custom Fonts', 'elementor-pro' ); } public function render_promotion_description() { echo esc_html__( 'Custom Fonts allows you to add your self-hosted fonts and use them on your Elementor projects to create a unique brand language.', 'elementor-pro' ); } } assets-manager/asset-types/admin-menu-items/custom-icons-menu-item.php000064400000001314150432040170022135 0ustar00license_admin->get_connect_url( [ 'utm_source' => 'wp-custom-icons', 'utm_medium' => 'wp-dash', 'utm_campaign' => 'connect-and-activate-license', ] ); $renew_url = 'https://go.elementor.com/renew-custom-icons/'; return API::is_license_expired() ? $renew_url : $connect_url; } public function get_position() { return null; } public function get_cta_text() { return API::is_license_expired() ? esc_html__( 'Renew now', 'elementor-pro' ) : esc_html__( 'Connect & Activate', 'elementor-pro' ); } public function get_label() { return esc_html__( 'Custom Icons', 'elementor-pro' ); } public function get_page_title() { return esc_html__( 'Custom Icons', 'elementor-pro' ); } public function get_promotion_title() { return esc_html__( 'Add Your Custom Icons', 'elementor-pro' ); } public function render_promotion_description() { echo esc_html__( 'Don\'t rely solely on the FontAwesome icons everyone else is using! Differentiate your website and your style with custom icons you can upload from your favorite icons source.', 'elementor-pro' ); } } assets-manager/asset-types/fonts-manager.php000064400000043022150432040170015206 0ustar00font_types; } if ( isset( $this->font_types[ $type ] ) ) { return $this->font_types[ $type ]; } return false; } /** * Add a font type to the font manager * * @param string $font_type * @param Classes\Font_Base $instance */ public function add_font_type( $font_type, $instance ) { $this->font_types[ $font_type ] = $instance; } /** * Register elementor font custom post type and elementor font type custom taxonomy */ public function register_post_type_and_tax() { $labels = [ 'name' => _x( 'Custom Fonts', 'CPT Name', 'elementor-pro' ), 'singular_name' => _x( 'Font', 'CPT Singular Name', 'elementor-pro' ), 'add_new' => esc_html__( 'Add New', 'elementor-pro' ), 'add_new_item' => esc_html__( 'Add New Font', 'elementor-pro' ), 'edit_item' => esc_html__( 'Edit Font', 'elementor-pro' ), 'new_item' => esc_html__( 'New Font', 'elementor-pro' ), 'all_items' => esc_html__( 'All Fonts', 'elementor-pro' ), 'view_item' => esc_html__( 'View Font', 'elementor-pro' ), 'search_items' => esc_html__( 'Search Font', 'elementor-pro' ), 'not_found' => esc_html__( 'No fonts found', 'elementor-pro' ), 'not_found_in_trash' => esc_html__( 'No fonts found in trash', 'elementor-pro' ), 'parent_item_colon' => '', 'menu_name' => _x( 'Custom Fonts', 'CPT Menu Name', 'elementor-pro' ), ]; $args = [ 'labels' => $labels, 'public' => false, 'rewrite' => false, 'show_ui' => true, 'show_in_menu' => false, 'show_in_nav_menus' => false, 'exclude_from_search' => true, 'capability_type' => 'post', 'hierarchical' => false, 'supports' => [ 'title' ], ]; $this->post_type_object = register_post_type( self::CPT, $args ); $taxonomy_labels = [ 'name' => _x( 'Font Types', 'Font type taxonomy general name', 'elementor-pro' ), 'singular_name' => _x( 'Font Type', 'Font type singular name', 'elementor-pro' ), 'search_items' => esc_html__( 'Search Font Types', 'elementor-pro' ), 'popular_items' => esc_html__( 'Popular Font Types', 'elementor-pro' ), 'all_items' => esc_html__( 'All Font Types', 'elementor-pro' ), 'edit_item' => esc_html__( 'Edit Font Type', 'elementor-pro' ), 'update_item' => esc_html__( 'Update Font Type', 'elementor-pro' ), 'add_new_item' => esc_html__( 'Add New Font Type', 'elementor-pro' ), 'new_item_name' => esc_html__( 'New Font Type Name', 'elementor-pro' ), 'separate_items_with_commas' => esc_html__( 'Separate Font Types with commas', 'elementor-pro' ), 'add_or_remove_items' => esc_html__( 'Add or remove Font Types', 'elementor-pro' ), 'choose_from_most_used' => esc_html__( 'Choose from the most used Font Types', 'elementor-pro' ), 'not_found' => esc_html__( 'No Font Types found.', 'elementor-pro' ), 'menu_name' => esc_html__( 'Font Types', 'elementor-pro' ), ]; $taxonomy_args = [ 'labels' => $taxonomy_labels, 'hierarchical' => false, 'show_ui' => true, 'show_in_nav_menus' => false, 'query_var' => is_admin(), 'rewrite' => false, 'public' => false, 'meta_box_cb' => [ $this, 'print_taxonomy_metabox' ], ]; $this->taxonomy_object = register_taxonomy( self::TAXONOMY, self::CPT, $taxonomy_args ); } public function post_updated_messages( $messages ) { $messages[ self::CPT ] = [ 0 => '', // Unused. Messages start at index 1. 1 => esc_html__( 'Font updated.', 'elementor-pro' ), 2 => esc_html__( 'Custom field updated.', 'elementor-pro' ), 3 => esc_html__( 'Custom field deleted.', 'elementor-pro' ), 4 => esc_html__( 'Font updated.', 'elementor-pro' ), /* translators: %s: Date and time of the revision. */ 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Font restored to revision from %s', 'elementor-pro' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false, 6 => esc_html__( 'Font saved.', 'elementor-pro' ), 7 => esc_html__( 'Font saved.', 'elementor-pro' ), 8 => esc_html__( 'Font submitted.', 'elementor-pro' ), 9 => esc_html__( 'Font updated.', 'elementor-pro' ), 10 => esc_html__( 'Font draft updated.', 'elementor-pro' ), ]; return $messages; } /** * Print Font Type metabox * * @param $post * @param $box */ public function print_taxonomy_metabox( $post, $box ) { wp_nonce_field( self::CPT, self::CPT . '_nonce' ); $name = self::TAXONOMY; ?>
ID, $name ); $slug = false; if ( is_array( $term_obj ) && isset( $term_obj[0] ) ) { $slug = $term_obj[0]->slug; } $options = ''; foreach ( $this->font_types as $type => $instance ) { $options .= sprintf( '' . "\n", $type, selected( $slug, $type, false ), $instance->get_name() ); } ?>
can_use_custom_fonts() ) { $admin_menu_manager->register( static::MENU_SLUG, new Custom_Fonts_Menu_Item() ); } else { $admin_menu_manager->register( static::PROMOTION_MENU_SLUG, new Custom_Fonts_Promotion_Menu_Item() ); } } private function can_use_custom_fonts() { return ( API::is_license_active() || $this->has_fonts() ); } private function has_fonts() { if ( null !== $this->has_fonts ) { return $this->has_fonts; } $existing_fonts = new \WP_Query( [ 'post_type' => static::CPT, 'posts_per_page' => 1, ] ); $this->has_fonts = $existing_fonts->post_count > 0; return $this->has_fonts; } public function redirect_admin_old_page_to_new() { if ( ! empty( $_GET['page'] ) && 'elementor_custom_fonts' === $_GET['page'] ) { wp_safe_redirect( admin_url( static::MENU_SLUG ) ); die; } } /** * Render preview column in font manager admin listing * * @param $column * @param $post_id */ public function render_columns( $column, $post_id ) { if ( 'font_preview' === $column ) { $font_type = $this->get_font_type_by_post_id( $post_id, true ); if ( false === $font_type ) { return; } $font_type->render_preview_column( $post_id ); } } /** * Handle editor request to embed/link font CSS per font type * * @param array $data * * @return array * @throws \Exception */ public function assets_manager_panel_action_data( array $data ) { $document = Pro_Utils::_unstable_get_document_for_edit( $data['editor_post_id'] ); if ( empty( $data['type'] ) ) { throw new \Exception( 'Font type is required.' ); } if ( empty( $data['font'] ) ) { throw new \Exception( 'Font is required.' ); } $asset = $this->get_font_type_object( $data['type'] ); if ( ! $asset ) { throw new \Exception( 'Font type not found.' ); } try { return $asset->handle_panel_request( $data ); } catch ( \Exception $exception ) { throw $exception; } } /** * Clean up admin Font manager admin listing */ public function clean_admin_listing_page() { global $typenow; if ( self::CPT !== $typenow ) { return; } add_filter( 'months_dropdown_results', '__return_empty_array' ); add_action( 'manage_' . self::CPT . '_posts_custom_column', [ $this, 'render_columns' ], 10, 2 ); add_filter( 'display_post_states', [ $this, 'display_post_states' ], 10, 2 ); add_filter( 'screen_options_show_screen', '__return_false' ); } public function update_enter_title_here( $title, $post ) { if ( isset( $post->post_type ) && self::CPT === $post->post_type ) { return esc_html__( 'Enter Font Family', 'elementor-pro' ); } return $title; } public function post_row_actions( $actions, $post ) { if ( self::CPT !== $post->post_type ) { return $actions; } unset( $actions['inline hide-if-no-js'] ); return $actions; } public function display_post_states( $post_states, $post ) { $font_type = $this->get_font_type_by_post_id( $post->ID, true ); if ( false !== $font_type ) { $font_type->get_font_variations_count( $post->ID ); } return $post_states; } /** * Define which columns to display in font manager admin listing * * @param $columns * * @return array */ public function manage_columns( $columns ) { return [ 'cb' => '', 'title' => esc_html__( 'Font Family', 'elementor-pro' ), 'font_preview' => esc_html__( 'Preview', 'elementor-pro' ), ]; } public function register_fonts_in_control( $fonts ) { $custom_fonts = $this->get_font_types(); if ( empty( $custom_fonts ) ) { $this->generate_fonts_list(); $custom_fonts = $this->get_font_types(); } return array_replace( $custom_fonts, $fonts ); } public function register_fonts_groups( $font_groups ) { $new_groups = []; foreach ( $this->get_font_type_object() as $type => $instance ) { $new_groups[ $type ] = $instance->get_name(); } return array_replace( $new_groups, $font_groups ); } /** * Gets a Font type for any given post id * * @param $post_id * @param bool $return_object * * @return array|bool|Classes\Font_Base */ private function get_font_type_by_post_id( $post_id, $return_object = false ) { $term_obj = get_the_terms( $post_id, self::TAXONOMY ); if ( is_array( $term_obj ) ) { $type_obj = array_shift( $term_obj ); if ( false === $return_object ) { return $type_obj->slug; } return $this->get_font_type_object( $type_obj->slug ); } return false; } /** * Get font manager fonts as font family => font type array * @return array */ private function get_font_types() { static $font_types = false; if ( ! $font_types ) { $font_types = get_option( self::FONTS_NAME_TYPE_OPTION_NAME, [] ); } return $font_types; } /** * Generates a list of all Font Manager fonts and stores it in the options table * @return array */ private function generate_fonts_list() { $fonts = new \WP_Query( [ 'post_type' => self::CPT, 'posts_per_page' => -1, ] ); $new_fonts = []; $font_types = []; foreach ( $fonts->posts as $font ) { $font_type = $this->get_font_type_by_post_id( $font->ID, true ); if ( false === $font_type ) { continue; } $font_types = array_replace( $font_types, $font_type->get_font_family_type( $font->ID, $font->post_title ) ); $new_fonts = array_replace( $new_fonts, $font_type->get_font_data( $font->ID, $font->post_title ) ); } update_option( self::FONTS_NAME_TYPE_OPTION_NAME, $font_types ); update_option( self::FONTS_OPTION_NAME, $new_fonts ); return $new_fonts; } /** * runs on Elementor font post save and calls the font type handler save meta method * * @param int $post_id * @param \WP_Post $post * @param bool $update * * @return mixed */ public function save_post_meta( $post_id, $post, $update ) { // If this is an autosave, our form has not been submitted, // so we don't want to do anything. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return $post_id; } // Check the user's permissions. if ( ! current_user_can( 'edit_post', $post_id ) ) { return $post_id; } // Check if our nonce is set. if ( ! isset( $_POST[ self::CPT . '_nonce' ] ) ) { return $post_id; } // Verify that the nonce is valid. if ( ! wp_verify_nonce( Pro_Utils::_unstable_get_super_global_value( $_POST, self::CPT . '_nonce' ), self::CPT ) ) { return $post_id; } // Save font type // only custom for now $custom_font = $this->get_font_type_object( 'custom' ); wp_set_object_terms( $post_id, $custom_font->get_type(), self::TAXONOMY ); // Let Font type handle saving // Sanitize the whole $_POST array $custom_font->save_meta( $post_id, Pro_Utils::_unstable_get_super_global_value( [ 'data' => $_POST ], 'data' ) ); } /** * Helper to clean font list on save/update */ public function clear_fonts_list() { delete_option( self::FONTS_OPTION_NAME ); delete_option( self::FONTS_NAME_TYPE_OPTION_NAME ); } /** * Get fonts array form the database or generate a new list if $force is set to true * * @param bool $force * * @return array|bool|mixed */ public function get_fonts() { static $fonts = false; if ( false !== $fonts ) { return $fonts; } $fonts = $this->generate_fonts_list(); $fonts = get_option( self::FONTS_OPTION_NAME, false ); return $fonts; } /** * Enqueue fonts css * * @param $post_css */ public function enqueue_fonts( $post_css ) { $used_fonts = $post_css->get_fonts(); $font_manager_fonts = $this->get_fonts(); $font_types = $this->get_font_types(); foreach ( $used_fonts as $font_family ) { if ( ! isset( $font_types[ $font_family ] ) || in_array( $font_family, $this->enqueued_fonts ) ) { continue; } $font_type = $this->get_font_type_object( $font_types[ $font_family ] ); if ( ! $font_type ) { continue; } $font_data = []; if ( isset( $font_manager_fonts[ $font_family ] ) ) { $font_data = $font_manager_fonts[ $font_family ]; } $font_type->enqueue_font( $font_family, $font_data, $post_css ); $this->enqueued_fonts[] = $font_family; } } public function register_ajax_actions( Ajax $ajax ) { $ajax->register_ajax_action( 'pro_assets_manager_panel_action_data', [ $this, 'assets_manager_panel_action_data' ] ); } public function add_finder_item( array $categories ) { $categories['settings']['items']['custom-fonts'] = [ 'title' => esc_html__( 'Custom Fonts', 'elementor-pro' ), 'icon' => 'typography', 'url' => admin_url( static::MENU_SLUG ), 'keywords' => [ 'custom', 'fonts', 'elementor' ], ]; if ( ! $this->can_use_custom_fonts() ) { $lock = new Feature_Lock( [ 'type' => 'custom-font' ] ); $categories['settings']['items']['custom-fonts']['lock'] = $lock->get_config(); } return $categories; } /** * Register Font Manager action and filter hooks */ protected function actions() { add_action( 'init', [ $this, 'register_post_type_and_tax' ] ); if ( is_admin() ) { add_action( 'init', [ $this, 'redirect_admin_old_page_to_new' ] ); add_action( 'elementor/admin/menu/register', function ( Admin_Menu_Manager $admin_menu_manager ) { $this->register_admin_menu( $admin_menu_manager ); } ); // TODO: BC - Remove after `Admin_Menu_Manager` will be the standard. add_action( 'admin_menu', function () { if ( did_action( 'elementor/admin/menu/register' ) ) { return; } $menu_title = _x( 'Custom Fonts', 'Elementor Font', 'elementor-pro' ); add_submenu_page( Settings::PAGE_ID, $menu_title, $menu_title, self::CAPABILITY, static::MENU_SLUG ); }, 50 ); add_action( 'admin_head', [ $this, 'clean_admin_listing_page' ] ); } // TODO: Maybe just ignore all of those when the user can't use custom fonts? add_filter( 'post_row_actions', [ $this, 'post_row_actions' ], 10, 2 ); add_filter( 'manage_' . self::CPT . '_posts_columns', [ $this, 'manage_columns' ], 100 ); add_action( 'save_post_' . self::CPT, [ $this, 'save_post_meta' ], 10, 3 ); add_action( 'save_post_' . self::CPT, [ $this, 'clear_fonts_list' ], 100 ); add_filter( 'elementor/fonts/groups', [ $this, 'register_fonts_groups' ] ); add_filter( 'elementor/fonts/additional_fonts', [ $this, 'register_fonts_in_control' ] ); add_filter( 'elementor/finder/categories', [ $this, 'add_finder_item' ] ); add_action( 'elementor/css-file/post/parse', [ $this, 'enqueue_fonts' ] ); add_action( 'elementor/css-file/global/parse', [ $this, 'enqueue_fonts' ] ); add_filter( 'post_updated_messages', [ $this, 'post_updated_messages' ] ); add_filter( 'enter_title_here', [ $this, 'update_enter_title_here' ], 10, 2 ); // Ajax. add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); /** * Elementor fonts manager loaded. * * Fires after the fonts manager was fully loaded and instantiated. * * @since 2.0.0 * * @param Fonts_Manager $this An instance of fonts manager. */ do_action( 'elementor_pro/fonts_manager_loaded', $this ); } /** * Fonts_Manager constructor. */ public function __construct() { $this->actions(); $this->add_font_type( 'custom', new Fonts\Custom_Fonts() ); $this->add_font_type( 'typekit', new Fonts\Typekit_Fonts() ); } } assets-manager/asset-types/fonts/typekit-fonts.php000064400000017374150432040170016431 0ustar00get_typekit_kit_id(); if ( ! $kit_id ) { return false; } $response = wp_remote_get( $this->api_base . '/' . $kit_id . '/published' ); // Response is a WP_Error object if ( is_wp_error( $response ) ) { return false; } // Response code is not success $response_code = (int) wp_remote_retrieve_response_code( $response ); $response_body = json_decode( wp_remote_retrieve_body( $response ) ); if ( 200 !== $response_code ) { switch ( $response_code ) { case 404: $this->error = esc_html__( 'Project not found.', 'elementor-pro' ); break; default: $this->error = $response_code; if ( isset( $response_body->errors ) ) { $this->error .= ': ' . implode( ', ', $response_body->errors ); } break; } return false; } if ( ! $response_body ) { $this->error = esc_html__( 'No project data was returned.', 'elementor-pro' ); return false; } /* * Expected Json response example * { * "kit": { * "id": "nmm7qvq", * "families": [ * { * "id": "hmqz", * "name": "Adobe Caslon Pro", * "slug": "adobe-caslon-pro", * "css_names": [ * "adobe-caslon-pro" * ], * "css_stack": "\"adobe-caslon-pro\",serif", * "variations": [ "n6","i6","i7" ] * } * ] * } * } */ if ( ! is_object( $response_body ) || ! isset( $response_body->kit ) || ! isset( $response_body->kit->families ) || ! is_array( $response_body->kit->families ) ) { return false; } $families = []; foreach ( $response_body->kit->families as $font_family ) { $font_css = isset( $font_family->css_names[0] ) ? $font_family->css_names[0] : $font_family->slug; $families[ $font_css ] = $this->get_type(); } update_option( self::TYPEKIT_FONTS_OPTION_NAME, $families ); return $families; } private function get_kit_fonts() { $typekit_fonts = $this->get_typekit_fonts(); if ( ! $typekit_fonts ) { $typekit_fonts = $this->fetch_typekit_data(); } return $typekit_fonts; } /** * @param array $data * * @return array * @throws \Exception */ public function handle_panel_request( array $data ) { $font_family = sanitize_text_field( $data['font'] ); $typekit_fonts = $this->get_kit_fonts(); if ( ! $typekit_fonts || ! is_array( $typekit_fonts ) ) { throw new \Exception( 'Error with TypeKit fonts.' ); } if ( ! in_array( $font_family, array_keys( $typekit_fonts ) ) ) { throw new \Exception( 'Font missing in Project.' ); } $kit_id = $this->get_typekit_kit_id(); return [ 'font_url' => sprintf( self::TYPEKIT_FONTS_LINK, $kit_id ) ]; } public function sanitize_kit_id_settings( $input ) { if ( empty( $input ) ) { delete_option( self::TYPEKIT_FONTS_OPTION_NAME ); } return $input; } public function register_admin_fields( Settings $settings ) { $fonts = $this->get_typekit_fonts(); $button_label = esc_html__( 'Get Project ID', 'elementor-pro' ); $found_label = '{{count}} ' . esc_html__( 'Fonts Families Found in project. Please note that typekit takes a few minutes to sync once you publish or update a project.', 'elementor-pro' ); if ( $fonts && is_array( $fonts ) ) { $button_label = esc_html__( 'Sync Project', 'elementor-pro' ); } $settings->add_section( Settings::TAB_INTEGRATIONS, 'typekit', [ 'callback' => function() { echo '

' . esc_html__( 'Adobe Fonts (TypeKit)', 'elementor-pro' ) . '

'; esc_html_e( 'TypeKit partners with the world’s leading type foundries to bring thousands of beautiful fonts to designers every day.', 'elementor-pro' ); }, 'fields' => [ self::TYPEKIT_KIT_ID_OPTION_NAME => [ 'label' => esc_html__( 'Project ID', 'elementor-pro' ), 'field_args' => [ 'type' => 'text', 'desc' => sprintf( /* translators: 1: Link opening tag, 2: Link closing tag. */ esc_html__( 'Enter Your %1$sTypeKit Project ID%2$s.', 'elementor-pro' ), '', '' ), ], 'setting_args' => [ 'sanitize_callback' => [ $this, 'sanitize_kit_id_settings' ], ], ], 'validate_api_data' => [ 'field_args' => [ 'type' => 'raw_html', 'html' => sprintf( '

', esc_html( $found_label ), self::TYPEKIT_KIT_ID_OPTION_NAME . '_fetch', wp_create_nonce( self::TYPEKIT_KIT_ID_OPTION_NAME ), $button_label ), ], ], ], ] ); } public function register_fonts_in_control( $fonts ) { $typekit_fonts = $this->get_kit_fonts(); if ( $typekit_fonts ) { return array_merge( $typekit_fonts, $fonts ); } return $fonts; } public function print_font_link( $font ) { if ( $this->kit_enqueued ) { return; } if ( $this->is_font_in_kit( $font ) ) { $kit_url = sprintf( self::TYPEKIT_FONTS_LINK, $this->get_typekit_kit_id() ); echo ''; $this->kit_enqueued = true; } } private function is_font_in_kit( $font ) { $kit_fonts = $this->get_kit_fonts(); if ( ! $kit_fonts || ! is_array( $kit_fonts ) ) { return false; } return in_array( $font, array_keys( $kit_fonts ) ); } public function integrations_admin_ajax_handler() { check_ajax_referer( self::TYPEKIT_KIT_ID_OPTION_NAME, '_nonce' ); if ( ! current_user_can( Fonts_Manager::CAPABILITY ) ) { wp_send_json_error( 'Permission denied' ); } $kit_id = Utils::_unstable_get_super_global_value( $_POST, 'kit_id' ); if ( ! $kit_id ) { wp_send_json_error(); } $fonts = []; try { update_option( 'elementor_' . self::TYPEKIT_KIT_ID_OPTION_NAME, sanitize_text_field( $kit_id ) ); $fonts = $this->fetch_typekit_data(); } catch ( \Exception $exception ) { wp_send_json_error(); } wp_send_json_success( [ 'fonts' => $fonts, 'count' => count( $fonts ), ] ); } protected function actions() { parent::actions(); if ( is_admin() ) { add_action( 'elementor/admin/after_create_settings/' . Settings::PAGE_ID, [ $this, 'register_admin_fields' ], 100 ); } add_filter( 'elementor/fonts/additional_fonts', [ $this, 'register_fonts_in_control' ] ); add_action( 'elementor/fonts/print_font_links/' . $this->get_type(), [ $this, 'print_font_link' ] ); add_action( 'wp_ajax_elementor_pro_admin_fetch_fonts', [ $this, 'integrations_admin_ajax_handler' ] ); } } assets-manager/asset-types/fonts/custom-fonts.php000064400000033203150432040170016237 0ustar00 'font/woff|application/font-woff|application/x-font-woff|application/octet-stream', 'woff2' => 'font/woff2|application/octet-stream|font/x-woff2', 'ttf' => 'application/x-font-ttf|application/octet-stream|font/ttf', 'svg' => 'image/svg+xml|application/octet-stream|image/x-svg+xml', 'eot' => 'application/vnd.ms-fontobject|application/octet-stream|application/x-vnd.ms-fontobject', ]; } public function add_meta_box() { add_meta_box( 'elementor-font-' . $this->get_type() . 'metabox', __( 'Manage Your Font Files', 'elementor-pro' ), [ $this, 'render_metabox' ], Fonts_Manager::CPT, 'normal', 'default' ); } public function render_metabox( $post ) { wp_enqueue_media(); $fields = [ [ 'id' => 'open_div', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'repeater-content-top', ], ], [ 'id' => 'font_weight', 'field_type' => 'select', 'label' => esc_html__( 'Weight', 'elementor-pro' ) . ':', 'extra_attributes' => [ 'class' => 'font_weight', ], 'options' => $this->get_font_weight_options(), ], [ 'id' => 'font_style', 'field_type' => 'select', 'label' => esc_html__( 'Style', 'elementor-pro' ) . ':', 'extra_attributes' => [ 'class' => 'font_style', ], 'options' => $this->get_font_style_options(), ], [ 'id' => 'preview_label', 'field_type' => 'html', 'label' => false, 'raw_html' => sprintf( '
%s
', esc_html__( 'Elementor Is Making the Web Beautiful!!!', 'elementor-pro' ) ), ], [ 'id' => 'toolbar', 'field_type' => 'toolbar', 'label' => false, ], [ 'id' => 'close_div', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], [ 'id' => 'open_div', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'repeater-content-bottom', ], ], ]; foreach ( $this->get_file_types() as $type => $mine ) { $fields[] = [ 'id' => $type, 'field_type' => 'file', 'mine' => str_replace( '|', ',', $mine ), 'ext' => $type, /* translators: %s: Font file format. */ 'label' => sprintf( esc_html__( '%s File', 'elementor-pro' ), strtoupper( $type ) ), /* translators: %s: Font file format. */ 'box_title' => sprintf( esc_html__( 'Upload font .%s file', 'elementor-pro' ), $type ), /* translators: %s: Font file format. */ 'box_action' => sprintf( esc_html__( 'Select .%s file', 'elementor-pro' ), $type ), 'preview_anchor' => 'none', 'description' => $this->get_file_type_description( $type ), ]; } $fields[] = [ 'id' => 'close_div', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ]; $font_data = get_post_meta( $post->ID, self::FONT_META_KEY, true ); $repeater = [ 'fields' => $fields, 'id' => 'font_face', 'label' => false, 'add_label' => esc_html__( 'Add Font Variation', 'elementor-pro' ), 'toggle_title' => esc_html__( 'Edit', 'elementor-pro' ), 'remove_title' => esc_html__( 'Delete', 'elementor-pro' ), 'field_type' => 'repeater', 'row_label' => [ 'default' => 'Settings', 'selector' => '.font_weight', ], 'saved' => $font_data, ]; $this->print_metabox( [ $repeater ] ); // PHPCS - Dedicated for CSS. printf( '', get_post_meta( $post->ID, self::FONT_FACE_META_KEY, true ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } public function save_meta( $post_id, $data ) { if ( ! isset( $data['font_face'] ) || ! is_array( $data['font_face'] ) ) { return; } // Sanitize a little $font_face = []; foreach ( $data['font_face'] as $font_data ) { $font_face[] = $this->sanitize_text_field_recursive( $font_data ); } // All good save the files array update_post_meta( $post_id, self::FONT_META_KEY, $font_face ); // Save font face update_post_meta( $post_id, self::FONT_FACE_META_KEY, $this->generate_font_face( $post_id ) ); } public function upload_mimes( $mine_types ) { if ( current_user_can( Fonts_Manager::CAPABILITY ) && $this->is_elementor_font_upload() ) { foreach ( $this->get_file_types() as $type => $mine ) { if ( ! isset( $mine_types[ $type ] ) ) { $mine_types[ $type ] = $mine; } } } return $mine_types; } public function wp_handle_upload_prefilter( $file ) { if ( ! $this->is_elementor_font_upload() ) { return $file; } $ext = pathinfo( $file['name'], PATHINFO_EXTENSION ); if ( 'svg' !== $ext ) { return $file; } /** * @var \Elementor\Core\Files\Assets\Svg\Svg_Handler $svg_handler; */ $svg_handler = Plugin::elementor()->assets_manager->get_asset( 'svg-handler' ); if ( Files_Upload_Handler::file_sanitizer_can_run() && ! $svg_handler->sanitize_svg( $file['tmp_name'] ) ) { $file['error'] = esc_html__( 'Invalid SVG Format, file not uploaded for security reasons', 'elementor-pro' ); } return $file; } private function is_elementor_font_upload() { return isset( $_POST['uploadTypeCaller'] ) && 'elementor-admin-font-upload' === $_POST['uploadTypeCaller']; // phpcs:ignore } /** * A workaround for upload validation which relies on a PHP extension (fileinfo) with inconsistent reporting behaviour. * ref: https://core.trac.wordpress.org/ticket/39550 * ref: https://core.trac.wordpress.org/ticket/40175 */ public function filter_fix_wp_check_filetype_and_ext( $data, $file, $filename, $mimes ) { if ( ! empty( $data['ext'] ) && ! empty( $data['type'] ) ) { return $data; } $registered_file_types = $this->get_file_types(); $filetype = wp_check_filetype( $filename, $mimes ); if ( ! isset( $registered_file_types[ $filetype['ext'] ] ) ) { return $data; } // Fix incorrect file mime type $filetype['type'] = explode( '|', $filetype['type'] )[0]; return [ 'ext' => $filetype['ext'], 'type' => $filetype['type'], 'proper_filename' => $data['proper_filename'], ]; } public function generate_font_face( $post_id ) { $saved = get_post_meta( $post_id, self::FONT_META_KEY, true ); if ( ! is_array( $saved ) ) { return false; } $font_family = get_the_title( $post_id ); $font_face = ''; foreach ( $saved as $font_data ) { $font_face .= $this->get_font_face_from_data( $font_family, $font_data ) . PHP_EOL; } return $font_face; } public function get_font_face_from_data( $font_family, $data ) { $src = []; foreach ( [ 'eot', 'woff2', 'woff', 'ttf', 'svg' ] as $type ) { if ( ! isset( $data[ $type ] ) || ! isset( $data[ $type ]['url'] ) || empty( $data[ $type ]['url'] ) ) { continue; } if ( 'svg' === $type ) { $data[ $type ]['url'] .= '#' . str_replace( ' ', '', $font_family ); } $src[] = $this->get_font_src_per_type( $type, $data[ $type ]['url'] ); } $font_face = '@font-face {' . PHP_EOL; $font_face .= "\tfont-family: '" . $font_family . "';" . PHP_EOL; $font_face .= "\tfont-style: " . $data['font_style'] . ';' . PHP_EOL; $font_face .= "\tfont-weight: " . $data['font_weight'] . ';' . PHP_EOL; $font_face .= "\tfont-display: " . apply_filters( 'elementor_pro/custom_fonts/font_display', 'auto', $font_family, $data ) . ';' . PHP_EOL; if ( isset( $data['eot'] ) && isset( $data['eot']['url'] ) && ! empty( $data['eot']['url'] ) ) { $font_face .= "\tsrc: url('" . esc_attr( $data['eot']['url'] ) . "');" . PHP_EOL; } $font_face .= "\tsrc: " . implode( ',' . PHP_EOL . "\t\t", $src ) . ';' . PHP_EOL . '}'; return $font_face; } private function get_font_src_per_type( $type, $url ) { $src = 'url(\'' . esc_attr( $url ) . '\') '; switch ( $type ) { case 'woff': case 'woff2': case 'svg': $src .= 'format(\'' . $type . '\')'; break; case 'ttf': $src .= 'format(\'truetype\')'; break; case 'eot': $src = 'url(\'' . esc_attr( $url ) . '?#iefix\') format(\'embedded-opentype\')'; break; } return $src; } public function get_fonts( $force = false ) { $fonts = get_option( self::FONTS_OPTION_NAME, false ); if ( $fonts && ! $force ) { return $fonts; } add_filter( 'posts_fields', [ $this, 'posts_fields' ] ); $fonts = new \WP_Query( [ 'post_type' => Fonts_Manager::CPT, 'posts_per_page' => -1, ] ); remove_filter( 'posts_fields', [ $this, 'posts_fields' ] ); $new_fonts = []; foreach ( $fonts->posts as $font ) { $new_fonts[ $font->post_title ] = 'custom'; } update_option( self::FONTS_OPTION_NAME, $new_fonts ); return $new_fonts; } private function get_font_face_by_font_family( $font_family ) { global $wpdb; $id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = %s LIMIT 1", $font_family, Fonts_Manager::CPT ) ); if ( $id ) { return get_post_meta( $id, self::FONT_FACE_META_KEY, true ); } return ''; } public function render_preview_column( $post_id ) { $font_face = get_post_meta( $post_id, self::FONT_FACE_META_KEY, true ); if ( ! $font_face ) { return; } // PHPCS - the variable $font_face is CSS. the property $this->font_preview_phrase is safe. printf( '%s', $font_face, esc_html( get_the_title( $post_id ) ), $this->font_preview_phrase ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } public function get_font_family_type( $post_id, $post_title ) { return [ $post_title => $this->get_type(), ]; } public function get_font_data( $post_id, $post_title ) { return [ $post_title => [ 'font_face' => $this->generate_font_face( $post_id ), 'post_id' => $post_id, ], ]; } public function get_font_variations_count( $post_id ) { $data = get_post_meta( $post_id, self::FONT_META_KEY, true ); if ( ! empty( $data ) && count( $data ) > 0 ) { echo sprintf( '%d', count( $data ) ); } } /** * @param string $font_family * @param array $font_data * @param Base $post_css */ public function enqueue_font( $font_family, $font_data, $post_css ) { $font_faces = isset( $font_data['font_face'] ) ? $font_data['font_face'] : $this->get_font_face_by_font_family( $font_family ); // Add a css comment $custom_css = '/* Start Custom Fonts CSS */' . $font_faces . '/* End Custom Fonts CSS */'; $post_css->get_stylesheet()->add_raw_css( $custom_css ); } /** * @param array $data * * @return array * @throws \Exception */ public function handle_panel_request( array $data ) { $font_family = sanitize_text_field( $data['font'] ); $font_face = $this->get_font_face_by_font_family( $font_family ); if ( empty( $font_face ) ) { /* translators: %s: Font family. */ $error_message = sprintf( esc_html__( 'Font %s was not found.', 'elementor-pro' ), $font_family ); throw new \Exception( $error_message ); } return [ 'font_face' => $font_face, ]; } private function get_font_style_options() { return [ 'normal' => esc_html__( 'Normal', 'elementor-pro' ), 'italic' => esc_html__( 'Italic', 'elementor-pro' ), 'oblique' => esc_html__( 'Oblique', 'elementor-pro' ), ]; } private function get_font_weight_options() { return [ 'normal' => esc_html__( 'Normal', 'elementor-pro' ), 'bold' => esc_html__( 'Bold', 'elementor-pro' ), '100' => '100', '200' => '200', '300' => '300', '400' => '400', '500' => '500', '600' => '600', '700' => '700', '800' => '800', '900' => '900', ]; } private function get_file_type_description( $file_type ) { $descriptions = [ 'eot' => esc_html__( 'Embedded OpenType, Used by IE6-IE9 Browsers', 'elementor-pro' ), 'woff2' => esc_html__( 'The Web Open Font Format 2, Used by Super Modern Browsers', 'elementor-pro' ), 'woff' => esc_html__( 'The Web Open Font Format, Used by Modern Browsers', 'elementor-pro' ), 'ttf' => esc_html__( 'TrueType Fonts, Used for better supporting Safari, Android, iOS', 'elementor-pro' ), 'svg' => esc_html__( 'SVG fonts allow SVG to be used as glyphs when displaying text, Used by Legacy iOS', 'elementor-pro' ), ]; return isset( $descriptions[ $file_type ] ) ? $descriptions[ $file_type ] : ''; } private function replace_urls( $rows_affected, $from, $to ) { global $wpdb; $rows_affected = $wpdb->query( "UPDATE {$wpdb->postmeta} " . $wpdb->prepare( 'SET `meta_value` = REPLACE(`meta_value`, %s, %s) ', $from, $to ) . 'WHERE `meta_key` = \'' . self::FONT_FACE_META_KEY . '\'' ); return $rows_affected; } protected function actions() { parent::actions(); add_filter( 'elementor/tools/replace-urls', function( $rows_affected, $from, $to ) { return $this->replace_urls( $rows_affected, $from, $to ); }, 10, 3 ); add_filter( 'wp_check_filetype_and_ext', [ $this, 'filter_fix_wp_check_filetype_and_ext' ], 10, 4 ); add_filter( 'wp_handle_upload_prefilter', [ $this, 'wp_handle_upload_prefilter' ] ); add_filter( 'upload_mimes', [ $this, 'upload_mimes' ] ); add_action( 'add_meta_boxes_' . Fonts_Manager::CPT, [ $this, 'add_meta_box' ] ); } } blockquote/module.php000064400000000501150432040170010702 0ustar00start_controls_section( 'section_blockquote_content', [ 'label' => esc_html__( 'Blockquote', 'elementor-pro' ), ] ); $this->add_control( 'blockquote_skin', [ 'label' => esc_html__( 'Skin', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'border' => esc_html__( 'Border', 'elementor-pro' ), 'quotation' => esc_html__( 'Quotation', 'elementor-pro' ), 'boxed' => esc_html__( 'Boxed', 'elementor-pro' ), 'clean' => esc_html__( 'Clean', 'elementor-pro' ), ], 'default' => 'border', 'prefix_class' => 'elementor-blockquote--skin-', ] ); $this->add_control( 'alignment', [ 'label' => esc_html__( 'Alignment', 'elementor-pro' ), 'type' => Controls_Manager::CHOOSE, 'options' => [ 'left' => [ 'title' => esc_html__( 'Left', 'elementor-pro' ), 'icon' => 'eicon-text-align-left', ], 'center' => [ 'title' => esc_html__( 'Center', 'elementor-pro' ), 'icon' => 'eicon-text-align-center', ], 'right' => [ 'title' => esc_html__( 'Right', 'elementor-pro' ), 'icon' => 'eicon-text-align-right', ], ], 'prefix_class' => 'elementor-blockquote--align-', 'condition' => [ 'blockquote_skin!' => 'border', ], 'separator' => 'after', ] ); $this->add_control( 'blockquote_content', [ 'label' => esc_html__( 'Content', 'elementor-pro' ), 'type' => Controls_Manager::TEXTAREA, 'dynamic' => [ 'active' => true, ], 'default' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.', 'elementor-pro' ) . esc_html__( 'Lorem ipsum dolor sit amet consectetur adipiscing elit dolor', 'elementor-pro' ), 'placeholder' => esc_html__( 'Enter your quote', 'elementor-pro' ), 'rows' => 10, ] ); $this->add_control( 'author_name', [ 'label' => esc_html__( 'Author', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'dynamic' => [ 'active' => true, ], 'default' => esc_html__( 'John Doe', 'elementor-pro' ), 'separator' => 'after', ] ); $this->add_control( 'tweet_button', [ 'label' => esc_html__( 'Tweet Button', 'elementor-pro' ), 'type' => Controls_Manager::SWITCHER, 'label_on' => esc_html__( 'On', 'elementor-pro' ), 'label_off' => esc_html__( 'Off', 'elementor-pro' ), 'default' => 'yes', ] ); $this->add_control( 'tweet_button_view', [ 'label' => esc_html__( 'View', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'icon-text' => 'Icon & Text', 'icon' => 'Icon', 'text' => 'Text', ], 'prefix_class' => 'elementor-blockquote--button-view-', 'default' => 'icon-text', 'render_type' => 'template', 'condition' => [ 'tweet_button' => 'yes', ], ] ); $this->add_control( 'tweet_button_skin', [ 'label' => esc_html__( 'Skin', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'classic' => 'Classic', 'bubble' => 'Bubble', 'link' => 'Link', ], 'default' => 'classic', 'prefix_class' => 'elementor-blockquote--button-skin-', 'condition' => [ 'tweet_button' => 'yes', ], ] ); $this->add_control( 'tweet_button_label', [ 'label' => esc_html__( 'Label', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Tweet', 'elementor-pro' ), 'condition' => [ 'tweet_button' => 'yes', 'tweet_button_view!' => 'icon', ], 'dynamic' => [ 'active' => true, ], ] ); $this->add_control( 'user_name', [ 'label' => esc_html__( 'Username', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'placeholder' => '@username', 'condition' => [ 'tweet_button' => 'yes', ], 'dynamic' => [ 'active' => true, ], ] ); $this->add_control( 'url_type', [ 'label' => esc_html__( 'Target URL', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'current_page' => esc_html__( 'Current Page', 'elementor-pro' ), 'none' => esc_html__( 'None', 'elementor-pro' ), 'custom' => esc_html__( 'Custom', 'elementor-pro' ), ], 'default' => 'current_page', 'condition' => [ 'tweet_button' => 'yes', ], ] ); $this->add_control( 'url', [ 'label' => esc_html__( 'Link', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'input_type' => 'url', 'dynamic' => [ 'active' => true, 'categories' => [ TagsModule::POST_META_CATEGORY, TagsModule::URL_CATEGORY, ], ], 'placeholder' => esc_html__( 'https://your-link.com', 'elementor-pro' ), 'label_block' => true, 'condition' => [ 'url_type' => 'custom', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_content_style', [ 'label' => esc_html__( 'Content', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_control( 'content_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'global' => [ 'default' => Global_Colors::COLOR_TEXT, ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__content' => 'color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'content_typography', 'selector' => '{{WRAPPER}} .elementor-blockquote__content', ] ); $this->add_responsive_control( 'content_gap', [ 'label' => esc_html__( 'Gap', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__content +.e-q-footer' => 'margin-top: {{SIZE}}{{UNIT}}', ], ] ); $this->add_control( 'heading_author_style', [ 'type' => Controls_Manager::HEADING, 'label' => esc_html__( 'Author', 'elementor-pro' ), 'separator' => 'before', ] ); $this->add_control( 'author_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'global' => [ 'default' => Global_Colors::COLOR_SECONDARY, ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__author' => 'color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'author_typography', 'selector' => '{{WRAPPER}} .elementor-blockquote__author', ] ); $this->add_responsive_control( 'author_gap', [ 'label' => esc_html__( 'Gap', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'default' => [ 'size' => 20, ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__author' => 'margin-bottom: {{SIZE}}{{UNIT}}', ], 'condition' => [ 'alignment' => 'center', 'tweet_button' => 'yes', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_button_style', [ 'label' => esc_html__( 'Button', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_STYLE, ] ); $this->add_responsive_control( 'button_size', [ 'label' => esc_html__( 'Size', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'min' => 0.5, 'max' => 2, 'step' => 0.1, ], ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'font-size: calc({{SIZE}}{{UNIT}} * 10);', ], ] ); $this->add_control( 'button_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'border-radius: {{SIZE}}{{UNIT}}', ], 'range' => [ 'em' => [ 'min' => 0, 'max' => 5, 'step' => 0.1, ], 'rem' => [ 'min' => 0, 'max' => 5, 'step' => 0.1, ], 'px' => [ 'min' => 0, 'max' => 50, 'step' => 1, ], '%' => [ 'min' => 0, 'max' => 100, 'step' => 1, ], ], ] ); $this->add_control( 'button_color_source', [ 'label' => esc_html__( 'Color', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'official' => esc_html__( 'Official', 'elementor-pro' ), 'custom' => esc_html__( 'Custom', 'elementor-pro' ), ], 'default' => 'official', 'prefix_class' => 'elementor-blockquote--button-color-', ] ); $this->start_controls_tabs( 'tabs_button_style' ); $this->start_controls_tab( 'tab_button_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ), 'condition' => [ 'button_color_source' => 'custom', ], ] ); $this->add_control( 'button_background_color', [ 'label' => esc_html__( 'Background Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'background-color: {{VALUE}}', 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote__tweet-button:before, body {{WRAPPER}}.elementor-blockquote--align-left .elementor-blockquote__tweet-button:before' => 'border-right-color: {{VALUE}}; border-left-color: transparent', 'body.rtl {{WRAPPER}} .elementor-blockquote__tweet-button:before, body {{WRAPPER}}.elementor-blockquote--align-right .elementor-blockquote__tweet-button:before' => 'border-left-color: {{VALUE}}; border-right-color: transparent', ], 'condition' => [ 'button_color_source' => 'custom', 'tweet_button_skin!' => 'link', ], ] ); $this->add_control( 'button_text_color', [ 'label' => esc_html__( 'Text Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'color: {{VALUE}}', '{{WRAPPER}} .elementor-blockquote__tweet-button svg' => 'fill: {{VALUE}}', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'tab_button_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ), 'condition' => [ 'button_color_source' => 'custom', ], ] ); $this->add_control( 'button_background_color_hover', [ 'label' => esc_html__( 'Background Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__tweet-button:hover' => 'background-color: {{VALUE}}', 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote__tweet-button:hover:before, body {{WRAPPER}}.elementor-blockquote--align-left .elementor-blockquote__tweet-button:hover:before' => 'border-right-color: {{VALUE}}; border-left-color: transparent', 'body.rtl {{WRAPPER}} .elementor-blockquote__tweet-button:before, body {{WRAPPER}}.elementor-blockquote--align-right .elementor-blockquote__tweet-button:hover:before' => 'border-left-color: {{VALUE}}; border-right-color: transparent', ], 'condition' => [ 'button_color_source' => 'custom', 'tweet_button_skin!' => 'link', ], ] ); $this->add_control( 'button_text_color_hover', [ 'label' => esc_html__( 'Text Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__tweet-button:hover' => 'color: {{VALUE}}', '{{WRAPPER}} .elementor-blockquote__tweet-button:hover svg' => 'fill: {{VALUE}}', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $default_fonts = Plugin::elementor()->kits_manager->get_current_settings( 'default_generic_fonts' ); if ( $default_fonts ) { $default_fonts = ', ' . $default_fonts; } $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'button_typography', 'selector' => '{{WRAPPER}} .elementor-blockquote__tweet-button span, {{WRAPPER}} .elementor-blockquote__tweet-button i', 'separator' => 'before', 'fields_options' => [ 'font_family' => [ 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__tweet-button' => 'font-family: "{{VALUE}}"' . $default_fonts . ';', ], ], ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_border_style', [ 'label' => esc_html__( 'Border', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_STYLE, 'condition' => [ 'blockquote_skin' => 'border', ], ] ); $this->start_controls_tabs( 'tabs_border_style' ); $this->start_controls_tab( 'tab_border_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ), ] ); $this->add_control( 'border_color', [ 'label' => esc_html__( 'Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote' => 'border-color: {{VALUE}}', ], ] ); $this->add_responsive_control( 'border_width', [ 'label' => esc_html__( 'Width', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote' => 'border-left-width: {{SIZE}}{{UNIT}}', 'body.rtl {{WRAPPER}} .elementor-blockquote' => 'border-right-width: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'border_gap', [ 'label' => esc_html__( 'Gap', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote' => 'padding-left: {{SIZE}}{{UNIT}}', 'body.rtl {{WRAPPER}} .elementor-blockquote' => 'padding-right: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); $this->start_controls_tab( 'tab_border_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ), ] ); $this->add_control( 'border_color_hover', [ 'label' => esc_html__( 'Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote:hover' => 'border-color: {{VALUE}}', ], ] ); $this->add_responsive_control( 'border_width_hover', [ 'label' => esc_html__( 'Width', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote:hover' => 'border-left-width: {{SIZE}}{{UNIT}}', 'body.rtl {{WRAPPER}} .elementor-blockquote:hover' => 'border-right-width: {{SIZE}}{{UNIT}}', ], ] ); $this->add_responsive_control( 'border_gap_hover', [ 'label' => esc_html__( 'Gap', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ 'body:not(.rtl) {{WRAPPER}} .elementor-blockquote:hover' => 'padding-left: {{SIZE}}{{UNIT}}', 'body.rtl {{WRAPPER}} .elementor-blockquote:hover' => 'padding-right: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_responsive_control( 'border_vertical_padding', [ 'label' => esc_html__( 'Vertical Padding', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote' => 'padding-top: {{SIZE}}{{UNIT}}; padding-bottom: {{SIZE}}{{UNIT}}', ], 'separator' => 'before', 'condition' => [ 'blockquote_skin' => 'border', ], ] ); $this->end_controls_section(); $this->start_controls_section( 'section_box_style', [ 'label' => esc_html__( 'Box', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_STYLE, 'condition' => [ 'blockquote_skin' => 'boxed', ], ] ); $this->add_responsive_control( 'box_padding', [ 'label' => esc_html__( 'Padding', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'vw', 'custom' ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote' => 'padding: {{SIZE}}{{UNIT}}', ], ] ); $this->start_controls_tabs( 'tabs_box_style' ); $this->start_controls_tab( 'tab_box_normal', [ 'label' => esc_html__( 'Normal', 'elementor-pro' ), ] ); $this->add_control( 'box_background_color', [ 'label' => esc_html__( 'Background Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote' => 'background-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'box_border', 'selector' => '{{WRAPPER}} .elementor-blockquote', ] ); $this->add_responsive_control( 'box_border_radius', [ 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote' => 'border-radius: {{SIZE}}{{UNIT}}', ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'box_box_shadow', 'exclude' => [ 'box_shadow_position', ], 'selector' => '{{WRAPPER}} .elementor-blockquote', ] ); $this->end_controls_tab(); $this->start_controls_tab( 'tab_box_hover', [ 'label' => esc_html__( 'Hover', 'elementor-pro' ), ] ); $this->add_control( 'box_background_color_hover', [ 'label' => esc_html__( 'Background Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote:hover' => 'background-color: {{VALUE}}', ], ] ); $this->add_group_control( Group_Control_Border::get_type(), [ 'name' => 'box_border_hover', 'selector' => '{{WRAPPER}} .elementor-blockquote:hover', ] ); $this->add_responsive_control( 'box_border_radius_hover', [ 'label' => esc_html__( 'Border Radius', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote:hover' => 'border-radius: {{SIZE}}{{UNIT}}', ], ] ); $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'box_box_shadow_hover', 'exclude' => [ 'box_shadow_position', ], 'selector' => '{{WRAPPER}} .elementor-blockquote:hover', ] ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->end_controls_section(); $this->start_controls_section( 'section_quote_style', [ 'label' => esc_html__( 'Quote', 'elementor-pro' ), 'tab' => Controls_Manager::TAB_STYLE, 'condition' => [ 'blockquote_skin' => 'quotation', ], ] ); $this->add_control( 'quote_text_color', [ 'label' => esc_html__( 'Color', 'elementor-pro' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .elementor-blockquote:before' => 'color: {{VALUE}}', ], ] ); $this->add_responsive_control( 'quote_size', [ 'label' => esc_html__( 'Size', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'range' => [ 'px' => [ 'min' => 0.5, 'max' => 2, 'step' => 0.1, ], ], 'default' => [ 'size' => 1, ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote:before' => 'font-size: calc({{SIZE}}{{UNIT}} * 100)', ], ] ); $this->add_responsive_control( 'quote_gap', [ 'label' => esc_html__( 'Gap', 'elementor-pro' ), 'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px', '%', 'em', 'rem', 'custom' ], 'selectors' => [ '{{WRAPPER}} .elementor-blockquote__content' => 'margin-top: {{SIZE}}{{UNIT}}', ], ] ); $this->end_controls_section(); } protected function render() { $settings = $this->get_settings_for_display(); $tweet_button_view = $settings['tweet_button_view']; $share_link = 'https://twitter.com/intent/tweet'; $text = rawurlencode( $settings['blockquote_content'] ); if ( ! empty( $settings['author_name'] ) ) { $text .= ' — ' . $settings['author_name']; } $share_link = add_query_arg( 'text', $text, $share_link ); if ( 'current_page' === $settings['url_type'] ) { $share_link = add_query_arg( 'url', rawurlencode( home_url() . add_query_arg( false, false ) ), $share_link ); } elseif ( 'custom' === $settings['url_type'] ) { $share_link = add_query_arg( 'url', rawurlencode( $settings['url'] ), $share_link ); } if ( ! empty( $settings['user_name'] ) ) { $user_name = $settings['user_name']; if ( '@' === substr( $user_name, 0, 1 ) ) { $user_name = substr( $user_name, 1 ); } $share_link = add_query_arg( 'via', rawurlencode( $user_name ), $share_link ); } $this->add_render_attribute( [ 'blockquote_content' => [ 'class' => 'elementor-blockquote__content' ], 'author_name' => [ 'class' => 'elementor-blockquote__author' ], 'tweet_button_label' => [ 'class' => 'elementor-blockquote__tweet-label' ], ] ); $this->add_inline_editing_attributes( 'blockquote_content' ); $this->add_inline_editing_attributes( 'author_name', 'none' ); $this->add_inline_editing_attributes( 'tweet_button_label', 'none' ); ?>

print_render_attribute_string( 'blockquote_content' ); ?>> print_unescaped_setting( 'blockquote_content' ); ?>

<# var tweetButtonView = settings.tweet_button_view; #>

{{{ settings.blockquote_content }}}

<# if ( 'yes' === settings.tweet_button || settings.author_name ) { #> <# } #>
add_control( 'field', [ 'label' => esc_html__( 'Format', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'average_rating' => esc_html__( 'Average Rating', 'elementor-pro' ), 'rating_count' => esc_html__( 'Rating Count', 'elementor-pro' ), 'review_count' => esc_html__( 'Review Count', 'elementor-pro' ), ], 'default' => 'average_rating', ] ); $this->add_product_id_control(); } public function render() { $settings = $this->get_settings_for_display(); $product = $this->get_product( $settings['product_id'] ); if ( ! $product ) { return ''; } $field = $settings['field']; $value = ''; switch ( $field ) { case 'average_rating': $value = $product->get_average_rating(); break; case 'rating_count': $value = $product->get_rating_count(); break; case 'review_count': $value = $product->get_review_count(); break; } // PHPCS - Safe WC data echo $value; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } woocommerce/tags/base-data-tag.php000064400000001013150432040170013113 0ustar00get_settings_for_display(); $product = $this->get_product( $settings['product_id'] ); if ( ! $product ) { return; } if ( 'yes' === $settings['show_text'] ) { $value = wc_get_stock_html( $product ); } else { $value = (int) $product->get_stock_quantity(); } // PHPCS - `wc_get_stock_html` is safe, and `get_stock_quantity` protected with (int). echo $value; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } protected function register_controls() { $this->add_control( 'show_text', [ 'label' => esc_html__( 'Show Text', 'elementor-pro' ), 'type' => Controls_Manager::SWITCHER, 'default' => 'yes', 'label_on' => esc_html__( 'Show', 'elementor-pro' ), 'label_off' => esc_html__( 'Hide', 'elementor-pro' ), ] ); $this->add_product_id_control(); } } woocommerce/tags/product-short-description.php000064400000001215150432040170015663 0ustar00add_product_id_control(); } public function render() { $product = $this->get_product( $this->get_settings( 'product_id' ) ); if ( ! $product ) { return; } echo wp_kses_post( $product->get_short_description() ); } } woocommerce/tags/product-sku.php000064400000001231150432040170013003 0ustar00add_product_id_control(); } public function render() { $product = $this->get_product( $this->get_settings( 'product_id' ) ); if ( ! $product ) { return; } $value = ''; if ( $product->get_sku() ) { $value = $product->get_sku(); } echo esc_html( $value ); } } woocommerce/tags/category-image.php000064400000002273150432040170013427 0ustar00get_category_ids(); if ( ! empty( $category_ids ) ) { $category_id = $category_ids[0]; } } } if ( $category_id ) { $image_id = get_term_meta( $category_id, 'thumbnail_id', true ); } if ( empty( $image_id ) ) { return []; } $src = wp_get_attachment_image_src( $image_id, 'full' ); return [ 'id' => $image_id, 'url' => $src[0], ]; } } woocommerce/tags/base-tag.php000064400000001147150432040170012214 0ustar00update_control( 'before', [ 'default' => esc_html__( 'Categories', 'elementor-pro' ) . ': ', ] ); } protected function register_controls() { $taxonomy_filter_args = [ 'show_in_nav_menus' => true, 'object_type' => [ 'product' ], ]; $taxonomies = get_taxonomies( $taxonomy_filter_args, 'objects' ); $options = []; foreach ( $taxonomies as $taxonomy => $object ) { $options[ $taxonomy ] = $object->label; } $this->add_control( 'taxonomy', [ 'label' => esc_html__( 'Taxonomy', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => $options, 'default' => 'product_cat', ] ); $this->add_control( 'separator', [ 'label' => esc_html__( 'Separator', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'default' => ', ', ] ); $this->add_product_id_control(); } public function render() { $settings = $this->get_settings_for_display(); $product = $this->get_product( $settings['product_id'] ); if ( ! $product ) { return; } $value = get_the_term_list( $product->get_id(), $settings['taxonomy'], '', $settings['separator'] ); echo wp_kses_post( $value ); } } woocommerce/tags/product-gallery.php000064400000001651150432040170013646 0ustar00get_product( $this->get_settings( 'product_id' ) ); if ( ! $product ) { return []; } $value = []; $attachment_ids = $product->get_gallery_image_ids(); foreach ( $attachment_ids as $attachment_id ) { $value[] = [ 'id' => $attachment_id, ]; } return $value; } } woocommerce/tags/product-content.php000064400000001151150432040170013654 0ustar00add_product_id_control(); } public function render() { $product = $this->get_product( $this->get_settings( 'product_id' ) ); if ( ! $product ) { return; } echo wp_kses_post( $product->get_description() ); } } woocommerce/tags/traits/tag-product-id.php000064400000001503150432040170014656 0ustar00add_control( 'product_id', [ 'label' => esc_html__( 'Product', 'elementor-pro' ), 'type' => QueryControlModule::QUERY_CONTROL_ID, 'options' => [], 'label_block' => true, 'autocomplete' => [ 'object' => QueryControlModule::QUERY_OBJECT_POST, 'query' => [ 'post_type' => [ 'product' ], ], ], // Since we're using the `wc_get_product` method to retrieve products, when no product selected manually // by the dynamic tag - the default should be `false` so the method will use the product id given in the // http request instead. 'default' => false, ] ); } } woocommerce/tags/product-sale.php000064400000001642150432040170013133 0ustar00add_control( 'text', [ 'label' => esc_html__( 'Text', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Sale!', 'elementor-pro' ), ] ); $this->add_product_id_control(); } public function render() { $settings = $this->get_settings_for_display(); $product = $this->get_product( $settings['product_id'] ); if ( ! $product ) { return; } $value = ''; if ( $product->is_on_sale() ) { $value = $settings['text']; } echo wp_kses_post( $value ); } } woocommerce/tags/woocommerce-add-to-cart.php000064400000004025150432040170015143 0ustar00add_control( 'product_id', [ 'label' => esc_html__( 'Product', 'elementor-pro' ), 'type' => QueryModule::QUERY_CONTROL_ID, 'options' => [], 'label_block' => true, 'autocomplete' => [ 'object' => QueryModule::QUERY_OBJECT_POST, 'query' => [ 'post_type' => [ 'product' ], 'tax_query' => [ [ 'taxonomy' => 'product_type', 'field' => 'slug', 'terms' => 'simple', ], ], ], ], ] ); $this->add_control( 'quantity', [ 'label' => esc_html__( 'Quantity', 'elementor-pro' ), 'type' => Controls_Manager::TEXT, 'ai' => [ 'active' => false, ], 'default' => 1, ] ); } public function get_value( array $options = [] ) { $settings = $this->get_settings_for_display(); if ( ! $settings['product_id'] ) { global $product; $product = wc_get_product(); if ( empty( $product ) ) { return; } $product_id = $product->get_id(); } else { $product_id = absint( $settings['product_id'] ); } $quantity = absint( $settings['quantity'] ); $url = 'yes' === get_option( 'woocommerce_cart_redirect_after_add' ) ? wc_get_cart_url() : get_permalink( $product_id ); return home_url() . '?add-to-cart=' . $product_id . '&quantity=' . $quantity . '&e-redirect=' . $url; } } woocommerce/tags/product-price.php000064400000002724150432040170013313 0ustar00add_control( 'format', [ 'label' => esc_html__( 'Format', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ 'both' => esc_html__( 'Both', 'elementor-pro' ), 'original' => esc_html__( 'Original', 'elementor-pro' ), 'sale' => esc_html__( 'Sale', 'elementor-pro' ), ], 'default' => 'both', ] ); $this->add_product_id_control(); } public function render() { $settings = $this->get_settings(); $product = $this->get_product( $settings['product_id'] ); if ( ! $product ) { return ''; } $format = $settings['format']; $value = ''; switch ( $format ) { case 'both': $value = $product->get_price_html(); break; case 'original': $value = wc_price( $product->get_regular_price() ) . $product->get_price_suffix(); break; case 'sale' && $product->is_on_sale(): $value = wc_price( $product->get_sale_price() ) . $product->get_price_suffix(); break; } // PHPCS - Just passing WC price as is echo $value; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } woocommerce/tags/product-image.php000064400000002247150432040170013273 0ustar00add_product_id_control(); } public function get_group() { return Module::WOOCOMMERCE_GROUP; } public function get_categories() { return [ \Elementor\Modules\DynamicTags\Module::IMAGE_CATEGORY ]; } public function get_value( array $options = [] ) { $product = $this->get_product( $this->get_settings( 'product_id' ) ); if ( ! $product ) { return []; } $image_id = $product->get_image_id(); if ( ! $image_id ) { return []; } $src = wp_get_attachment_image_src( $image_id, 'full' ); return [ 'id' => $image_id, 'url' => $src[0], ]; } } woocommerce/tags/product-title.php000064400000001341150432040170013324 0ustar00add_product_id_control(); } public function render() { $product = $this->get_product( $this->get_settings( 'product_id' ) ); if ( ! $product ) { return; } if ( 'variation' === $product->get_type() ) { $title = $product->get_name(); } else { $title = get_the_title( $product->get_id() ); } echo wp_kses_post( $title ); } } woocommerce/conditions/product-archive.php000064400000002736150432040170015050 0ustar00post_type, 'objects' ); $this->post_taxonomies = wp_filter_object_list( $taxonomies, [ 'public' => true, 'show_in_nav_menus' => true, ] ); parent::__construct( $data ); } public static function get_type() { return 'archive'; } public function get_name() { return 'product_archive'; } public static function get_priority() { return 40; } public function get_label() { return esc_html__( 'Product Archive', 'elementor-pro' ); } public function get_all_label() { return esc_html__( 'All Product Archives', 'elementor-pro' ); } public function register_sub_conditions() { $this->register_sub_condition( new Shop_page() ); $this->register_sub_condition( new Product_Search() ); foreach ( $this->post_taxonomies as $slug => $object ) { $condition = new ThemeBuilder\Conditions\Taxonomy( [ 'object' => $object, ] ); $this->register_sub_condition( $condition ); } } public function check( $args ) { return is_shop() || is_product_taxonomy() || Module::is_product_search(); } } woocommerce/conditions/shop-page.php000064400000001102150432040170013616 0ustar00 'product', ] ); $this->register_sub_condition( $product_archive ); $this->register_sub_condition( $product_single ); } public function check( $args ) { return is_woocommerce() || Module::is_product_search(); } } woocommerce/conditions/product-search.php000064400000001217150432040170014665 0ustar00parent = $widget; $this->add_control( 'columns', [ 'label' => esc_html__( 'Columns', 'elementor-pro' ), 'type' => Controls_Manager::SELECT, 'options' => [ '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', ], 'default' => '4', ] ); } public function render() { $this->parent->query_posts(); /** @var \WP_Query $query */ $query = $this->parent->get_query(); if ( ! $query->have_posts() ) { return; } global $woocommerce_loop; $woocommerce_loop['columns'] = (int) $this->get_instance_value( 'columns' ); Module::instance()->add_products_post_class_filter(); echo '
'; woocommerce_product_loop_start(); while ( $query->have_posts() ) { $query->the_post(); wc_get_template_part( 'content', 'product' ); } woocommerce_product_loop_end(); woocommerce_reset_loop(); wp_reset_postdata(); echo '
'; Module::instance()->remove_products_post_class_filter(); } } woocommerce/skins/skin-loop-product.php000064400000002406150432040170014312 0ustar00parent->add_render_attribute( '_wrapper', 'class', 'woocommerce' ); parent::render(); } /** * Register Query Controls * * Registers the controls for the query used by the Loop. * * @since 3.8.0 */ public function register_query_controls( Loop_Widget_Base $widget ) { $this->parent = $widget; $this->add_query_controls( Loop_Module::QUERY_ID ); } protected function render_post() { global $product; $product = wc_get_product( get_the_ID() ); parent::render_post(); } } woocommerce/wc-templates/cart/mini-cart.php000064400000011617150432040170015017 0ustar00exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_widget_cart_item_visible', true, $cart_item, $cart_item_key ) ); if ( ! $is_product_visible ) { return; } $product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key ); $product_price = apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key ); $product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key ); ?>
get_image(), $cart_item, $cart_item_key ); if ( ! $product_permalink ) : echo wp_kses_post( $thumbnail ); else : printf( '%s', esc_url( $product_permalink ), wp_kses_post( $thumbnail ) ); endif; ?>
get_name(), $cart_item, $cart_item_key ) . ' ' ); else : echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '%s', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key ) ); endif; do_action( 'woocommerce_after_cart_item_name', $cart_item, $cart_item_key ); // Meta data. echo wc_get_formatted_cart_item_data( $cart_item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
' . sprintf( '%s × %s', $cart_item['quantity'], $product_price ) . '', $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
', esc_url( wc_get_cart_remove_url( $cart_item_key ) ), $class, __( 'Remove this item', 'elementor-pro' ), esc_attr( $product_id ), esc_attr( $cart_item_key ), esc_attr( $_product->get_sku() ) ), $cart_item_key ); } ?>
cart->get_cart(); if ( empty( $cart_items ) ) { ?>
$cart_item ) { elementor_pro_render_mini_cart_item( $cart_item_key, $cart_item ); } do_action( 'woocommerce_mini_cart_contents' ); ?>
: cart->get_cart_subtotal(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
woocommerce/traits/product-id-trait.php000064400000001055150432040170014272 0ustar00get_product_variation( $product_id ); } $product = wc_get_product( $product_id ); if ( ! $product ) { $product = wc_get_product(); } return $product; } public function get_product_variation( $product_id = false ) { return wc_get_product( get_the_ID() ); } } woocommerce/traits/products-trait.php000064400000013165150432040170014070 0ustar00 [ 'default' => 'product', 'options' => [ 'current_query' => esc_html__( 'Current Query', 'elementor-pro' ), 'product' => esc_html__( 'Latest Products', 'elementor-pro' ), 'sale' => esc_html__( 'Sale', 'elementor-pro' ), 'featured' => esc_html__( 'Featured', 'elementor-pro' ), 'by_id' => _x( 'Manual Selection', 'Posts Query Control', 'elementor-pro' ), 'related_products' => esc_html__( 'Related Products', 'elementor-pro' ), 'upsells' => esc_html__( 'Upsells', 'elementor-pro' ), 'cross_sells' => esc_html__( 'Cross-Sells', 'elementor-pro' ), ], ], 'orderby' => [ 'default' => 'date', 'options' => [ 'date' => esc_html__( 'Date', 'elementor-pro' ), 'title' => esc_html__( 'Title', 'elementor-pro' ), 'price' => esc_html__( 'Price', 'elementor-pro' ), 'popularity' => esc_html__( 'Popularity', 'elementor-pro' ), 'rating' => esc_html__( 'Rating', 'elementor-pro' ), 'rand' => esc_html__( 'Random', 'elementor-pro' ), 'menu_order' => esc_html__( 'Menu Order', 'elementor-pro' ), ], ], 'exclude' => [ 'options' => [ 'current_post' => esc_html__( 'Current Post', 'elementor-pro' ), 'manual_selection' => esc_html__( 'Manual Selection', 'elementor-pro' ), 'terms' => esc_html__( 'Term', 'elementor-pro' ), ], ], 'include' => [ 'options' => [ 'terms' => esc_html__( 'Term', 'elementor-pro' ), ], ], ]; } private function init_query_settings( $name ) { $this->product_query_group_control_name = $name; $this->product_query_control_args = $this->get_query_control_args(); $this->product_query_post_type_control_id = $this->get_query_post_type_control_id(); } /** * @return array */ private function get_query_control_args() { $args = [ 'name' => $this->product_query_group_control_name, 'post_type' => 'product', 'presets' => [ 'include', 'exclude', 'order' ], 'fields_options' => $this->get_query_fields_options(), 'exclude' => [ 'posts_per_page', 'exclude_authors', 'authors', 'offset', 'related_fallback', 'related_ids', 'query_id', 'avoid_duplicates', 'ignore_sticky_posts', ], ]; $args['fields_options'] = array_merge( $args['fields_options'], $this->get_query_exclude_conditions() ); return $args; } private function get_query_exclude_conditions() { $fields = []; foreach ( $this->product_query_controls_to_hide as $control_name ) { $fields = $this->add_query_not_supported_types( $control_name, $fields ); } return $fields; } private function add_query_not_supported_types( $control_name, $fields ) { foreach ( $this->product_query_types as $query_type ) { $fields[ $control_name ]['condition']['post_type!'][] = $query_type; } return $fields; } /** * @return string */ private function get_query_post_type_control_id() { $control_id = $this->product_query_control_args['name'] . '_post_type'; // Check if the trait is currently being used by a widget or skin. Group controls add // the post_type as a prefix when added by a skin. if ( method_exists( $this, 'get_control_id' ) ) { $control_id = $this->product_query_control_args['post_type'] . '_' . $control_id; } return $control_id; } private function add_query_controls( $name ) { $this->init_query_settings( $name ); $this->add_group_control( Group_Control_Query::get_type(), $this->product_query_control_args ); $this->add_control( 'related_products_note', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Note: The Related Products Query is available when creating a Single Product template', 'elementor-pro' ), 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info', 'condition' => [ $this->product_query_post_type_control_id => 'related_products', ], ] ); $this->add_control( 'upsells_products_note', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Note: The Upsells Query is available when creating a Single Product template', 'elementor-pro' ), 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info', 'condition' => [ $this->product_query_post_type_control_id => 'upsells', ], ] ); $this->add_control( 'cross_sells_products_note', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Note: The Cross-Sells Query is available when creating a Cart page', 'elementor-pro' ), 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info', 'condition' => [ $this->product_query_post_type_control_id => 'cross_sells', ], ] ); } } woocommerce/module.php000064400000146451150432040170011070 0ustar00 'eicons', 'value' => 'eicon-' . $icon, ] ); } } /** * @param $settings * @return void */ private static function render_custom_menu_icon( $settings ) { if ( empty( $settings['menu_icon_svg'] ) ) { echo ''; // Default Custom icon. } else { Icons_Manager::render_icon( $settings['menu_icon_svg'], [ 'class' => 'e-toggle-cart-custom-icon', 'aria-hidden' => 'true', ] ); } } public function get_name() { return 'woocommerce'; } public function get_widgets() { return [ 'Archive_Products', 'Archive_Products_Deprecated', 'Archive_Description', 'Products', 'Products_Deprecated', 'Breadcrumb', 'Add_To_Cart', 'Elements', 'Single_Elements', 'Categories', 'Menu_Cart', 'Product_Title', 'Product_Images', 'Product_Price', 'Product_Add_To_Cart', 'Product_Rating', 'Product_Stock', 'Product_Meta', 'Product_Short_Description', 'Product_Content', 'Product_Data_Tabs', 'Product_Additional_Information', 'Product_Related', 'Product_Upsell', 'Purchase_Summary', 'Checkout', 'Cart', 'My_Account', 'Notices', ]; } const RECOMMENDED_POSTS_WIDGET_NAMES = [ 'theme-post-featured-image', 'woocommerce-product-title', 'woocommerce-product-add-to-cart', 'woocommerce-product-price', 'woocommerce-product-rating', 'woocommerce-product-stock', 'woocommerce-product-meta', 'woocommerce-product-short-description', 'woocommerce-product-content', 'woocommerce-product-data-tabs', 'woocommerce-product-additional-information', ]; // 'WC page name' => 'Elementor widget name' const WC_STATUS_PAGES_MAPPED_TO_WIDGETS = [ 'Cart' => 'woocommerce-cart', 'Checkout' => 'woocommerce-checkout-page', 'My account' => 'woocommerce-my-account', ]; public function add_product_post_class( $classes ) { $classes[] = 'product'; return $classes; } public function add_products_post_class_filter() { add_filter( 'post_class', [ $this, 'add_product_post_class' ] ); } public function remove_products_post_class_filter() { remove_filter( 'post_class', [ $this, 'add_product_post_class' ] ); } public function register_tags() { $tags = [ 'Product_Gallery', 'Product_Image', 'Product_Price', 'Product_Rating', 'Product_Sale', 'Product_Content', 'Product_Short_Description', 'Product_SKU', 'Product_Stock', 'Product_Terms', 'Product_Title', 'Category_Image', 'Woocommerce_Add_To_Cart', ]; /** @var \Elementor\Core\DynamicTags\Manager $module */ $module = Plugin::elementor()->dynamic_tags; $module->register_group( self::WOOCOMMERCE_GROUP, [ 'title' => esc_html__( 'WooCommerce', 'elementor-pro' ), ] ); foreach ( $tags as $tag ) { $tag = 'ElementorPro\\Modules\\Woocommerce\\tags\\' . $tag; $module->register( new $tag() ); } } public function register_wc_hooks() { wc()->frontend_includes(); } /** * @param Conditions_Manager $conditions_manager */ public function register_conditions( $conditions_manager ) { $woocommerce_condition = new Woocommerce(); $conditions_manager->get_condition( 'general' )->register_sub_condition( $woocommerce_condition ); } /** * @param Documents_Manager $documents_manager */ public function register_documents( $documents_manager ) { $this->docs_types = [ 'product-post' => Product_Post::get_class_full_name(), 'product' => Product::get_class_full_name(), 'product-archive' => Product_Archive::get_class_full_name(), ]; foreach ( $this->docs_types as $type => $class_name ) { $documents_manager->register_document_type( $type, $class_name ); } } public static function render_menu_cart_toggle_button( $settings ) { if ( null === WC()->cart ) { return; } $product_count = WC()->cart->get_cart_contents_count(); $sub_total = WC()->cart->get_cart_subtotal(); $icon = ! empty( $settings['icon'] ) ? $settings['icon'] : 'cart-medium'; ?>
cart ) { return; } $widget_cart_is_hidden = apply_filters( 'woocommerce_widget_cart_is_hidden', false ); $is_edit_mode = Plugin::elementor()->editor->is_edit_mode(); ?>
'e-close-cart-custom-icon', 'aria-hidden' => 'true', ] ); } ?>
editor->set_edit_mode( true ); } foreach ( $templates as $id ) { $this->get_all_fragments( $id, $all_fragments ); } wp_send_json( [ 'fragments' => $all_fragments ] ); } /** * Get All Fragments. * * @since 3.7.0 * * @param $id * @param $all_fragments * @return void */ public function get_all_fragments( $id, &$all_fragments ) { $fragments_in_document = $this->get_fragments_in_document( $id ); if ( $fragments_in_document ) { $all_fragments += $fragments_in_document; } } /** * Get Fragments In Document. * * A general function that will return any needed fragments for a Post. * * @since 3.7.0 * @access public * * @param int $id * * @return mixed $fragments */ public function get_fragments_in_document( $id ) { $document = Plugin::elementor()->documents->get( $id ); if ( ! is_object( $document ) ) { return false; } $fragments = []; $data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $data, $this->get_fragments_handler( $fragments ) ); return ! empty( $fragments ) ? $fragments : false; } /** * Get Fragments Handler. * * @since 3.7.0 * * @param array $fragments * @return void */ public function get_fragments_handler( array &$fragments ) { return function ( $element ) use ( &$fragments ) { if ( ! isset( $element['widgetType'] ) ) { return; } $fragment_data = $this->get_fragment_data( $element ); $total_fragments = count( $fragment_data ) / 2; for ( $i = 0; $i < $total_fragments; $i++ ) { $fragments[ $fragment_data['selector'][ $i ] ] = $fragment_data['html'][ $i ]; } }; } /** * Empty Cart Fragments * * When the Cart is emptied, the selected 'Empty Cart Template' needs to be added as an item * in the WooCommerce `$fragments` array, so that WC will push the custom Template content into the DOM. * This is done to prevent the need for a page refresh after the cart is cleared. * * @since 3.7.0 * * @param array $fragments * @return array */ public function empty_cart_fragments( $fragments ) { // Only do this when the cart is empty. if ( WC()->cart->get_cart_contents_count() !== 0 ) { return $fragments; } $document = Plugin::elementor()->documents->get( url_to_postid( wp_get_referer() ) ); if ( is_object( $document ) ) { $data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$fragments ) { if ( isset( $element['widgetType'] ) && 'woocommerce-cart' === $element['widgetType'] && ( isset( $element['settings']['additional_template_switch'] ) && 'active' === $element['settings']['additional_template_switch'] ) && ( isset( $element['settings']['additional_template_select'] ) && 0 < $element['settings']['additional_template_select'] ) ) { $fragments[ 'div.elementor-element-' . $element['id'] . ' .elementor-widget-container' ] = '
' . do_shortcode( '[elementor-template id="' . $element['settings']['additional_template_select'] . '"]' ) . '
'; } } ); } return $fragments; } public function maybe_init_cart() { $has_cart = is_a( WC()->cart, 'WC_Cart' ); if ( ! $has_cart ) { $session_class = apply_filters( 'woocommerce_session_handler', 'WC_Session_Handler' ); WC()->session = new $session_class(); WC()->session->init(); WC()->cart = new \WC_Cart(); WC()->customer = new \WC_Customer( get_current_user_id(), true ); } } public function localized_settings_frontend( $settings ) { $has_cart = is_a( WC()->cart, 'WC_Cart' ); if ( $has_cart ) { $settings['woocommerce']['menu_cart'] = [ 'cart_page_url' => wc_get_cart_url(), 'checkout_page_url' => wc_get_checkout_url(), 'fragments_nonce' => wp_create_nonce( self::MENU_CART_FRAGMENTS_ACTION ), ]; } return $settings; } public function theme_template_include( $need_override_location, $location ) { if ( is_product() && 'single' === $location ) { $need_override_location = true; } return $need_override_location; } public function add_loop_recommended_widgets( $config, $post_id ) { if ( ! $this->is_source_set_to_products( $post_id ) ) { return $config; } $config = $this->add_woocommerce_widgets_to_recommended( $config ); return $this->hide_woocommerce_widgets_in_loop_document( $config ); } /** * Add plugin path to wc template search path. * Based on: https://www.skyverge.com/blog/override-woocommerce-template-file-within-a-plugin/ * @param $template * @param $template_name * @param $template_path * * @return string */ public function woocommerce_locate_template( $template, $template_name, $template_path ) { if ( self::TEMPLATE_MINI_CART !== $template_name ) { return $template; } if ( ! $this->use_mini_cart_template ) { return $template; } $plugin_path = plugin_dir_path( __DIR__ ) . 'woocommerce/wc-templates/'; if ( file_exists( $plugin_path . $template_name ) ) { $template = $plugin_path . $template_name; } return $template; } /** * WooCommerce/WordPress widget(s), some of the widgets have css classes that used by final selectors. * before this filter, all those widgets were warped by `.elementor-widget-container` without chain original widget * classes, now they will be warped by div with the original css classes. * * @param array $default_widget_args * @param \Elementor\Widget_WordPress $widget * * @return array $default_widget_args */ public function woocommerce_wordpress_widget_css_class( $default_widget_args, $widget ) { $widget_instance = $widget->get_widget_instance(); if ( ! empty( $widget_instance->widget_cssclass ) ) { $default_widget_args['before_widget'] .= '
'; $default_widget_args['after_widget'] .= '
'; } return $default_widget_args; } public function register_admin_fields( Settings $settings ) { $settings->add_section( Settings::TAB_INTEGRATIONS, 'woocommerce', [ 'callback' => function() { echo '

' . esc_html__( 'WooCommerce', 'elementor-pro' ) . '

'; }, 'fields' => [ self::OPTION_NAME_USE_MINI_CART => [ 'label' => esc_html__( 'Mini Cart Template', 'elementor-pro' ), 'field_args' => [ 'type' => 'select', 'std' => 'initial', 'options' => [ 'initial' => '', // Relevant until either menu-cart widget is used or option is explicitly set to 'no'. 'no' => esc_html__( 'Disable', 'elementor-pro' ), 'yes' => esc_html__( 'Enable', 'elementor-pro' ), ], 'desc' => esc_html__( 'Set to `Disable` in order to use your Theme\'s or WooCommerce\'s mini-cart template instead of Elementor\'s.', 'elementor-pro' ), ], ], ], ] ); } /** * Load Widget Before WooCommerce Ajax. * * When outputting the complex WooCommerce shortcodes (which we use in our widgets) e.g. Checkout, Cart, etc. WC * immediately does more ajax calls and retrieves updated html fragments based on the data in the forms that may * be autofilled by the current user's browser e.g. the Payment section holding the "Place order" button. * * This function runs before these ajax calls. Using the `elementorPageId` and `elementorWidgetId` querystring * appended to the forms `_wp_http_referer` url field, or the referer page ID, it loads the relevant Elementor widget. * The rendered Elementor widget replaces the default WooCommerce template used to refresh WooCommerce elements in the page. * * This is necessary for example in the Checkout Payment section where we modify the Terms & Conditions text * using settings from the widget or when updating shipping methods on the Cart. * * @since 3.5.0 */ public function load_widget_before_wc_ajax() { // Make sure is a WooCommerce ajax call. $wc_ajax = ProUtils::_unstable_get_super_global_value( $_GET, 'wc-ajax' ); if ( ! $wc_ajax ) { return; } // Only handle relevant WC AJAX calls if ( ! in_array( $wc_ajax, [ 'update_order_review', 'update_shipping_method' ], true ) ) { return; } // Security checks. switch ( $wc_ajax ) { case 'update_order_review': check_ajax_referer( 'update-order-review', 'security' ); break; case 'update_shipping_method': check_ajax_referer( 'update-shipping-method', 'security' ); break; } $page_id = false; $widget_id = false; // Try to get the `$page_id` and `$widget_id` we added as a query string to `_wp_http_referer` in `post_data`. // This is only available when a form is submitted. $raw_post_data = ProUtils::_unstable_get_super_global_value( $_POST, 'post_data' ); if ( $raw_post_data ) { $raw_post_data = html_entity_decode( $raw_post_data ); parse_str( $raw_post_data, $post_data ); if ( isset( $post_data['_wp_http_referer'] ) ) { $wp_http_referer = wp_unslash( $post_data['_wp_http_referer'] ); $wp_http_referer_query_string = wp_parse_url( $wp_http_referer, PHP_URL_QUERY ); parse_str( $wp_http_referer_query_string, $wp_http_referer_query_string ); if ( isset( $wp_http_referer_query_string['elementorPageId'] ) ) { $page_id = $wp_http_referer_query_string['elementorPageId']; } if ( isset( $wp_http_referer_query_string['elementorWidgetId'] ) ) { $widget_id = $wp_http_referer_query_string['elementorWidgetId']; } } } if ( ! $page_id ) { $page_id = url_to_postid( wp_get_referer() ); } // Bail if no `$page_id`. if ( ! $page_id ) { return; } // Get Elementor document from `$page_id`. $document = Plugin::elementor()->documents->get_doc_for_frontend( $page_id ); // Bail if not Elementor page. if ( ! $document ) { return; } // Setup $page_id as the WP global $post, so is available to our widgets. $post = get_post( $page_id, OBJECT ); setup_postdata( $post ); $widget_data = false; if ( $widget_id ) { // If we did manage to pass `$widget_id` to this ajax call we get the widget data by its ID. $widget_data = Utils::find_element_recursive( $document->get_elements_data(), $widget_id ); } else { // If we didn't manage to pass `$widget_id` to this ajax call we use this alternate method and get the first // of the type of widget used on the WC endpoint pages responsible for these ajax calls - cart or checkout widget. $woocommerce_widgets = [ 'woocommerce-cart', 'woocommerce-checkout-page' ]; $document_data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $document_data, function( $element ) use ( $woocommerce_widgets, &$widget_data ) { if ( $widget_data && ( ! isset( $element['widgetType'] ) || ! in_array( $element['widgetType'], $woocommerce_widgets, true ) ) ) { return; } $widget_data = $element; } ); } // If we found a widget then run `add_render_hooks()` widget method. if ( $widget_data ) { $widget_instance = Plugin::elementor()->elements_manager->create_element_instance( $widget_data ); if ( method_exists( $widget_instance, 'add_render_hooks' ) ) { $widget_instance->add_render_hooks(); } } } /** * Elementor Woocommerce Checkout Login User * * Handle the Ajax call for the custom login form on the Checkout Widget * * @since 3.5.0 */ public function elementor_woocommerce_checkout_login_user() { if ( is_user_logged_in() ) { wp_logout(); } $error = false; $error_message = ''; if ( ! wp_verify_nonce( ProUtils::_unstable_get_super_global_value( $_POST, 'nonce' ), 'woocommerce-login' ) ) { $error = true; $error_message = sprintf( /* translators: 1: Bold text opening tag, 2: Bold text closing tag. */ esc_html__( '%1$sError:%2$s The nonce security check didn’t pass. Please reload the page and try again. You may want to try clearing your browser cache as a last attempt.', 'elementor-pro' ), '', '' ); } else { $info = [ 'user_login' => trim( ProUtils::_unstable_get_super_global_value( $_POST, 'username' ) ), 'user_password' => $_POST['password'] ?? '', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, A password should not be sanitized. 'remember' => ProUtils::_unstable_get_super_global_value( $_POST, 'remember' ), ]; $user_signon = wp_signon( $info, false ); if ( is_wp_error( $user_signon ) ) { $error = true; $error_message = $user_signon->get_error_message(); } } if ( $error ) { wc_add_notice( $error_message, 'error' ); $response = [ 'logged_in' => false, 'message' => wc_print_notices( true ), ]; } else { $response = [ 'logged_in' => true ]; } echo wp_json_encode( $response ); wp_die(); } /** * Register Ajax Actions. * * Registers ajax action used by the Editor js. * * @since 3.5.0 * * @param Ajax $ajax */ public function register_ajax_actions( Ajax $ajax ) { // `woocommerce_update_page_option` is called in the editor save-show-modal.js. $ajax->register_ajax_action( 'pro_woocommerce_update_page_option', [ $this, 'update_page_option' ] ); $ajax->register_ajax_action( 'pro_woocommerce_mock_notices', [ $this, 'woocommerce_mock_notices' ] ); } /** * @throws \Exception */ public function woocommerce_mock_notices( $data ) { $document = ProUtils::_unstable_get_document_for_edit( $data['editor_post_id'] ); if ( in_array( 'wc_error', $data['notice_elements'], true ) ) { $notice_message = sprintf( '%1$s %2$s', esc_html__( 'This is how an error notice would look.', 'elementor-pro' ), esc_html__( 'Here\'s a link', 'elementor-pro' ) ); wc_add_notice( $notice_message, 'error' ); } if ( in_array( 'wc_message', $data['notice_elements'], true ) ) { $notice_message = sprintf( '%1$s %2$s %3$s', esc_html__( 'Button', 'elementor-pro' ), esc_html__( 'This is what a WooCommerce message notice looks like.', 'elementor-pro' ), esc_html__( 'Here\'s a link', 'elementor-pro' ) ); wc_add_notice( $notice_message, 'success' ); } if ( in_array( 'wc_info', $data['notice_elements'], true ) ) { $notice_message = sprintf( '%1$s %2$s', esc_html__( 'Button', 'elementor-pro' ), esc_html__( 'This is how WooCommerce provides an info notice.', 'elementor-pro' ) ); wc_add_notice( $notice_message, 'notice' ); } return '
' . wc_print_notices( true ) . '
'; } /** * Update Page Option. * * Ajax action can be used to update any WooCommerce option. * * @since 3.5.0 * * @param array $data */ public function update_page_option( $data ) { $is_admin = current_user_can( 'manage_options' ); $is_shop_manager = current_user_can( 'manage_woocommerce' ); $is_allowed = $is_admin || $is_shop_manager; if ( ! $is_allowed ) { return new \WP_Error( 401 ); } $allowed_options = [ 'woocommerce_checkout_page_id', 'woocommerce_cart_page_id', 'woocommerce_myaccount_page_id', 'elementor_woocommerce_purchase_summary_page_id', ]; $option_name = $data['option_name']; $post_id = absint( $data['editor_post_id'] ); if ( ! in_array( $option_name, $allowed_options, true ) ) { return new \WP_Error( 400 ); } update_option( $option_name, $post_id ); } public function init_site_settings( \Elementor\Core\Kits\Documents\Kit $kit ) { $kit->register_tab( 'settings-woocommerce', \ElementorPro\Modules\Woocommerce\Settings\Settings_Woocommerce::class ); } public function add_products_type_to_template_popup( $form ) { $this->add_products_to_options( $form, '_elementor_source' ); } public function add_products_type_to_loop_settings_query( $form ) { $this->add_products_to_options( $form, 'source' ); } public function e_cart_count_fragments( $fragments ) { $product_count = WC()->cart->get_cart_contents_count(); $fragments['.elementor-menu-cart__toggle_button span.elementor-button-text'] = '' . WC()->cart->get_cart_subtotal() . ''; $fragments['.elementor-menu-cart__toggle_button span.elementor-button-icon-qty'] = '' . $product_count . ''; return $fragments; } /** * @param $form * @param $control_name * @return void */ protected function add_products_to_options( $form, $control_name ) { if ( empty( $form ) ) { return; } $controls = $form->get_controls( $control_name ); if ( ! $controls || ! isset( $controls['options'] ) ) { return; } $options = $controls['options']; $options[ self::LOOP_PRODUCT_SKIN_ID ] = esc_html__( 'Products', 'elementor-pro' ); $form->update_control($control_name, [ 'options' => $options, ]); } /** * Add Update Kit Settings Hooks * * Add hooks that update the corresponding kit setting when the WooCommerce option is updated. */ public function add_update_kit_settings_hooks() { add_action( 'update_option_woocommerce_cart_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_cart_page_id', $value ); }, 10, 2 ); add_action( 'update_option_woocommerce_checkout_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_checkout_page_id', $value ); }, 10, 2 ); add_action( 'update_option_woocommerce_myaccount_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_myaccount_page_id', $value ); }, 10, 2 ); add_action( 'update_option_woocommerce_terms_page_id', function( $old_value, $value ) { Plugin::elementor()->kits_manager->update_kit_settings_based_on_option( 'woocommerce_terms_page_id', $value ); }, 10, 2 ); } /** * Elementor WC My Account Logout * * Programatically log out if $_REQUEST['elementor_wc_logout'] is set. * The $_REQUEST variables we have generated a custom logout URL for in the My Account menu. * * @since 3.5.0 */ public function elementor_wc_my_account_logout() { $elementor_wc_logout = ProUtils::_unstable_get_super_global_value( $_REQUEST, 'elementor_wc_logout' ); $nonce = ProUtils::_unstable_get_super_global_value( $_REQUEST, '_wpnonce' ); if ( $elementor_wc_logout && $nonce && wp_verify_nonce( $nonce, 'customer-logout' ) ) { wp_logout(); // Log the user out Programatically. wp_safe_redirect( esc_url( ProUtils::_unstable_get_super_global_value( $_REQUEST, 'elementor_my_account_redirect' ) ) ); // Redirect back to the widget page. exit; } } /** * Add Localize Data * * Makes `woocommercePages` available with the page name and the associated post ID for use with the various * widgets site settings modal. * * @param $settings * @return array */ public function add_localize_data( $settings ) { $settings['woocommerce']['woocommercePages'] = [ 'checkout' => wc_get_page_id( 'checkout' ), 'cart' => wc_get_page_id( 'cart' ), 'myaccount' => wc_get_page_id( 'myaccount' ), 'purchase_summary' => get_option( 'elementor_woocommerce_purchase_summary_page_id' ), ]; return $settings; } /** * Localize Added To Cart On Product Single * * WooCommerce doesn't trigger `added_to_cart` event on its products single page which is required for us to * automatically open our Menu Cart if the settings is chosen. We make the `productAddedToCart` setting * available that we can use in the Menu Cart js to check if a product has just been added. * * @since 3.5.0 */ public function localize_added_to_cart_on_product_single() { add_filter( 'elementor_pro/frontend/localize_settings', function ( $settings ) { $settings['woocommerce']['productAddedToCart'] = true; return $settings; } ); } public function e_notices_body_classes( $classes ) { if ( $this->should_load_wc_notices_styles() ) { foreach ( $this->woocommerce_notices_elements as $notice_element ) { $classes[] = 'e-' . str_replace( '_', '-', $notice_element ) . '-notice'; } } return $classes; } public function e_notices_css( $classes ) { if ( $this->should_load_wc_notices_styles() ) { wp_enqueue_style( 'e-woocommerce-notices', ELEMENTOR_PRO_URL . 'assets/css/woocommerce-notices.min.css', [], ELEMENTOR_PRO_VERSION ); } } public function get_order_received_endpoint_url( $url, $endpoint, $value ) { $order_received_endpoint = get_option( 'woocommerce_checkout_order_received_endpoint', 'order-received' ); if ( $order_received_endpoint === $endpoint ) { $woocommerce_purchase_summary_page_id = get_option( 'elementor_woocommerce_purchase_summary_page_id' ); $order = wc_get_order( $value ); if ( $woocommerce_purchase_summary_page_id && $order ) { $url = trailingslashit( trailingslashit( trailingslashit( get_permalink( $woocommerce_purchase_summary_page_id ) ) . $order_received_endpoint ) . $order->get_id() ); } } return $url; } public function maybe_define_woocommerce_checkout() { $woocommerce_purchase_summary_page_id = get_option( 'elementor_woocommerce_purchase_summary_page_id' ); if ( $woocommerce_purchase_summary_page_id && intval( $woocommerce_purchase_summary_page_id ) === get_queried_object_id() ) { if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { define( 'WOOCOMMERCE_CHECKOUT', true ); } } } /** * Products Query Sources Fragments. * * Since we introduced additional query sources to the Products Widget, * some of these query sources can now be used outside of the Single Product template. * * For example the Related Products and Cross-Sells. * * But now we'll need to make those sections also update when the Cart is updated. So * we'll do this by creating fragments for each of these. * * @since 3.7.0 * * @param array $fragments * * @return array */ public function products_query_sources_fragments( $fragments ) { if ( WC()->cart->get_cart_contents_count() !== 0 ) { $document = Plugin::elementor()->documents->get( url_to_postid( wp_get_referer() ) ); if ( is_object( $document ) ) { $data = $document->get_elements_data(); Plugin::elementor()->db->iterate_data( $data, function( $element ) use ( &$fragments ) { if ( isset( $element['widgetType'] ) && 'woocommerce-products' === $element['widgetType'] ) { $settings = $element['settings']; if ( isset( $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ] ) ) { $query_type = $settings[ Products_Renderer::QUERY_CONTROL_NAME . '_post_type' ]; $query_types_to_check = [ 'related_products', 'upsells', 'cross_sells' ]; if ( in_array( $query_type, $query_types_to_check, true ) ) { switch ( $query_type ) { case 'related_products': $content = self::get_products_related_content( $settings ); break; case 'upsells': $content = self::get_upsells_content( $settings ); break; case 'cross_sells': $content = self::get_cross_sells_content( $settings ); break; default: $content = null; } if ( $content ) { $fragments[ 'div.elementor-element-' . $element['id'] . ' div.elementor-widget-container' ] = '
' . $content . '
'; } } } } } ); } } else { $fragments['div.elementor-widget-container .woocommerce .cross-sells'] = '
'; $fragments['div.elementor-widget-container .woocommerce section.up-sells'] = '
'; } return $fragments; } /** * Get Products Related Content. * * A function to return content for the 'related' products query type in the Products widget. * This function is declared in the Module file so it can be accessed during a WC fragment refresh * and also be used in the Product widget's render method. * * @since 3.7.0 * @access public * * @param array $settings * * @return mixed The content or false */ public static function get_products_related_content( $settings ) { global $product; $product = wc_get_product(); if ( ! $product ) { return; } return self::get_product_widget_content( $settings, 'related_products', 'woocommerce_product_related_products_heading', 'products_related_title_text' ); } /** * Get Upsells Content. * * A function to return content for the 'upsell' query type in the Products widget. * This function is declared in the Module file so it can be accessed during a WC fragment refresh * and also be used in the Product widget's render method. * * @since 3.7.0 * @access public * * @param array $settings * * @return mixed The content or false */ public static function get_upsells_content( $settings ) { return self::get_product_widget_content( $settings, 'upsells', 'woocommerce_product_upsells_products_heading', 'products_upsells_title_text' ); } /** * Get Cross Sells Content. * * A function to return content for the 'cross_sells' query type in the Products widget. * This function is declared in the Module file so it can be accessed during a WC fragment refresh * and also be used in the Product widget's render method. * * @since 3.7.0 * @access public * * @param array $settings * * @return mixed The content or false */ public static function get_cross_sells_content( $settings ) { return self::get_product_widget_content( $settings, 'cross_sells', 'woocommerce_product_cross_sells_products_heading', 'products_cross_sells_title_text' ); } /** * Print Woocommerce Shipping Message * * Format the shipping messages that will be displayed on the Cart and Checkout Widgets. * This will add extra classes to those messages so that we can target certain messages * with certain style controls. * * @since 3.5.0 * * @param string $html the original HTML from WC * @param string $classes the classes we will surround $html with * @return string the final formatted HTML that will be rendered */ private function print_woocommerce_shipping_message( $html, $classes ) { return '' . $html . ''; } /** * Should load WC Notices Styles * * Determine if we should load the WooCommerce notices CSS. * It should only load: * - When we are in the Editor, regardless if any notices have been activated. * - If WooCoomerce is active. * - When we are on the front end, if at least one notice is activated. * * It should not load in WP Admin. * * @return boolean */ private function should_load_wc_notices_styles() { $woocommerce_active = in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ); $is_editor = ProUtils::_unstable_get_super_global_value( $_GET, 'elementor-preview' ); // Editor checks. if ( $woocommerce_active && $is_editor ) { return true; } $kit = Plugin::elementor()->kits_manager->get_active_kit_for_frontend(); $this->woocommerce_notices_elements = is_array( $kit->get_settings_for_display( 'woocommerce_notices_elements' ) ) ? $kit->get_settings_for_display( 'woocommerce_notices_elements' ) : []; // Front end checks. if ( 0 < count( $this->woocommerce_notices_elements ) // At least one notice has been activated. && $woocommerce_active // WooCommerce is active. && ( ! is_admin() || $is_editor ) // We are not in WP Admin. ) { return true; } return false; } /** * Get Product Widget Content. * * A general function to create markup for the new query types in the Products widget. * * @since 3.7.0 * @access private * * @param array $settings The widget settings. * @param string $type The query type to create content for. * @param string $title_hook The hook name to filter in the widget title. * @param string $title_key The control ID for the section title. * * @return mixed The content or false */ private static function get_product_widget_content( $settings, $type, $title_hook, $title_key = '' ) { add_filter( $title_hook, function ( $heading ) use ( $settings, $title_key ) { $title_text = isset( $settings[ $title_key ] ) ? $settings[ $title_key ] : ''; if ( ! empty( $title_text ) ) { return $title_text; } return $heading; }, 10, 1 ); ob_start(); $args = self::parse_product_widget_args( $settings, $type ); if ( 'related_products' === $type ) { woocommerce_related_products( $args ); } elseif ( 'upsells' === $type ) { woocommerce_upsell_display( $args['limit'], $args['columns'], $args['orderby'], $args['order'] ); } else { /** * We need to wrap this content in the 'woocommerce' class for the layout to have the correct styling. * Because this will only be used as a separate widget on the Cart page, * the normal 'woocommerce' div from the cart widget will be closed before this content. */ echo '
'; woocommerce_cross_sell_display( $args['limit'], $args['columns'], $args['orderby'], $args['order'] ); echo '
'; } $products_html = ob_get_clean(); remove_filter( $title_hook, function(){}, 10 ); if ( $products_html ) { $products_html = str_replace( '