css/post-preview.php000064400000002375147207273540010533 0ustar00post_id_for_data = $post_id; $parent_id = wp_get_post_parent_id( $post_id ); parent::__construct( $parent_id ); } protected function get_post_id_for_data() { return $this->post_id_for_data; } /** * Get file handle ID. * * Retrieve the handle ID for the previewed post CSS file. * * @since 1.9.0 * @access protected * * @return string CSS file handle ID. */ protected function get_file_handle_id() { return 'elementor-preview-' . $this->get_post_id_for_data(); } } css/global-css.php000064400000007670147207273540010120 0ustar00render_schemes_and_globals_css(); } /** * Get inline dependency. * * Retrieve the name of the stylesheet used by `wp_add_inline_style()`. * * @since 1.2.0 * @access protected * * @return string Name of the stylesheet. */ protected function get_inline_dependency() { return 'elementor-frontend'; } /** * Is update required. * * Whether the CSS requires an update. When there are new schemes or settings * updates. * * @since 1.2.0 * @access protected * * @return bool True if the CSS requires an update, False otherwise. */ protected function is_update_required() { return $this->get_meta( 'time' ) < get_option( Settings::UPDATE_TIME_FIELD ); } /** * Render schemes CSS. * * Parse the CSS for all the widgets and all the scheme controls. * * @since 1.2.0 * @access private */ private function render_schemes_and_globals_css() { $elementor = Plugin::$instance; /** @var Manager $module */ $kits_manager = Plugin::$instance->kits_manager; $custom_colors_enabled = $kits_manager->is_custom_colors_enabled(); $custom_typography_enabled = $kits_manager->is_custom_typography_enabled(); // If both default colors and typography are disabled, there is no need to render schemes and default global css. if ( ! $custom_colors_enabled && ! $custom_typography_enabled ) { return; } foreach ( $elementor->widgets_manager->get_widget_types() as $widget ) { $controls = $widget->get_controls(); $global_controls = []; $global_values['__globals__'] = []; foreach ( $controls as $control ) { $is_color_control = 'color' === $control['type']; $is_typography_control = isset( $control['groupType'] ) && 'typography' === $control['groupType']; // If it is a color/typography control and default colors/typography are disabled, // don't add the default CSS. if ( ( $is_color_control && ! $custom_colors_enabled ) || ( $is_typography_control && ! $custom_typography_enabled ) ) { continue; } $global_control = $control; // Handle group controls that don't have a default global property. if ( ! empty( $control['groupType'] ) ) { $global_control = $controls[ $control['groupPrefix'] . $control['groupType'] ]; } // If the control has a default global defined, add it to the globals array // that is used in add_control_rules. if ( ! empty( $control['global']['default'] ) ) { $global_values['__globals__'][ $control['name'] ] = $global_control['global']['default']; } if ( ! empty( $global_control['global']['default'] ) ) { $global_controls[] = $control; } } foreach ( $global_controls as $control ) { $this->add_control_rules( $control, $controls, function( $control ) {}, [ '{{WRAPPER}}' ], [ '.elementor-widget-' . $widget->get_name() ], $global_values ); } } } } css/base.php000064400000066175147207273540007011 0ustar00update_file(); $meta = $this->get_meta(); $meta['time'] = time(); $content = $this->get_content(); if ( empty( $content ) ) { $meta['status'] = self::CSS_STATUS_EMPTY; $meta['css'] = ''; } else { $use_external_file = $this->use_external_file(); if ( $use_external_file ) { $meta['status'] = self::CSS_STATUS_FILE; } else { $meta['status'] = self::CSS_STATUS_INLINE; $meta['css'] = $content; } } $meta['dynamic_elements_ids'] = $this->dynamic_elements_ids; $this->update_meta( $meta ); } /** * @since 2.1.0 * @access public */ public function write() { if ( $this->use_external_file() ) { parent::write(); } } /** * @since 3.0.0 * @access public */ public function delete() { if ( $this->use_external_file() ) { parent::delete(); } else { $this->delete_meta(); } } /** * Get Responsive Control Duplication Mode * * @since 3.4.0 * * @return string */ protected function get_responsive_control_duplication_mode() { return 'on'; } /** * Enqueue CSS. * * Either enqueue the CSS file in Elementor or add inline style. * * This method is also responsible for loading the fonts. * * @since 1.2.0 * @access public */ public function enqueue() { $handle_id = $this->get_file_handle_id(); if ( isset( self::$printed[ $handle_id ] ) ) { return; } self::$printed[ $handle_id ] = true; $meta = $this->get_meta(); if ( self::CSS_STATUS_EMPTY === $meta['status'] ) { return; } /** * Enqueue CSS file. * * Fires before enqueuing a CSS file. * * @param Base $this The current CSS file. */ do_action( 'elementor/css-file/before_enqueue', $this ); // First time after clear cache and etc. if ( '' === $meta['status'] || $this->is_update_required() ) { $this->update(); $meta = $this->get_meta(); } if ( self::CSS_STATUS_INLINE === $meta['status'] ) { $dep = $this->get_inline_dependency(); // If the dependency has already been printed ( like a template in footer ) if ( wp_styles()->query( $dep, 'done' ) ) { printf( '', $this->get_file_handle_id(), $meta['css'] ); // XSS ok. } else { wp_add_inline_style( $dep, $meta['css'] ); } } elseif ( self::CSS_STATUS_FILE === $meta['status'] ) { // Re-check if it's not empty after CSS update. wp_enqueue_style( $this->get_file_handle_id(), $this->get_url(), $this->get_enqueue_dependencies(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion } // Handle fonts. if ( ! empty( $meta['fonts'] ) ) { foreach ( $meta['fonts'] as $font ) { Plugin::$instance->frontend->enqueue_font( $font ); } } if ( ! empty( $meta['icons'] ) ) { $icons_types = Icons_Manager::get_icon_manager_tabs(); foreach ( $meta['icons'] as $icon_font ) { if ( ! isset( $icons_types[ $icon_font ] ) ) { continue; } Plugin::$instance->frontend->enqueue_font( $icon_font ); } } $name = $this->get_name(); /** * Enqueue CSS file. * * Fires when CSS file is enqueued on Elementor. * * The dynamic portion of the hook name, `$name`, refers to the CSS file name. * * @since 2.0.0 * * @param Base $this The current CSS file. */ do_action( "elementor/css-file/{$name}/enqueue", $this ); /** * Enqueue CSS file. * * Fires after enqueuing a CSS file. * * @param Base $this The current CSS file. */ do_action( 'elementor/css-file/after_enqueue', $this ); } /** * Print CSS. * * Output the final CSS inside the `'; // XSS ok. Plugin::$instance->frontend->print_fonts_links(); } /** * Add control rules. * * Parse the CSS for all the elements inside any given control. * * This method recursively renders the CSS for all the selectors in the control. * * @since 1.2.0 * @access public * * @param array $control The controls. * @param array $controls_stack The controls stack. * @param callable $value_callback Callback function for the value. * @param array $placeholders Placeholders. * @param array $replacements Replacements. * @param array $values Global Values. */ public function add_control_rules( array $control, array $controls_stack, callable $value_callback, array $placeholders, array $replacements, array $values = [] ) { if ( empty( $control['selectors'] ) ) { return; } $control_global_key = $control['name']; if ( ! empty( $control['groupType'] ) ) { $control_global_key = $control['groupPrefix'] . $control['groupType']; } $global_values = []; $global_key = ''; if ( ! empty( $values['__globals__'] ) ) { $global_values = $values['__globals__']; } if ( ! empty( $global_values[ $control_global_key ] ) ) { $global_key = $global_values[ $control_global_key ]; } if ( ! $global_key ) { $value = call_user_func( $value_callback, $control ); if ( null === $value ) { return; } } $stylesheet = $this->get_stylesheet(); $control = apply_filters( 'elementor/files/css/selectors', $control, $value ?? [], $this ); foreach ( $control['selectors'] as $selector => $css_property ) { $output_css_property = ''; if ( $global_key ) { $selector_global_value = $this->get_selector_global_value( $control, $global_key ); if ( $selector_global_value ) { $output_css_property = preg_replace( '/(:)[^;]+(;?)/', '$1' . $selector_global_value . '$2', $css_property ); } } else { try { if ( $this->unit_has_custom_selector( $control, $value ) ) { $css_property = $control['unit_selectors_dictionary'][ $value['unit'] ]; } $output_css_property = preg_replace_callback( '/{{(?:([^.}]+)\.)?([^}| ]*)(?: *\|\| *(?:([^.}]+)\.)?([^}| ]*) *)*}}/', function( $matches ) use ( $control, $value_callback, $controls_stack, $value, $css_property ) { $external_control_missing = $matches[1] && ! isset( $controls_stack[ $matches[1] ] ); $parsed_value = ''; $value = apply_filters( 'elementor/files/css/property', $value, $css_property, $matches, $control ); if ( ! $external_control_missing ) { $parsed_value = $this->parse_property_placeholder( $control, $value, $controls_stack, $value_callback, $matches[2], $matches[1] ); } if ( '' === $parsed_value ) { if ( isset( $matches[4] ) ) { $parsed_value = $matches[4]; $is_string_value = preg_match( '/^([\'"])(.*)\1$/', $parsed_value, $string_matches ); if ( $is_string_value ) { $parsed_value = $string_matches[2]; } elseif ( ! is_numeric( $parsed_value ) ) { if ( $matches[3] && ! isset( $controls_stack[ $matches[3] ] ) ) { return ''; } $parsed_value = $this->parse_property_placeholder( $control, $value, $controls_stack, $value_callback, $matches[4], $matches[3] ); } } if ( '' === $parsed_value ) { if ( $external_control_missing ) { return ''; } throw new \Exception(); } } if ( '__EMPTY__' === $parsed_value ) { $parsed_value = ''; } return $parsed_value; }, $css_property ); } catch ( \Exception $e ) { return; } } if ( ! $output_css_property ) { continue; } $device_pattern = '/^(?:\([^\)]+\)){1,2}/'; preg_match( $device_pattern, $selector, $device_rules ); $query = []; if ( $device_rules ) { $selector = preg_replace( $device_pattern, '', $selector ); preg_match_all( '/\(([^)]+)\)/', $device_rules[0], $pure_device_rules ); $pure_device_rules = $pure_device_rules[1]; foreach ( $pure_device_rules as $device_rule ) { if ( Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP === $device_rule ) { continue; } $device = preg_replace( '/\+$/', '', $device_rule ); $endpoint = $device === $device_rule ? 'max' : 'min'; $query[ $endpoint ] = $device; } } $parsed_selector = str_replace( $placeholders, $replacements, $selector ); if ( ! $query && ! empty( $control['responsive'] ) ) { $query = array_intersect_key( $control['responsive'], array_flip( [ 'min', 'max' ] ) ); if ( ! empty( $query['max'] ) && Breakpoints_Manager::BREAKPOINT_KEY_DESKTOP === $query['max'] ) { unset( $query['max'] ); } } $stylesheet->add_rules( $parsed_selector, $output_css_property, $query ); } } protected function unit_has_custom_selector( $control, $value ) { return isset( $control['unit_selectors_dictionary'] ) && isset( $control['unit_selectors_dictionary'][ $value['unit'] ] ); } /** * @param array $control * @param mixed $value * @param array $controls_stack * @param callable $value_callback * @param string $placeholder * @param string $parser_control_name * * @return string */ public function parse_property_placeholder( array $control, $value, array $controls_stack, $value_callback, $placeholder, $parser_control_name = null ) { if ( $parser_control_name ) { // If both the processed control and the control name found in the placeholder are responsive if ( ! empty( $control['responsive'] ) && ! empty( $controls_stack[ $parser_control_name ]['responsive'] ) ) { $device_suffix = Controls_Manager::get_responsive_control_device_suffix( $control ); $control = $controls_stack[ $parser_control_name . $device_suffix ] ?? $controls_stack[ $parser_control_name ]; } else { $control = $controls_stack[ $parser_control_name ]; } $value = call_user_func( $value_callback, $control ); } // If the control value is empty, check for global default. `0` (integer, string) are falsy but are valid values. if ( empty( $value ) && '0' !== $value && 0 !== $value ) { $value = $this->get_control_global_default_value( $control ); } if ( Controls_Manager::FONT === $control['type'] ) { $this->fonts[] = $value; } /** @var Base_Data_Control $control_obj */ $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] ); return (string) $control_obj->get_style_value( $placeholder, $value, $control ); } /** * Get the fonts. * * Retrieve the list of fonts. * * @since 1.9.0 * @access public * * @return array Fonts. */ public function get_fonts() { return $this->fonts; } /** * Get stylesheet. * * Retrieve the CSS file stylesheet instance. * * @since 1.2.0 * @access public * * @return Stylesheet The stylesheet object. */ public function get_stylesheet() { if ( ! $this->stylesheet_obj ) { $this->init_stylesheet(); } return $this->stylesheet_obj; } /** * Add controls stack style rules. * * Parse the CSS for all the elements inside any given controls stack. * * This method recursively renders the CSS for all the child elements in the stack. * * @since 1.6.0 * @access public * * @param Controls_Stack $controls_stack The controls stack. * @param array $controls Controls array. * @param array $values Values array. * @param array $placeholders Placeholders. * @param array $replacements Replacements. * @param array $all_controls All controls. */ public function add_controls_stack_style_rules( Controls_Stack $controls_stack, array $controls, array $values, array $placeholders, array $replacements, array $all_controls = null ) { if ( ! $all_controls ) { $all_controls = $controls_stack->get_controls(); } $parsed_dynamic_settings = $controls_stack->parse_dynamic_settings( $values, $controls ); foreach ( $controls as $control ) { if ( ! empty( $control['style_fields'] ) ) { $this->add_repeater_control_style_rules( $controls_stack, $control, $values[ $control['name'] ], $placeholders, $replacements ); } if ( ! empty( $control[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ) ) { $this->add_dynamic_control_style_rules( $control, $control[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ); } if ( Controls_Manager::ICONS === $control['type'] ) { $this->icons_fonts[] = $values[ $control['name'] ]['library']; } if ( ! empty( $parsed_dynamic_settings[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ) ) { // Dynamic CSS should not be added to the CSS files. // Instead it's handled by \Elementor\Core\DynamicTags\Dynamic_CSS // and printed in a style tag. unset( $parsed_dynamic_settings[ $control['name'] ] ); $this->dynamic_elements_ids[] = $controls_stack->get_id(); continue; } if ( empty( $control['selectors'] ) ) { continue; } $this->add_control_style_rules( $control, $parsed_dynamic_settings, $all_controls, $placeholders, $replacements ); } } /** * Get file handle ID. * * Retrieve the file handle ID. * * @since 1.2.0 * @access protected * @abstract * * @return string CSS file handle ID. */ abstract protected function get_file_handle_id(); /** * Render CSS. * * Parse the CSS. * * @since 1.2.0 * @access protected * @abstract */ abstract protected function render_css(); protected function get_default_meta() { return array_merge( parent::get_default_meta(), [ 'fonts' => array_unique( $this->fonts ), 'icons' => array_unique( $this->icons_fonts ), 'dynamic_elements_ids' => [], 'status' => '', ] ); } /** * Get enqueue dependencies. * * Retrieve the name of the stylesheet used by `wp_enqueue_style()`. * * @since 1.2.0 * @access protected * * @return array Name of the stylesheet. */ protected function get_enqueue_dependencies() { return []; } /** * Get inline dependency. * * Retrieve the name of the stylesheet used by `wp_add_inline_style()`. * * @since 1.2.0 * @access protected * * @return string Name of the stylesheet. */ protected function get_inline_dependency() { return ''; } /** * Is update required. * * Whether the CSS requires an update. When there are new schemes or settings * updates. * * @since 1.2.0 * @access protected * * @return bool True if the CSS requires an update, False otherwise. */ protected function is_update_required() { return false; } /** * Parse CSS. * * Parsing the CSS file. * * @since 1.2.0 * @access protected */ protected function parse_content() { Performance::set_use_style_controls( true ); $initial_responsive_controls_duplication_mode = Plugin::$instance->breakpoints->get_responsive_control_duplication_mode(); Plugin::$instance->breakpoints->set_responsive_control_duplication_mode( $this->get_responsive_control_duplication_mode() ); $this->render_css(); $name = $this->get_name(); /** * Parse CSS file. * * Fires when CSS file is parsed on Elementor. * * The dynamic portion of the hook name, `$name`, refers to the CSS file name. * * @since 2.0.0 * * @param Base $this The current CSS file. */ do_action( "elementor/css-file/{$name}/parse", $this ); Plugin::$instance->breakpoints->set_responsive_control_duplication_mode( $initial_responsive_controls_duplication_mode ); Performance::set_use_style_controls( false ); return $this->get_stylesheet()->__toString(); } /** * Add control style rules. * * Register new style rules for the control. * * @since 1.6.0 * @access private * * @param array $control The control. * @param array $values Values array. * @param array $controls The controls stack. * @param array $placeholders Placeholders. * @param array $replacements Replacements. */ protected function add_control_style_rules( array $control, array $values, array $controls, array $placeholders, array $replacements ) { $this->add_control_rules( $control, $controls, function( $control ) use ( $values ) { return $this->get_style_control_value( $control, $values ); }, $placeholders, $replacements, $values ); } /** * Get Control Global Default Value * * If the control has a global default value, and the corresponding global default setting is enabled, this method * fetches and returns the global default value. Otherwise, it returns null. * * @since 3.7.0 * @access private * * @param $control * @return string|null */ private function get_control_global_default_value( $control ) { if ( empty( $control['global']['default'] ) ) { return null; } // If the control value is empty, and the control has a global default set, fetch the global value and use it. $global_enabled = false; if ( 'color' === $control['type'] ) { $global_enabled = Plugin::$instance->kits_manager->is_custom_colors_enabled(); } elseif ( isset( $control['groupType'] ) && 'typography' === $control['groupType'] ) { $global_enabled = Plugin::$instance->kits_manager->is_custom_typography_enabled(); } $value = null; // Only apply the global default if Global Colors are enabled. if ( $global_enabled ) { $value = $this->get_selector_global_value( $control, $control['global']['default'] ); } return $value; } /** * Get style control value. * * Retrieve the value of the style control for any give control and values. * * It will retrieve the control name and return the style value. * * @since 1.6.0 * @access private * * @param array $control The control. * @param array $values Values array. * * @return mixed Style control value. */ private function get_style_control_value( array $control, array $values ) { if ( ! empty( $values['__globals__'][ $control['name'] ] ) ) { // When the control itself has no global value, but it refers to another control global value return $this->get_selector_global_value( $control, $values['__globals__'][ $control['name'] ] ); } $value = $values[ $control['name'] ]; if ( isset( $control['selectors_dictionary'][ $value ] ) ) { $value = $control['selectors_dictionary'][ $value ]; } if ( ! is_numeric( $value ) && ! is_float( $value ) && empty( $value ) ) { return null; } return $value; } /** * Init stylesheet. * * Initialize CSS file stylesheet by creating a new `Stylesheet` object and register new * breakpoints for the stylesheet. * * @since 1.2.0 * @access private */ private function init_stylesheet() { $this->stylesheet_obj = new Stylesheet(); $active_breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints(); foreach ( $active_breakpoints as $breakpoint_name => $breakpoint ) { $this->stylesheet_obj->add_device( $breakpoint_name, $breakpoint->get_value() ); } } /** * Add repeater control style rules. * * Register new style rules for the repeater control. * * @since 2.0.0 * @access private * * @param Controls_Stack $controls_stack The control stack. * @param array $repeater_control The repeater control. * @param array $repeater_values Repeater values array. * @param array $placeholders Placeholders. * @param array $replacements Replacements. */ protected function add_repeater_control_style_rules( Controls_Stack $controls_stack, array $repeater_control, array $repeater_values, array $placeholders, array $replacements ) { $placeholders = array_merge( $placeholders, [ '{{CURRENT_ITEM}}' ] ); foreach ( $repeater_control['style_fields'] as $index => $item ) { $this->add_controls_stack_style_rules( $controls_stack, $item, $repeater_values[ $index ], $placeholders, array_merge( $replacements, [ '.elementor-repeater-item-' . $repeater_values[ $index ]['_id'] ] ), $repeater_control['fields'] ); } } /** * Add dynamic control style rules. * * Register new style rules for the dynamic control. * * @since 2.0.0 * @access private * * @param array $control The control. * @param string $value The value. */ protected function add_dynamic_control_style_rules( array $control, $value ) { Plugin::$instance->dynamic_tags->parse_tags_text( $value, $control, function( $id, $name, $settings ) { $tag = Plugin::$instance->dynamic_tags->create_tag( $id, $name, $settings ); if ( ! $tag instanceof Tag ) { return; } $this->add_controls_stack_style_rules( $tag, $this->get_style_controls( $tag ), $tag->get_active_settings(), [ '{{WRAPPER}}' ], [ '#elementor-tag-' . $id ] ); } ); } private function get_selector_global_value( $control, $global_key ) { $data = Plugin::$instance->data_manager_v2->run( $global_key ); if ( empty( $data['value'] ) ) { return null; } $global_args = explode( '?id=', $global_key ); $id = $global_args[1]; if ( ! empty( $control['groupType'] ) ) { $strings_to_replace = [ $control['groupPrefix'] ]; $active_breakpoint_keys = array_keys( Plugin::$instance->breakpoints->get_active_breakpoints() ); foreach ( $active_breakpoint_keys as $breakpoint ) { $strings_to_replace[] = '_' . $breakpoint; } $property_name = str_replace( $strings_to_replace, '', $control['name'] ); // TODO: This check won't retrieve the proper answer for array values (multiple controls). if ( empty( $data['value'][ Global_Typography::TYPOGRAPHY_GROUP_PREFIX . $property_name ] ) ) { return null; } $property_name = str_replace( '_', '-', $property_name ); $value = "var( --e-global-$control[groupType]-$id-$property_name )"; if ( $control['groupPrefix'] . 'font_family' === $control['name'] ) { $default_generic_fonts = Plugin::$instance->kits_manager->get_current_settings( 'default_generic_fonts' ); if ( $default_generic_fonts ) { $value .= ", $default_generic_fonts"; } } } else { $value = "var( --e-global-$control[type]-$id )"; } return $value; } final protected function get_active_controls( Controls_Stack $controls_stack, array $controls = null, array $settings = null ) { if ( ! $controls ) { $controls = $controls_stack->get_controls(); } if ( ! $settings ) { $settings = $controls_stack->get_controls_settings(); } if ( $this->is_global_parsing_supported() ) { $settings = $this->parse_global_settings( $settings, $controls ); } $active_controls = array_reduce( array_keys( $controls ), function( $active_controls, $control_key ) use ( $controls_stack, $controls, $settings ) { $control = $controls[ $control_key ]; if ( $controls_stack->is_control_visible( $control, $settings, $controls ) ) { $active_controls[ $control_key ] = $control; } return $active_controls; }, [] ); return $active_controls; } final public function get_style_controls( Controls_Stack $controls_stack, array $controls = null, array $settings = null ) { $controls = $this->get_active_controls( $controls_stack, $controls, $settings ); $style_controls = []; foreach ( $controls as $control_name => $control ) { $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] ); if ( ! $control_obj instanceof Base_Data_Control ) { continue; } $control = array_merge( $control_obj->get_settings(), $control ); if ( $control_obj instanceof Control_Repeater ) { $style_fields = []; foreach ( $controls_stack->get_settings( $control_name ) as $item ) { $style_fields[] = $this->get_style_controls( $controls_stack, $control['fields'], $item ); } $control['style_fields'] = $style_fields; } if ( ! empty( $control['selectors'] ) || ! empty( $control['dynamic'] ) || $this->is_global_control( $controls_stack, $control_name, $controls ) || ! empty( $control['style_fields'] ) ) { $style_controls[ $control_name ] = $control; } } return $style_controls; } private function parse_global_settings( array $settings, array $controls ) { foreach ( $controls as $control ) { $control_name = $control['name']; $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] ); if ( ! $control_obj instanceof Base_Data_Control ) { continue; } if ( $control_obj instanceof Control_Repeater ) { foreach ( $settings[ $control_name ] as & $field ) { $field = $this->parse_global_settings( $field, $control['fields'] ); } continue; } if ( empty( $control['global']['active'] ) ) { continue; } if ( empty( $settings['__globals__'][ $control_name ] ) ) { continue; } $settings[ $control_name ] = 'global'; } return $settings; } private function is_global_control( Controls_Stack $controls_stack, $control_name, $controls ) { $control = $controls[ $control_name ]; $control_global_key = $control_name; if ( ! empty( $control['groupType'] ) ) { $control_global_key = $control['groupPrefix'] . $control['groupType']; } if ( empty( $controls[ $control_global_key ]['global']['active'] ) ) { return false; } $globals = $controls_stack->get_settings( '__globals__' ); return ! empty( $globals[ $control_global_key ] ); } } css/post-local-cache.php000064400000001366147207273540011204 0ustar00meta_cache; } protected function delete_meta() { $this->meta_cache = []; } protected function update_meta( $meta ) { $this->meta_cache = $meta; } protected function get_data() { $document = Plugin::$instance->documents->get( $this->get_post_id_for_data() ); return $document ? $document->get_elements_data() : []; } } css/post.php000064400000017050147207273540007050 0ustar00post_id = $post_id; parent::__construct( static::FILE_PREFIX . $post_id . '.css' ); } /** * Get CSS file name. * * Retrieve the CSS file name. * * @since 1.6.0 * @access public * * @return string CSS file name. */ public function get_name() { return 'post'; } /** * Get post ID. * * Retrieve the ID of current post. * * @since 1.2.0 * @access public * * @return int Post ID. */ public function get_post_id() { return $this->post_id; } /** * Get unique element selector. * * Retrieve the unique selector for any given element. * * @since 1.2.0 * @access public * * @param Element_Base $element The element. * * @return string Unique element selector. */ public function get_element_unique_selector( Element_Base $element ) { return '.elementor-' . $this->post_id . ' .elementor-element' . $element->get_unique_selector(); } /** * Load meta data. * * Retrieve the post CSS file meta data. * * @since 1.2.0 * @access protected * * @return array Post CSS file meta data. */ protected function load_meta() { return get_post_meta( $this->post_id, static::META_KEY, true ); } /** * Update meta data. * * Update the global CSS file meta data. * * @since 1.2.0 * @access protected * * @param array $meta New meta data. */ protected function update_meta( $meta ) { update_post_meta( $this->post_id, static::META_KEY, $meta ); } /** * Delete meta. * * Delete the file meta data. * * @since 2.1.0 * @access protected */ protected function delete_meta() { delete_post_meta( $this->post_id, static::META_KEY ); } public function write() { parent::write(); if ( ! empty( $this->additional_style_dependencies ) ) { $meta = $this->get_meta(); $meta['additional_style_dependencies'] = $this->additional_style_dependencies; $this->update_meta( $meta ); } } /** * Get post data. * * Retrieve raw post data from the database. * * @since 1.9.0 * @access protected * * @return array Post data. */ protected function get_data() { $document = Plugin::$instance->documents->get( $this->post_id ); return $document ? $document->get_elements_data() : []; } /** * Render CSS. * * Parse the CSS for all the elements. * * @since 1.2.0 * @access protected */ protected function render_css() { $data = $this->get_data(); if ( ! empty( $data ) ) { foreach ( $data as $element_data ) { $element = Plugin::$instance->elements_manager->create_element_instance( $element_data ); if ( ! $element ) { continue; } $this->render_styles( $element ); } } } /** * Enqueue CSS. * * Enqueue the post CSS file in Elementor. * * This method ensures that the post was actually built with elementor before * enqueueing the post CSS file. * * @since 1.2.2 * @access public */ public function enqueue() { $document = Plugin::$instance->documents->get( $this->post_id ); if ( ! $document || ! $document->is_built_with_elementor() ) { return; } parent::enqueue(); } /** * Add controls-stack style rules. * * Parse the CSS for all the elements inside any given controls stack. * * This method recursively renders the CSS for all the child elements in the stack. * * @since 1.6.0 * @access public * * @param Controls_Stack $controls_stack The controls stack. * @param array $controls Controls array. * @param array $values Values array. * @param array $placeholders Placeholders. * @param array $replacements Replacements. * @param array $all_controls All controls. */ public function add_controls_stack_style_rules( Controls_Stack $controls_stack, array $controls, array $values, array $placeholders, array $replacements, array $all_controls = null ) { parent::add_controls_stack_style_rules( $controls_stack, $controls, $values, $placeholders, $replacements, $all_controls ); if ( $controls_stack instanceof Element_Base ) { foreach ( $controls_stack->get_children() as $child_element ) { $this->render_styles( $child_element ); } } } /** * Get enqueue dependencies. * * Retrieve the name of the stylesheet used by `wp_enqueue_style()`. * * @since 1.2.0 * @access protected * * @return array Name of the stylesheet. */ protected function get_enqueue_dependencies() { $enqueue_dependencies = [ 'elementor-frontend' ]; $additional_style_dependencies = $this->get_meta( 'additional_style_dependencies' ); if ( ! empty( $additional_style_dependencies ) ) { $enqueue_dependencies = array_merge( $enqueue_dependencies, $additional_style_dependencies ); } return $enqueue_dependencies; } /** * Get inline dependency. * * Retrieve the name of the stylesheet used by `wp_add_inline_style()`. * * @since 1.2.0 * @access protected * * @return string Name of the stylesheet. */ protected function get_inline_dependency() { return 'elementor-frontend'; } /** * Get file handle ID. * * Retrieve the handle ID for the post CSS file. * * @since 1.2.0 * @access protected * * @return string CSS file handle ID. */ protected function get_file_handle_id() { return 'elementor-post-' . $this->post_id; } /** * Render styles. * * Parse the CSS for any given element. * * @since 1.2.0 * @access protected * * @param Element_Base $element The element. */ protected function render_styles( Element_Base $element ) { /** * Before element parse CSS. * * Fires before the CSS of the element is parsed. * * @since 1.2.0 * * @param Post $this The post CSS file. * @param Element_Base $element The element. */ do_action( 'elementor/element/before_parse_css', $this, $element ); $element_settings = $element->get_settings(); $this->add_controls_stack_style_rules( $element, $this->get_style_controls( $element, null, $element->get_parsed_dynamic_settings() ), $element_settings, [ '{{ID}}', '{{WRAPPER}}' ], [ $element->get_id(), $this->get_element_unique_selector( $element ) ] ); $element_style_depend = $element->get_style_depends(); if ( ! empty( $element_style_depend ) ) { $this->additional_style_dependencies = array_unique( array_merge( $this->additional_style_dependencies, $element_style_depend ) ); } /** * After element parse CSS. * * Fires after the CSS of the element is parsed. * * @since 1.2.0 * * @param Post $this The post CSS file. * @param Element_Base $element The element. */ do_action( 'elementor/element/parse_css', $this, $element ); } } file-types/svg.php000064400000013230147207273540010147 0ustar00sanitize_file( $filename ); } /** * Validate File * * @since 3.3.0 * @access public * * @param $file * @return bool|\WP_Error */ public function validate_file( $file ) { if ( ! $this->sanitize_svg( $file['tmp_name'] ) ) { return new \WP_Error( Exceptions::FORBIDDEN, esc_html__( 'This file is not allowed for security reasons.', 'elementor' ) ); } return true; } /** * Sanitizer * * @since 3.5.0 * @access public * * @param $content * @return bool|string */ public function sanitizer( $content ) { return ( new SVG_Sanitizer() )->sanitize( $content ); } /** * WP Prepare Attachment For J * * Runs on the `wp_prepare_attachment_for_js` filter. * * @since 3.5.0 * @access public * * @param $attachment_data * @param $attachment * @param $meta * * @return mixed */ public function wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta ) { if ( 'image' !== $attachment_data['type'] || 'svg+xml' !== $attachment_data['subtype'] || ! class_exists( 'SimpleXMLElement' ) ) { return $attachment_data; } $svg = self::get_inline_svg( $attachment->ID ); if ( ! $svg ) { return $attachment_data; } try { $svg = new \SimpleXMLElement( $svg ); } catch ( \Exception $e ) { return $attachment_data; } $src = $attachment_data['url']; $width = (int) $svg['width']; $height = (int) $svg['height']; // Media Gallery $attachment_data['image'] = compact( 'src', 'width', 'height' ); $attachment_data['thumb'] = compact( 'src', 'width', 'height' ); // Single Details of Image $attachment_data['sizes']['full'] = [ 'height' => $height, 'width' => $width, 'url' => $src, 'orientation' => $height > $width ? 'portrait' : 'landscape', ]; return $attachment_data; } /** * Set Svg Meta Data * * Adds dimensions metadata to uploaded SVG files, since WordPress doesn't do it. * * @since 3.5.0 * @access public * * @return mixed */ public function set_svg_meta_data( $data, $id ) { $attachment = get_post( $id ); // Filter makes sure that the post is an attachment. $mime_type = $attachment->post_mime_type; // If the attachment is an svg if ( 'image/svg+xml' === $mime_type ) { // If the svg metadata are empty or the width is empty or the height is empty. // then get the attributes from xml. if ( empty( $data ) || empty( $data['width'] ) || empty( $data['height'] ) ) { $attachment = wp_get_attachment_url( $id ); $xml = simplexml_load_file( $attachment ); if ( ! empty( $xml ) ) { $attr = $xml->attributes(); $view_box = explode( ' ', $attr->viewBox );// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase $data['width'] = isset( $attr->width ) && preg_match( '/\d+/', $attr->width, $value ) ? (int) $value[0] : ( 4 === count( $view_box ) ? (int) $view_box[2] : null ); $data['height'] = isset( $attr->height ) && preg_match( '/\d+/', $attr->height, $value ) ? (int) $value[0] : ( 4 === count( $view_box ) ? (int) $view_box[3] : null ); } } } return $data; } /** * Delete Meta Cache * * Deletes the Inline SVG post meta entry. * * @since 3.5.0 * @access public */ public function delete_meta_cache() { delete_post_meta_by_key( self::META_KEY ); } /** * File Sanitizer Can Run * * Checks if the classes required for the file sanitizer are in memory. * * @since 3.5.0 * @access public * @static * * @return bool */ public static function file_sanitizer_can_run() { return class_exists( 'DOMDocument' ) && class_exists( 'SimpleXMLElement' ); } /** * Get Inline SVG * * @since 3.5.0 * @access public * @static * * @param $attachment_id * @return bool|mixed|string */ public static function get_inline_svg( $attachment_id ) { $svg = get_post_meta( $attachment_id, self::META_KEY, true ); if ( ! empty( $svg ) ) { $valid_svg = ( new SVG_Sanitizer() )->sanitize( $svg ); return ( false === $valid_svg ) ? '' : $valid_svg; } $attachment_file = get_attached_file( $attachment_id ); if ( ! file_exists( $attachment_file ) ) { return ''; } $svg = Utils::file_get_contents( $attachment_file ); $valid_svg = ( new SVG_Sanitizer() )->sanitize( $svg ); if ( false === $valid_svg ) { return ''; } if ( ! empty( $valid_svg ) ) { update_post_meta( $attachment_id, self::META_KEY, $valid_svg ); } return $valid_svg; } public function __construct() { add_filter( 'wp_update_attachment_metadata', [ $this, 'set_svg_meta_data' ], 10, 2 ); add_filter( 'wp_prepare_attachment_for_js', [ $this, 'wp_prepare_attachment_for_js' ], 10, 3 ); add_action( 'elementor/core/files/clear_cache', [ $this, 'delete_meta_cache' ] ); } } file-types/base.php000064400000002503147207273540010263 0ustar00uploads_manager->create_unique_dir(); } $zip->open( $file_path ); // if an array of allowed file types is provided, get the filtered file list to extract. $allowed_files = $allowed_file_types ? $this->get_allowed_files( $zip, $allowed_file_types ) : null; $zip->extractTo( $extraction_directory, $allowed_files ); $zip->close(); return [ 'extraction_directory' => $extraction_directory, 'files' => $this->find_temp_files( $extraction_directory ), ]; } /** * Get Allowed Files * * Accepts a zipArchive instance and an array of allowed file types. Iterates over the zip archive's files and * checks if their extensions are in the list of allowed file types. Returns an array containing all valid files. * * @since 3.3.0 * * @param \ZipArchive $zip * @param array $allowed_file_types * @return array */ private function get_allowed_files( $zip, $allowed_file_types ) { $allowed_files = []; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase for ( $i = 0; $i < $zip->numFiles; $i++ ) { $filename = $zip->getNameIndex( $i ); $extension = pathinfo( $filename, PATHINFO_EXTENSION ); // Skip files with transversal paths. if ( strpos( $filename, '..' ) !== false ) { continue; } if ( in_array( $extension, $allowed_file_types, true ) ) { $allowed_files[] = $filename; } } return $allowed_files; } /** * Find temporary files. * * Recursively finds a list of temporary files from the extracted zip file. * * Example return data: * * [ * 0 => '/www/wp-content/uploads/elementor/tmp/5eb3a7a411d44/templates/block-2-col-marble-title.json', * 1 => '/www/wp-content/uploads/elementor/tmp/5eb3a7a411d44/templates/block-2-col-text-and-photo.json', * ] * * @since 2.9.8 * @access private * * @param string $temp_path - The temporary file path to scan for template files * * @return array An array of temporary files on the filesystem */ private function find_temp_files( $temp_path ) { $file_names = []; $possible_file_names = array_diff( scandir( $temp_path ), [ '.', '..' ] ); // Find nested files in the unzipped path. This happens for example when the user imports a Website Kit. foreach ( $possible_file_names as $possible_file_name ) { $full_possible_file_name = $temp_path . $possible_file_name; if ( is_dir( $full_possible_file_name ) ) { $file_names = array_merge( $file_names, $this->find_temp_files( $full_possible_file_name . '/' ) ); } else { $file_names[] = $full_possible_file_name; } } return $file_names; } } base.php000064400000013135147207273540006205 0ustar00files_manager->get( get_called_class(), func_get_args() ); } /** * @since 2.1.0 * @access public */ public function __construct( $file_name ) { /** * Elementor File Name * * Filters the File name * * @since 2.3.0 * * @param string $file_name * @param object $this The file instance, which inherits Elementor\Core\Files */ $file_name = apply_filters( 'elementor/files/file_name', $file_name, $this ); $this->set_file_name( $file_name ); $this->set_files_dir( static::DEFAULT_FILES_DIR ); $this->set_path(); } /** * @since 2.1.0 * @access public */ public function set_files_dir( $files_dir ) { $this->files_dir = $files_dir; } /** * @since 2.1.0 * @access public */ public function set_file_name( $file_name ) { $this->file_name = $file_name; } /** * @since 2.1.0 * @access public */ public function get_file_name() { return $this->file_name; } /** * @since 2.1.0 * @access public */ public function get_url() { $url = set_url_scheme( self::get_base_uploads_url() . $this->files_dir . $this->file_name ); return add_query_arg( [ 'ver' => $this->get_meta( 'time' ) ], $url ); } /** * Get Path * * Returns the local path of the generated file. * * @since 3.5.0 * @access public * * @return string */ public function get_path() { return set_url_scheme( self::get_base_uploads_dir() . $this->files_dir . $this->file_name ); } /** * @since 2.1.0 * @access public */ public function get_content() { if ( ! $this->content ) { $this->content = $this->parse_content(); } return $this->content; } /** * @since 2.1.0 * @access public */ public function update() { $this->update_file(); $meta = $this->get_meta(); $meta['time'] = time(); $this->update_meta( $meta ); } /** * @since 2.1.0 * @access public */ public function update_file() { $this->content = $this->parse_content(); if ( $this->content ) { $this->write(); } else { $this->delete(); } } /** * @since 2.1.0 * @access public */ public function write() { return file_put_contents( $this->path, $this->content ); } /** * @since 2.1.0 * @access public */ public function delete() { if ( file_exists( $this->path ) ) { unlink( $this->path ); } $this->delete_meta(); } /** * Get meta data. * * Retrieve the CSS file meta data. Returns an array of all the data, or if * custom property is given it will return the property value, or `null` if * the property does not exist. * * @since 2.1.0 * @access public * * @param string $property Optional. Custom meta data property. Default is * null. * * @return array|null An array of all the data, or if custom property is * given it will return the property value, or `null` if * the property does not exist. */ public function get_meta( $property = null ) { $meta = array_merge( $this->get_default_meta(), (array) $this->load_meta() ); if ( $property ) { return isset( $meta[ $property ] ) ? $meta[ $property ] : null; } return $meta; } /** * @since 2.1.0 * @access protected * @abstract */ abstract protected function parse_content(); /** * Load meta. * * Retrieve the file meta data. * * @since 2.1.0 * @access protected */ protected function load_meta() { return get_option( static::META_KEY ); } /** * Update meta. * * Update the file meta data. * * @since 2.1.0 * @access protected * * @param array $meta New meta data. */ protected function update_meta( $meta ) { update_option( static::META_KEY, $meta ); } /** * Delete meta. * * Delete the file meta data. * * @since 2.1.0 * @access protected */ protected function delete_meta() { delete_option( static::META_KEY ); } /** * @since 2.1.0 * @access protected */ protected function get_default_meta() { return [ 'time' => 0, ]; } /** * @since 2.1.0 * @access private * @static */ private static function get_wp_uploads_dir() { global $blog_id; if ( empty( self::$wp_uploads_dir[ $blog_id ] ) ) { self::$wp_uploads_dir[ $blog_id ] = wp_upload_dir( null, false ); } return self::$wp_uploads_dir[ $blog_id ]; } /** * @since 2.1.0 * @access private */ private function set_path() { $dir_path = self::get_base_uploads_dir() . $this->files_dir; if ( ! is_dir( $dir_path ) ) { wp_mkdir_p( $dir_path ); } $this->path = $dir_path . $this->file_name; } } uploads-manager.php000064400000044242147207273540010355 0ustar00 new Json(), 'zip' => new Zip(), 'svg' => new Svg(), ]; foreach ( $file_types as $file_type => $file_handler ) { $this->file_type_handlers[ $file_type ] = $file_handler; } } /** * Extract and Validate Zip * * This method accepts a $file array (which minimally should include a 'tmp_name') * * @since 3.3.0 * @access public * * @param string $file_path * @param array $allowed_file_types * @return array|\WP_Error */ public function extract_and_validate_zip( $file_path, $allowed_file_types = null ) { $result = []; /** @var Zip $zip_handler - File Type */ $zip_handler = $this->file_type_handlers['zip']; // Returns an array of file paths. $extracted = $zip_handler->extract( $file_path, $allowed_file_types ); if ( is_wp_error( $extracted ) ) { return $extracted; } // If there are no extracted file names, no files passed the extraction validation. if ( empty( $extracted['files'] ) ) { // TODO: Decide what to do if no files passed the extraction validation return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT ); } $result['extraction_directory'] = $extracted['extraction_directory']; foreach ( $extracted['files'] as $extracted_file_path ) { // Each file is an array with a 'name' (file path) property. if ( ! is_wp_error( $this->validate_file( [ 'tmp_name' => $extracted_file_path ] ) ) ) { $result['files'][] = $extracted_file_path; } } return $result; } /** * Handle Elementor Upload * * This method receives a $file array. If the received file is a Base64 string, the $file array should include a * 'fileData' property containing the string, which is decoded and has its contents stored in a temporary file. * If the $file parameter passed is a standard $file array, the 'name' and 'tmp_name' properties are used for * validation. * * The file goes through validation; if it passes validation, the file is returned. Otherwise, an error is returned. * * @since 3.3.0 * @access public * * @param array $data * @param array $allowed_file_extensions Optional. an array of file types that are allowed to pass validation for each * upload. * @return array|\WP_Error */ public function handle_elementor_upload( array $data, $allowed_file_extensions = null ) { // If $file['fileData'] is set, it signals that the passed file is a Base64 string that needs to be decoded and // saved to a temporary file. if ( isset( $data['fileData'] ) ) { $data = $this->save_base64_to_tmp_file( $data, $allowed_file_extensions ); } if ( is_wp_error( $data ) ) { return $data; } $validation_result = $this->validate_file( $data, $allowed_file_extensions ); if ( is_wp_error( $validation_result ) ) { return $validation_result; } return $data; } /** * are Unfiltered Uploads Enabled * * @since 3.5.0 * @access public * * @return bool */ final public static function are_unfiltered_uploads_enabled() { $enabled = ! ! get_option( self::UNFILTERED_FILE_UPLOADS_KEY ) && Svg::file_sanitizer_can_run() && User::is_current_user_can_upload_json(); /** * Allow Unfiltered Files Upload. * * Determines whether to enable unfiltered file uploads. * * @since 3.0.0 * * @param bool $enabled Whether upload is enabled or not. */ $enabled = apply_filters( 'elementor/files/allow_unfiltered_upload', $enabled ); return $enabled; } /** * Handle Elementor WP Media Upload * * Runs on the 'wp_handle_upload_prefilter' filter. * * @since 3.2.0 * @access public * * @param $file * @return mixed */ public function handle_elementor_wp_media_upload( $file ) { // If it isn't a file uploaded by Elementor, we do not intervene. if ( ! $this->is_elementor_wp_media_upload() ) { return $file; } $result = $this->validate_file( $file ); if ( is_wp_error( $result ) ) { $file['error'] = $result->get_error_message(); } return $file; } /** * Get File Type Handler * * Initialize the proper file type handler according to the file extension * and assign it to the file type handlers array. * * @since 3.3.0 * @access public * * @param string|null $file_extension - file extension * @return File_Type_Base[]|File_Type_Base */ public function get_file_type_handlers( $file_extension = null ) { return self::get_items( $this->file_type_handlers, $file_extension ); } /** * Check filetype and ext * * 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 * * @since 3.5.0 * @access public * * @param $data * @param $file * @param $filename * @param $mimes * * @return mixed */ public function check_filetype_and_ext( $data, $file, $filename, $mimes ) { if ( ! empty( $data['ext'] ) && ! empty( $data['type'] ) ) { return $data; } $wp_file_type = wp_check_filetype( $filename, $mimes ); $file_type_handlers = $this->get_file_type_handlers(); if ( isset( $file_type_handlers[ $wp_file_type['ext'] ] ) ) { $file_type_handler = $file_type_handlers[ $wp_file_type['ext'] ]; $data['ext'] = $file_type_handler->get_file_extension(); $data['type'] = $file_type_handler->get_mime_type(); } return $data; } /** * Remove File Or Directory * * Directory is deleted recursively with all of its contents (subdirectories and files). * * @since 3.3.0 * @access public * * @param string $path */ public function remove_file_or_dir( $path ) { if ( is_dir( $path ) ) { $this->remove_directory_with_files( $path ); } elseif ( is_file( $path ) ) { unlink( $path ); } } /** * Create Temp File * * Create a random temporary file. * * @since 3.3.0 * @access public * * @param string $file_content * @param string $file_name * @return string|\WP_Error */ public function create_temp_file( $file_content, $file_name ) { $file_name = str_replace( ' ', '', sanitize_file_name( $file_name ) ); if ( empty( $file_name ) ) { return new \WP_Error( 'invalid_file_name', esc_html__( 'Invalid file name.', 'elementor' ) ); } $temp_filename = $this->create_unique_dir() . $file_name; /** * Temp File Path * * Allows modifying the full path of the temporary file. * * @since 3.7.0 * * @param string full path to file */ $temp_filename = apply_filters( 'elementor/files/temp-file-path', $temp_filename ); file_put_contents( $temp_filename, $file_content ); // phpcs:ignore return $temp_filename; } /** * Get Temp Directory * * Get the temporary files directory path. If the directory does not exist, this method creates it. * * @since 3.3.0 * @access public * * @return string $temp_dir */ public function get_temp_dir() { if ( ! $this->temp_dir ) { $wp_upload_dir = wp_upload_dir(); $temp_dir = implode( DIRECTORY_SEPARATOR, [ $wp_upload_dir['basedir'], 'elementor', 'tmp' ] ) . DIRECTORY_SEPARATOR; /** * Temp File Path * * Allows modifying the full path of the temporary file. * * @since 3.7.0 * * @param string temporary directory */ $this->temp_dir = apply_filters( 'elementor/files/temp-dir', $temp_dir ); if ( ! is_dir( $this->temp_dir ) ) { wp_mkdir_p( $this->temp_dir ); } } return $this->temp_dir; } /** * Create Unique Temp Dir * * Create a unique temporary directory * * @since 3.3.0 * @access public * * @return string the new directory path */ public function create_unique_dir() { $unique_dir_path = $this->get_temp_dir() . uniqid() . DIRECTORY_SEPARATOR; wp_mkdir_p( $unique_dir_path ); return $unique_dir_path; } /** * Register Ajax Actions * * Runs on the 'elementor/ajax/register_actions' hook. Receives the AJAX module as a parameter and registers * callbacks for specified action IDs. * * @since 3.5.0 * @access public * * @param Ajax $ajax */ public function register_ajax_actions( Ajax $ajax ) { $ajax->register_ajax_action( 'enable_unfiltered_files_upload', [ $this, 'enable_unfiltered_files_upload' ] ); } /** * Set Unfiltered Files Upload * * @since 3.5.0 * @access public */ public function enable_unfiltered_files_upload() { if ( ! current_user_can( 'manage_options' ) ) { return; } update_option( self::UNFILTERED_FILE_UPLOADS_KEY, 1 ); } /** * Support Unfiltered File Uploads * * When uploading a file within Elementor, this method adds the registered * file types to WordPress' allowed mimes list. This will only happen if the user allowed unfiltered file uploads * in Elementor's settings in the admin dashboard. * * @since 3.5.0 * @access public * * @param array $allowed_mimes * @return array allowed mime types */ final public function support_unfiltered_elementor_file_uploads( $allowed_mimes ) { if ( $this->is_elementor_upload() && $this->are_unfiltered_uploads_enabled() ) { foreach ( $this->file_type_handlers as $file_type_handler ) { $allowed_mimes[ $file_type_handler->get_file_extension() ] = $file_type_handler->get_mime_type(); } } return $allowed_mimes; } /** * Set Elementor Upload State * * @since 3.5.0 * @access public * * @param $state */ public function set_elementor_upload_state( $state ) { $this->is_elementor_upload = $state; } /** * Is Elementor Upload * * This method checks if the current session includes a request to upload files made via Elementor. * * @since 3.5.0 * @access private * * @return bool */ private function is_elementor_upload() { return $this->is_elementor_upload || $this->is_elementor_media_upload() || $this->is_elementor_wp_media_upload(); } /** * Is Elementor Media Upload * * Checks whether the current request includes uploading files via Elementor which are not destined for the Media * Library. * * @since 3.5.0 * @access public * * @return bool */ public function is_elementor_media_upload() { // Sometimes `uploadTypeCaller` passed as a GET parameter when using the WP Media Library REST API, where the // whole request body is occupied by the uploaded file. return isset( $_REQUEST['uploadTypeCaller'] ) && 'elementor-media-upload' === $_REQUEST['uploadTypeCaller']; // phpcs:ignore } /** * Is Elementor WP Media Upload * * Checks whether the current request is a request to upload files into the WP Media Library via Elementor. * * @since 3.3.0 * @access private * * @return bool */ private function is_elementor_wp_media_upload() { return isset( $_REQUEST['uploadTypeCaller'] ) && 'elementor-wp-media-upload' === $_REQUEST['uploadTypeCaller']; // phpcs:ignore } /** * Add File Extension To Allowed Extensions List * * @since 3.3.0 * @access private * * @param string $file_type */ private function add_file_extension_to_allowed_extensions_list( $file_type ) { $file_handler = $this->file_type_handlers[ $file_type ]; $file_extension = $file_handler->get_file_extension(); // Only add the file extension to the list if it doesn't already exist in it. if ( ! in_array( $file_extension, $this->allowed_file_extensions, true ) ) { $this->allowed_file_extensions[] = $file_extension; } } /** * Save Base64 as File * * Saves a Base64 string as a .tmp file in Elementor's temporary files directory. * * @since 3.3.0 * @access private * * @param $file * @param array|null $allowed_file_extensions * * @return array|\WP_Error */ private function save_base64_to_tmp_file( $file, $allowed_file_extensions = null ) { if ( empty( $file['fileName'] ) || empty( $file['fileData'] ) ) { return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT ); } $file_extension = pathinfo( $file['fileName'], PATHINFO_EXTENSION ); $is_file_type_allowed = $this->is_file_type_allowed( $file_extension, $allowed_file_extensions ); if ( is_wp_error( $is_file_type_allowed ) ) { return $is_file_type_allowed; } $file_content = base64_decode( $file['fileData'] ); // phpcs:ignore // If the decode fails if ( ! $file_content ) { return new \WP_Error( 'file_error', self::INVALID_FILE_CONTENT ); } $temp_filename = $this->create_temp_file( $file_content, $file['fileName'] ); if ( is_wp_error( $temp_filename ) ) { return $temp_filename; } return [ // the original uploaded file name 'name' => $file['fileName'], // The path to the temporary file 'tmp_name' => $temp_filename, ]; } /** * Validate File * * @since 3.3.0 * @access private * * @param array $file * @param array $file_extensions Optional * @return bool|\WP_Error */ private function validate_file( array $file, $file_extensions = [] ) { $uploaded_file_name = isset( $file['name'] ) ? $file['name'] : $file['tmp_name']; $file_extension = pathinfo( $uploaded_file_name, PATHINFO_EXTENSION ); if ( ! $this->is_elementor_wp_media_upload() ) { $is_file_type_allowed = $this->is_file_type_allowed( $file_extension, $file_extensions ); if ( is_wp_error( $is_file_type_allowed ) ) { return $is_file_type_allowed; } } $file_type_handler = $this->get_file_type_handlers( $file_extension ); // If Elementor does not have a handler for this file type, don't block it. if ( ! $file_type_handler ) { return true; } // If there is a File Type Handler for the uploaded file, it means it is a non-standard file type. In this case, // we check if unfiltered file uploads are enabled or not before allowing it. if ( ! self::are_unfiltered_uploads_enabled() ) { $error = 'json' === $file_extension ? esc_html__( 'You do not have permission to upload JSON files.', 'elementor' ) : esc_html__( 'This file is not allowed for security reasons.', 'elementor' ); return new \WP_Error( Exceptions::FORBIDDEN, $error ); } // Here is each file type handler's chance to run its own specific validations return $file_type_handler->validate_file( $file ); } /** * Is File Type Allowed * * Checks whether the passed file extension is allowed for upload. * * @since 3.5.0 * @access private * * @param $file_extension * @param $filtered_file_extensions * @return bool|\WP_Error */ private function is_file_type_allowed( $file_extension, $filtered_file_extensions ) { $allowed_file_extensions = $this->get_allowed_file_extensions(); if ( $filtered_file_extensions ) { $allowed_file_extensions = array_intersect( $allowed_file_extensions, $filtered_file_extensions ); } $is_allowed = false; // Check if the file type (extension) is in the allowed extensions list. If it is a non-standard file type (not // enabled by default in WordPress) and unfiltered file uploads are not enabled, it will not be in the allowed // file extensions list. foreach ( $allowed_file_extensions as $allowed_extension ) { if ( preg_match( '/' . $allowed_extension . '/', $file_extension ) ) { $is_allowed = true; break; } } if ( ! $is_allowed ) { $is_allowed = new \WP_Error( Exceptions::FORBIDDEN, 'Uploading this file type is not allowed.' ); } /** * Elementor File Type Allowed * * Allows setting file types * * @since 3.5.0 * * @param bool|\WP_Error $is_allowed */ return apply_filters( 'elementor/files/allow-file-type/' . $file_extension, $is_allowed ); } /** * Remove Directory with Files * * @since 3.3.0 * @access private * * @param string $dir * @return bool */ private function remove_directory_with_files( $dir ) { $dir_iterator = new \RecursiveDirectoryIterator( $dir, \RecursiveDirectoryIterator::SKIP_DOTS ); foreach ( new \RecursiveIteratorIterator( $dir_iterator, \RecursiveIteratorIterator::CHILD_FIRST ) as $name => $item ) { if ( is_dir( $name ) ) { rmdir( $name ); } elseif ( is_file( $name ) ) { unlink( $name ); } } return rmdir( $dir ); } /** * Get Allowed File Extensions * * Retrieve an array containing the list of file extensions allowed for upload. * * @since 3.3.0 * @access private * * @return array file extension/s */ private function get_allowed_file_extensions() { if ( ! $this->allowed_file_extensions ) { $this->allowed_file_extensions = array_keys( get_allowed_mime_types() ); foreach ( $this->get_file_type_handlers() as $file_type => $handler ) { if ( $handler->is_upload_allowed() ) { // Add the file extension to the allowed extensions list only if unfiltered files upload is enabled. $this->add_file_extension_to_allowed_extensions_list( $file_type ); } } } return $this->allowed_file_extensions; } public function __construct() { $this->register_file_types(); add_filter( 'upload_mimes', [ $this, 'support_unfiltered_elementor_file_uploads' ] ); add_filter( 'wp_handle_upload_prefilter', [ $this, 'handle_elementor_wp_media_upload' ] ); add_filter( 'wp_check_filetype_and_ext', [ $this, 'check_filetype_and_ext' ], 10, 4 ); // Ajax. add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] ); } } manager.php000064400000011412147207273540006701 0ustar00register_actions(); } public function get( $class, $args ) { $id = $class . '-' . wp_json_encode( $args ); if ( ! isset( $this->files[ $id ] ) ) { // Create an instance from dynamic args length. $reflection_class = new \ReflectionClass( $class ); $this->files[ $id ] = $reflection_class->newInstanceArgs( $args ); } return $this->files[ $id ]; } /** * On post delete. * * Delete post CSS immediately after a post is deleted from the database. * * Fired by `deleted_post` action. * * @since 1.2.0 * @access public * * @param string $post_id Post ID. */ public function on_delete_post( $post_id ) { if ( ! Utils::is_post_support( $post_id ) ) { return; } $css_file = Post_CSS::create( $post_id ); $css_file->delete(); } /** * On export post meta. * * When exporting data using WXR, skip post CSS file meta key. This way the * export won't contain the post CSS file data used by Elementor. * * Fired by `wxr_export_skip_postmeta` filter. * * @since 1.2.0 * @access public * * @param bool $skip Whether to skip the current post meta. * @param string $meta_key Current meta key. * * @return bool Whether to skip the post CSS meta. */ public function on_export_post_meta( $skip, $meta_key ) { if ( Post_CSS::META_KEY === $meta_key ) { $skip = true; } return $skip; } /** * Clear cache. * * Delete all meta containing files data. And delete the actual * files from the upload directory. * * @since 1.2.0 * @access public */ public function clear_cache() { // Delete files. $path = Base::get_base_uploads_dir() . Base::DEFAULT_FILES_DIR . '*'; foreach ( glob( $path ) as $file_path ) { unlink( $file_path ); } delete_post_meta_by_key( Post_CSS::META_KEY ); delete_post_meta_by_key( Document_Base::CACHE_META_KEY ); delete_post_meta_by_key( Assets::ASSETS_META_KEY ); delete_option( Global_CSS::META_KEY ); delete_option( Frontend::META_KEY ); $this->reset_assets_data(); /** * Elementor clear files. * * Fires after Elementor clears files * * @since 2.1.0 */ do_action( 'elementor/core/files/clear_cache' ); } public function clear_custom_image_sizes() { if ( ! defined( 'BFITHUMB_UPLOAD_DIR' ) ) { return; } $upload_info = wp_upload_dir(); $upload_dir = $upload_info['basedir'] . '/' . BFITHUMB_UPLOAD_DIR; $path = $upload_dir . '/*'; foreach ( glob( $path ) as $file_path ) { unlink( $file_path ); } } /** * Register Ajax Actions * * Deprecated - use the Uploads Manager instead. * * @deprecated 3.5.0 * * @param Ajax $ajax */ public function register_ajax_actions( Ajax $ajax ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); Plugin::$instance->uploads_manager->register_ajax_actions( $ajax ); } /** * Ajax Unfiltered Files Upload * * Deprecated - use the Uploads Manager instead. * * @deprecated 3.5.0 */ public function ajax_unfiltered_files_upload() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); Plugin::$instance->uploads_manager->enable_unfiltered_files_upload(); } /** * Register actions. * * Register filters and actions for the files manager. * * @since 1.2.0 * @access private */ private function register_actions() { add_action( 'deleted_post', [ $this, 'on_delete_post' ] ); add_filter( 'wxr_export_skip_postmeta', [ $this, 'on_export_post_meta' ], 10, 2 ); add_action( 'update_option_home', function () { $this->reset_assets_data(); } ); add_action( 'update_option_siteurl', function () { $this->reset_assets_data(); } ); } /** * Reset Assets Data. * * Reset the page assets data. * * @since 3.3.0 * @access private */ private function reset_assets_data() { delete_option( Page_Assets_Data_Manager::ASSETS_DATA_KEY ); } } assets/svg/svg-handler.php000064400000015541147207273540011611 0ustar00uploads_manager->get_file_type_handlers( 'svg' );` */ class Svg_Handler extends Files_Upload_Handler { /** * Inline svg attachment meta key * * @deprecated 3.5.0 */ const META_KEY = '_elementor_inline_svg'; /** * @deprecated 3.5.0 */ const SCRIPT_REGEX = '/(?:\w+script|data):/xi'; /** * Attachment ID. * * Holds the current attachment ID. * * @deprecated 3.5.0 * * @var int */ private $attachment_id; /** * @deprecated 3.5.0 */ public static function get_name() { return 'svg-handler'; } /** * get_meta * * @deprecated 3.5.0 * * @return mixed */ protected function get_meta() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); return get_post_meta( $this->attachment_id, self::META_KEY, true ); } /** * update_meta * * @deprecated 3.5.0 * * @param $meta */ protected function update_meta( $meta ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); update_post_meta( $this->attachment_id, self::META_KEY, $meta ); } /** * delete_meta * * @deprecated 3.5.0 */ protected function delete_meta() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); delete_post_meta( $this->attachment_id, self::META_KEY ); } /** * @deprecated 3.5.0 */ public function get_mime_type() { return 'image/svg+xml'; } /** * @deprecated 3.5.0 */ public function get_file_type() { return 'svg'; } /** * delete_meta_cache * * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->delete_meta_cache()` instead. */ public function delete_meta_cache() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->delete_meta_cache()' ); /** @var Svg $svg_handler */ $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); $svg_handler->delete_meta_cache(); } /** * get_inline_svg * * @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg::get_inline_svg()` instead. * * @param $attachment_id * * @return bool|mixed|string */ public static function get_inline_svg( $attachment_id ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Core\Files\File_Types\Svg::get_inline_svg()' ); return Svg::get_inline_svg( $attachment_id ); } /** * sanitize_svg * * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->delete_meta_cache()->sanitize_svg()` instead. * * @param $filename * * @return bool */ public function sanitize_svg( $filename ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->delete_meta_cache()->sanitize_svg()' ); /** @var Svg $svg_handler */ $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); return $svg_handler->sanitize_svg( $filename ); } /** * sanitizer * * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->sanitizer()` instead. * * @param $content * * @return bool|string */ public function sanitizer( $content ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->sanitizer()' ); /** @var Svg $svg_handler */ $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); return $svg_handler->sanitizer( $content ); } /** * wp_prepare_attachment_for_js * * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->wp_prepare_attachment_for_js()` instead. * * @param $attachment_data * @param $attachment * @param $meta * * @return mixed */ public function wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->wp_prepare_attachment_for_js()' ); /** @var Svg $svg_handler */ $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); return $svg_handler->wp_prepare_attachment_for_js( $attachment_data, $attachment, $meta ); } /** * set_attachment_id * * @deprecated 3.5.0 * * @param $attachment_id * * @return int */ public function set_attachment_id( $attachment_id ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); $this->attachment_id = $attachment_id; return $this->attachment_id; } /** * get_attachment_id * * @deprecated 3.5.0 * * @return int */ public function get_attachment_id() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); return $this->attachment_id; } /** * set_svg_meta_data * * @deprecated 3.5.0 Use `Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' )->set_svg_meta_data()` instead. * * @return mixed */ public function set_svg_meta_data( $data, $id ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Plugin::$instance->uploads_manager->get_file_type_handlers( \'svg\' )->set_svg_meta_data()' ); /** @var Svg $svg_handler */ $svg_handler = Plugin::$instance->uploads_manager->get_file_type_handlers( 'svg' ); return $svg_handler->set_svg_meta_data( $data, $id ); } /** * handle_upload_prefilter * * @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()` instead. * * @param $file * * @return mixed */ public function handle_upload_prefilter( $file ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()' ); return Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload( $file ); } } assets/files-upload-handler.php000064400000010527147207273540012576 0ustar00uploads_manager->are_unfiltered_uploads_enabled()` instead. * * @return bool */ private function is_elementor_media_upload() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()' ); return Plugin::$instance->uploads_manager->is_elementor_media_upload(); } /** * Is Enabled * * @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()` instead. * * @return bool */ final public static function is_enabled() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()' ); return Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled(); } /** * @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->are_unfiltered_uploads_enabled()` instead. */ final public function support_unfiltered_files_upload( $existing_mimes ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->support_unfiltered_file_uploads()' ); return Plugin::$instance->uploads_manager->support_unfiltered_elementor_file_uploads( $existing_mimes ); } /** * handle_upload_prefilter * * @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()` instead. * * @param $file * * @return mixed */ public function handle_upload_prefilter( $file ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload()' ); return Plugin::$instance->uploads_manager->handle_elementor_wp_media_upload( $file ); } /** * is_file_should_handled * * @deprecated 3.5.0 * * @param $file * * @return bool */ protected function is_file_should_handled( $file ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0' ); $ext = pathinfo( $file['name'], PATHINFO_EXTENSION ); return $this->is_elementor_media_upload() && $this->get_file_type() === $ext; } /** * file_sanitizer_can_run * * @deprecated 3.5.0 Use `Elementor\Core\Files\File_Types\Svg::file_sanitizer_can_run()` instead. * * @return bool */ public static function file_sanitizer_can_run() { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Core\Files\File_Types\Svg::file_sanitizer_can_run()' ); return Svg::file_sanitizer_can_run(); } /** * Check filetype and ext * * 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 * * @deprecated 3.5.0 Use `Elementor\Plugin::$instance->uploads_manager->check_filetype_and_ext()` instead. * * @param $data * @param $file * @param $filename * @param $mimes * * @return mixed */ public function check_filetype_and_ext( $data, $file, $filename, $mimes ) { Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.5.0', 'Elementor\Plugin::$instance->uploads_manager->check_filetype_and_ext()' ); Plugin::$instance->uploads_manager->check_filetype_and_ext( $data, $file, $filename, $mimes ); } } assets/manager.php000064400000002325147207273540010206 0ustar00register_asset_types(); /** * Elementor files assets registered. * * Fires after Elementor registers assets types * * @since 2.6.0 */ do_action( 'elementor/core/files/assets/assets_registered', $this ); } public function get_asset( $name ) { return isset( $this->asset_types[ $name ] ) ? $this->asset_types[ $name ] : false; } /** * Add Asset * @param $instance */ public function add_asset( $instance ) { $this->asset_types[ $instance::get_name() ] = $instance; } /** * Register Asset Types * * Registers Elementor Asset Types */ private function register_asset_types() { $this->add_asset( new Svg_Handler() ); } } assets/json/json-handler.php000064400000001302147207273540012123 0ustar00uploads_manager->get_file_type_handlers( 'svg' );` */ class Json_Handler extends Files_Upload_Handler { /** * @deprecated 3.5.0 */ public static function get_name() { return 'json-handler'; } /** * @deprecated 3.5.0 */ public function get_mime_type() { return 'application/json'; } /** * @deprecated 3.5.0 */ public function get_file_type() { return 'json'; } }