first commit
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Assets_Config_Provider extends Collection {
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
private $path_resolver = null;
|
||||
|
||||
/**
|
||||
* @param callable $path_resolver
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set_path_resolver( callable $path_resolver ) {
|
||||
$this->path_resolver = $path_resolver;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load asset config from a file into the collection.
|
||||
*
|
||||
* @param $key
|
||||
* @param $path
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function load( $key, $path = null ) {
|
||||
if ( ! $path && $this->path_resolver ) {
|
||||
$path_resolver_callback = $this->path_resolver;
|
||||
|
||||
$path = $path_resolver_callback( $key );
|
||||
}
|
||||
|
||||
if ( ! $path || ! file_exists( $path ) ) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$config = require $path;
|
||||
|
||||
if ( ! $this->is_valid_handle( $config ) ) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->items[ $key ] = [
|
||||
'handle' => $config['handle'],
|
||||
'deps' => $this->is_valid_deps( $config ) ? $config['deps'] : [],
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the handle property in the config is a valid.
|
||||
*
|
||||
* @param $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_handle( $config ) {
|
||||
return ! empty( $config['handle'] ) && is_string( $config['handle'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the deps property in the config is a valid.
|
||||
*
|
||||
* @param $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_deps( $config ) {
|
||||
return isset( $config['deps'] ) && is_array( $config['deps'] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Assets_Translation_Loader {
|
||||
|
||||
public static function for_handles( array $handles, $domain = null, $replace_callback = null ) {
|
||||
self::set_domain( $handles, $domain );
|
||||
self::replace_translation_path( $handles, $replace_callback );
|
||||
}
|
||||
|
||||
private static function set_domain( array $handles, $domain = null ) {
|
||||
if ( empty( $domain ) || ! is_string( $domain ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $handles as $handle ) {
|
||||
wp_set_script_translations( $handle, $domain );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The purpose of this function is to replace the requested translation file
|
||||
* with a file that contains all the translations for specific scripts.
|
||||
*
|
||||
* When developing a module and using Webpack's dynamic load feature, the script will be split into multiple chunks.
|
||||
* As a result, the WordPress translations expressions will also be split into multiple files.
|
||||
* Therefore, we replace the requested translation file with another file (generated in the build process)
|
||||
* that contains all the translations for the specific script (including dynamically loaded chunks).
|
||||
*
|
||||
* Want to go deeper? Read the following article:
|
||||
* @see https://developer.wordpress.com/2022/01/06/wordpress-plugin-i18n-webpack-and-composer/
|
||||
*
|
||||
* @param array $handles
|
||||
* @param callable|null $replace_callback
|
||||
*/
|
||||
private static function replace_translation_path( array $handles, $replace_callback = null ) {
|
||||
$sources = self::map_handles_to_src( $handles );
|
||||
|
||||
add_filter( 'load_script_textdomain_relative_path', function ( $relative_path, $src ) use ( $sources, $replace_callback ) {
|
||||
if ( ! in_array( $src, $sources, true ) ) {
|
||||
return $relative_path;
|
||||
}
|
||||
|
||||
if ( is_callable( $replace_callback ) ) {
|
||||
return $replace_callback( $relative_path, $src );
|
||||
}
|
||||
|
||||
return self::default_replace_translation( $relative_path );
|
||||
}, 10, 2 );
|
||||
}
|
||||
|
||||
private static function map_handles_to_src( array $handles ) {
|
||||
return array_map( function ( $handle ) {
|
||||
return wp_scripts()->registered[ $handle ]->src;
|
||||
}, $handles );
|
||||
}
|
||||
|
||||
private static function default_replace_translation( $relative_path ) {
|
||||
// Translations are always based on the non-minified filename.
|
||||
$relative_path_without_ext = preg_replace( '/(\.min)?\.js$/i', '', $relative_path );
|
||||
|
||||
// By default, we suffix the file with `.strings` (e.g 'assets/js/editor.js' => 'assets/js/editor.strings.js').
|
||||
return implode( '.', [
|
||||
$relative_path_without_ext,
|
||||
'strings',
|
||||
'js',
|
||||
] );
|
||||
}
|
||||
}
|
||||
526
wp-content/plugins/elementor/core/utils/collection.php
Normal file
526
wp-content/plugins/elementor/core/utils/collection.php
Normal file
@@ -0,0 +1,526 @@
|
||||
<?php
|
||||
/**
|
||||
* Inspired by Laravel Collection.
|
||||
* @link https://github.com/illuminate/collections
|
||||
*/
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Collection implements \ArrayAccess, \Countable, \IteratorAggregate {
|
||||
/**
|
||||
* The items contained in the collection.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* Collection constructor.
|
||||
*
|
||||
* @param array $items
|
||||
*/
|
||||
public function __construct( array $items = [] ) {
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function make( array $items = [] ) {
|
||||
return new static( $items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|null $callback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filter( callable $callback = null ) {
|
||||
if ( ! $callback ) {
|
||||
return new static( array_filter( $this->items ) );
|
||||
}
|
||||
|
||||
return new static( array_filter( $this->items, $callback, ARRAY_FILTER_USE_BOTH ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $items
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function merge( $items ) {
|
||||
if ( $items instanceof Collection ) {
|
||||
$items = $items->all();
|
||||
}
|
||||
|
||||
return new static( array_merge( $this->items, $items ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Union the collection with the given items.
|
||||
*
|
||||
* @param array $items
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function union( array $items ) {
|
||||
return new static( $this->all() + $items );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge array recursively
|
||||
*
|
||||
* @param $items
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function merge_recursive( $items ) {
|
||||
if ( $items instanceof Collection ) {
|
||||
$items = $items->all();
|
||||
}
|
||||
|
||||
return new static( array_merge_recursive( $this->items, $items ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace array recursively
|
||||
*
|
||||
* @param $items
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function replace_recursive( $items ) {
|
||||
if ( $items instanceof Collection ) {
|
||||
$items = $items->all();
|
||||
}
|
||||
|
||||
return new static( array_replace_recursive( $this->items, $items ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Implode the items
|
||||
*
|
||||
* @param $glue
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function implode( $glue ) {
|
||||
return implode( $glue, $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a map over each of the items.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function map( callable $callback ) {
|
||||
$keys = array_keys( $this->items );
|
||||
|
||||
$items = array_map( $callback, $this->items, $keys );
|
||||
|
||||
return new static( array_combine( $keys, $items ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a callback over each of the items.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function each( callable $callback ) {
|
||||
foreach ( $this->items as $key => $value ) {
|
||||
if ( false === $callback( $value, $key ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
* @param null $initial
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function reduce( callable $callback, $initial = null ) {
|
||||
$result = $initial;
|
||||
|
||||
foreach ( $this->all() as $key => $value ) {
|
||||
$result = $callback( $result, $value, $key );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function map_with_keys( callable $callback ) {
|
||||
$result = [];
|
||||
|
||||
foreach ( $this->items as $key => $value ) {
|
||||
$assoc = $callback( $value, $key );
|
||||
|
||||
foreach ( $assoc as $map_key => $map_value ) {
|
||||
$result[ $map_key ] = $map_value;
|
||||
}
|
||||
}
|
||||
|
||||
return new static( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all items except for those with the specified keys.
|
||||
*
|
||||
* @param array $keys
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function except( array $keys ) {
|
||||
return $this->filter( function ( $value, $key ) use ( $keys ) {
|
||||
return ! in_array( $key, $keys, true );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the items with the specified keys.
|
||||
*
|
||||
* @param array $keys
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function only( array $keys ) {
|
||||
return $this->filter( function ( $value, $key ) use ( $keys ) {
|
||||
return in_array( $key, $keys, true );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run over the collection to get specific prop from the collection item.
|
||||
*
|
||||
* @param $key
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function pluck( $key ) {
|
||||
$result = [];
|
||||
|
||||
foreach ( $this->items as $item ) {
|
||||
$result[] = $this->get_item_value( $item, $key );
|
||||
}
|
||||
|
||||
return new static( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Group the collection items by specific key in each collection item.
|
||||
*
|
||||
* @param $group_by
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function group_by( $group_by ) {
|
||||
$result = [];
|
||||
|
||||
foreach ( $this->items as $item ) {
|
||||
$group_key = $this->get_item_value( $item, $group_by, 0 );
|
||||
|
||||
$result[ $group_key ][] = $item;
|
||||
}
|
||||
|
||||
return new static( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort keys
|
||||
*
|
||||
* @param false $descending
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function sort_keys( $descending = false ) {
|
||||
$items = $this->items;
|
||||
|
||||
if ( $descending ) {
|
||||
krsort( $items );
|
||||
} else {
|
||||
ksort( $items );
|
||||
}
|
||||
|
||||
return new static( $items );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific item from the collection.
|
||||
*
|
||||
* @param $key
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get( $key, $default = null ) {
|
||||
if ( ! array_key_exists( $key, $this->items ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $this->items[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item.
|
||||
*
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function first( $default = null ) {
|
||||
if ( $this->is_empty() ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
foreach ( $this->items as $item ) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an element from the items.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function find( callable $callback, $default = null ) {
|
||||
foreach ( $this->all() as $key => $item ) {
|
||||
if ( $callback( $item, $key ) ) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable|string|int $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function contains( $value ) {
|
||||
$callback = $value instanceof \Closure
|
||||
? $value
|
||||
: function ( $item ) use ( $value ) {
|
||||
return $item === $value;
|
||||
};
|
||||
|
||||
foreach ( $this->all() as $key => $item ) {
|
||||
if ( $callback( $item, $key ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure all the values inside the array are uniques.
|
||||
*
|
||||
* @param null|string|string[] $keys
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function unique( $keys = null ) {
|
||||
if ( ! $keys ) {
|
||||
return new static(
|
||||
array_unique( $this->items )
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! is_array( $keys ) ) {
|
||||
$keys = [ $keys ];
|
||||
}
|
||||
|
||||
$exists = [];
|
||||
|
||||
return $this->filter( function ( $item ) use ( $keys, &$exists ) {
|
||||
$value = null;
|
||||
|
||||
foreach ( $keys as $key ) {
|
||||
$current_value = $this->get_item_value( $item, $key );
|
||||
|
||||
$value .= "{$key}:{$current_value};";
|
||||
}
|
||||
|
||||
// If no value for the specific key return the item.
|
||||
if ( null === $value ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If value is not exists, add to the exists array and return the item.
|
||||
if ( ! in_array( $value, $exists, true ) ) {
|
||||
$exists[] = $value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function keys() {
|
||||
return array_keys( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_empty() {
|
||||
return empty( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function all() {
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function values() {
|
||||
return array_values( $this->all() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Support only one level depth.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function flatten() {
|
||||
$result = [];
|
||||
|
||||
foreach ( $this->all() as $item ) {
|
||||
$item = $item instanceof Collection ? $item->all() : $item;
|
||||
|
||||
if ( ! is_array( $item ) ) {
|
||||
$result[] = $item;
|
||||
} else {
|
||||
$values = array_values( $item );
|
||||
|
||||
foreach ( $values as $value ) {
|
||||
$result[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new static( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ...$values
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push( ...$values ) {
|
||||
foreach ( $values as $value ) {
|
||||
$this->items[] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function prepend( ...$values ) {
|
||||
$this->items = array_merge( $values, $this->items );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists( $offset ) {
|
||||
return isset( $this->items[ $offset ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet( $offset ) {
|
||||
return $this->items[ $offset ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet( $offset, $value ) {
|
||||
if ( is_null( $offset ) ) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[ $offset ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset( $offset ) {
|
||||
unset( $this->items[ $offset ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ArrayIterator|\Traversable
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
return new \ArrayIterator( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count() {
|
||||
return count( $this->items );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $item
|
||||
* @param $key
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
private function get_item_value( $item, $key, $default = null ) {
|
||||
$value = $default;
|
||||
|
||||
if ( is_object( $item ) && isset( $item->{$key} ) ) {
|
||||
$value = $item->{$key};
|
||||
} elseif ( is_array( $item ) && isset( $item[ $key ] ) ) {
|
||||
$value = $item[ $key ];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
41
wp-content/plugins/elementor/core/utils/exceptions.php
Normal file
41
wp-content/plugins/elementor/core/utils/exceptions.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor exceptions.
|
||||
*
|
||||
* Elementor exceptions handler class is responsible for handling exceptions.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class Exceptions {
|
||||
|
||||
/**
|
||||
* HTTP status code for bad request error.
|
||||
*/
|
||||
const BAD_REQUEST = 400;
|
||||
|
||||
/**
|
||||
* HTTP status code for unauthorized access error.
|
||||
*/
|
||||
const UNAUTHORIZED = 401;
|
||||
|
||||
/**
|
||||
* HTTP status code for forbidden access error.
|
||||
*/
|
||||
const FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* HTTP status code for resource that could not be found.
|
||||
*/
|
||||
const NOT_FOUND = 404;
|
||||
|
||||
/**
|
||||
* HTTP status code for internal server error.
|
||||
*/
|
||||
const INTERNAL_SERVER_ERROR = 500;
|
||||
}
|
||||
138
wp-content/plugins/elementor/core/utils/force-locale.php
Normal file
138
wp-content/plugins/elementor/core/utils/force-locale.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Force translation to use a specific locale.
|
||||
*
|
||||
* A hacky class to force any translation functions in the call-stack between the
|
||||
* `force()` & `reset()` methods to use a specific locale.
|
||||
*/
|
||||
class Force_Locale {
|
||||
|
||||
/**
|
||||
* @var string Locale to force (e.g. `he_IL`).
|
||||
*/
|
||||
private $new_locale;
|
||||
|
||||
/**
|
||||
* @var string Original locale before forcing.
|
||||
*/
|
||||
private $original_locale;
|
||||
|
||||
/**
|
||||
* @var \WP_Textdomain_Registry
|
||||
*/
|
||||
private $original_textdomain_registry;
|
||||
|
||||
/**
|
||||
* @var \Closure Filter reference `pre_determine_locale`.
|
||||
*/
|
||||
private $filter;
|
||||
|
||||
public function __construct( $new_locale, $original_locale = null ) {
|
||||
$this->new_locale = $new_locale;
|
||||
|
||||
$this->original_locale = $original_locale ? $original_locale : determine_locale();
|
||||
|
||||
$this->filter = function() use ( $new_locale ) {
|
||||
return $new_locale;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the translations to use a specific locale.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function force() {
|
||||
switch_to_locale( $this->new_locale );
|
||||
|
||||
/**
|
||||
* Reset the \WP_Textdomain_Registry instance to clear its cache.
|
||||
*
|
||||
* @see https://github.com/WordPress/wordpress-develop/blob/799d7dc86f5b07b17f7a418948fc851bd2fc334b/src/wp-includes/class-wp-textdomain-registry.php#L179-L187
|
||||
* @see https://github.com/WordPress/wordpress-develop/blob/799d7dc86f5b07b17f7a418948fc851bd2fc334b/tests/phpunit/tests/l10n/wpLocaleSwitcher.php#L19-L31
|
||||
*/
|
||||
$this->reset_textdomain_registry();
|
||||
|
||||
/**
|
||||
* Reset l10n in order to clear the translations cache.
|
||||
*
|
||||
* @see https://github.com/WordPress/wordpress-develop/blob/2437ef5130f10153bc4fffa412d4f37e65e3d66b/src/wp-includes/l10n.php#L1324
|
||||
* @see https://github.com/WordPress/wordpress-develop/blob/2437ef5130f10153bc4fffa412d4f37e65e3d66b/src/wp-includes/l10n.php#L1222
|
||||
* @see https://github.com/WordPress/wordpress-develop/blob/2437ef5130f10153bc4fffa412d4f37e65e3d66b/src/wp-includes/l10n.php#L821
|
||||
*/
|
||||
$this->reset_l10n();
|
||||
|
||||
/**
|
||||
* Force the translations of `$new_locale` to be loaded.
|
||||
*
|
||||
* @see https://github.com/WordPress/wordpress-develop/blob/2437ef5130f10153bc4fffa412d4f37e65e3d66b/src/wp-includes/l10n.php#L1294
|
||||
*/
|
||||
add_filter( 'pre_determine_locale', $this->filter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the original locale and cleanup filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function restore() {
|
||||
$this->restore_textdomain_registry();
|
||||
|
||||
$this->reset_l10n();
|
||||
|
||||
switch_to_locale( $this->original_locale );
|
||||
|
||||
remove_filter( 'pre_determine_locale', $this->filter );
|
||||
}
|
||||
|
||||
private function reset_textdomain_registry() {
|
||||
if ( ! class_exists( '\WP_Textdomain_Registry' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \WP_Textdomain_Registry $wp_textdomain_registry */
|
||||
global $wp_textdomain_registry;
|
||||
|
||||
$this->original_textdomain_registry = $wp_textdomain_registry;
|
||||
|
||||
$wp_textdomain_registry = new \WP_Textdomain_Registry();
|
||||
}
|
||||
|
||||
private function restore_textdomain_registry() {
|
||||
if ( ! $this->original_textdomain_registry ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \WP_Textdomain_Registry $wp_textdomain_registry */
|
||||
global $wp_textdomain_registry;
|
||||
|
||||
$wp_textdomain_registry = $this->original_textdomain_registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the l10n global variables.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function reset_l10n() {
|
||||
global $l10n, $l10n_unloaded;
|
||||
|
||||
if ( is_array( $l10n ) ) {
|
||||
foreach ( $l10n as $domain => $l10n_data ) {
|
||||
unset( $l10n[ $domain ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_array( $l10n_unloaded ) ) {
|
||||
foreach ( $l10n_unloaded as $domain => $l10n_unloaded_data ) {
|
||||
unset( $l10n_unloaded[ $domain ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
356
wp-content/plugins/elementor/core/utils/hints.php
Normal file
356
wp-content/plugins/elementor/core/utils/hints.php
Normal file
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
namespace elementor\core\utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
use Elementor\User;
|
||||
|
||||
class Hints {
|
||||
const INFO = 'info';
|
||||
const SUCCESS = 'success';
|
||||
const WARNING = 'warning';
|
||||
const DANGER = 'danger';
|
||||
|
||||
const DEFINED = 'defined';
|
||||
const DISMISSED = 'dismissed';
|
||||
const CAPABILITY = 'capability';
|
||||
const PLUGIN_INSTALLED = 'plugin_installed';
|
||||
const PLUGIN_ACTIVE = 'plugin_active';
|
||||
|
||||
/**
|
||||
* get_notice_types
|
||||
* @return string[]
|
||||
*/
|
||||
public static function get_notice_types(): array {
|
||||
return [
|
||||
self::INFO,
|
||||
self::SUCCESS,
|
||||
self::WARNING,
|
||||
self::DANGER,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* get_hints
|
||||
*
|
||||
* @param $hint_key
|
||||
*
|
||||
* @return array|string[]|\string[][]
|
||||
*/
|
||||
public static function get_hints( $hint_key = null ): array {
|
||||
$hints = [
|
||||
'image-optimization-once' => [
|
||||
self::DISMISSED => 'image-optimization-once',
|
||||
self::CAPABILITY => 'install_plugins',
|
||||
self::DEFINED => 'IMAGE_OPTIMIZATION_VERSION',
|
||||
],
|
||||
'image-optimization-once-media-modal' => [
|
||||
self::DISMISSED => 'image-optimization-once-media-modal',
|
||||
self::CAPABILITY => 'install_plugins',
|
||||
self::DEFINED => 'IMAGE_OPTIMIZATION_VERSION',
|
||||
],
|
||||
'image-optimization' => [
|
||||
self::DISMISSED => 'image_optimizer_hint',
|
||||
self::CAPABILITY => 'install_plugins',
|
||||
self::DEFINED => 'IMAGE_OPTIMIZATION_VERSION',
|
||||
],
|
||||
'image-optimization-media-modal' => [
|
||||
self::DISMISSED => 'image-optimization-media-modal',
|
||||
self::CAPABILITY => 'install_plugins',
|
||||
self::DEFINED => 'IMAGE_OPTIMIZATION_VERSION',
|
||||
],
|
||||
];
|
||||
if ( ! $hint_key ) {
|
||||
return $hints;
|
||||
}
|
||||
|
||||
return $hints[ $hint_key ] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* get_notice_icon
|
||||
* @return string
|
||||
*/
|
||||
public static function get_notice_icon(): string {
|
||||
return '<div class="elementor-control-notice-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.25 9H3M9 2.25V3M15 9H15.75M4.2 4.2L4.725 4.725M13.8 4.2L13.275 4.725M7.27496 12.75H10.725M6.75 12C6.12035 11.5278 5.65525 10.8694 5.42057 10.1181C5.1859 9.36687 5.19355 8.56082 5.44244 7.81415C5.69133 7.06748 6.16884 6.41804 6.80734 5.95784C7.44583 5.49764 8.21294 5.25 9 5.25C9.78706 5.25 10.5542 5.49764 11.1927 5.95784C11.8312 6.41804 12.3087 7.06748 12.5576 7.81415C12.8065 8.56082 12.8141 9.36687 12.5794 10.1181C12.3448 10.8694 11.8796 11.5278 11.25 12C10.9572 12.2899 10.7367 12.6446 10.6064 13.0355C10.4761 13.4264 10.4397 13.8424 10.5 14.25C10.5 14.6478 10.342 15.0294 10.0607 15.3107C9.77936 15.592 9.39782 15.75 9 15.75C8.60218 15.75 8.22064 15.592 7.93934 15.3107C7.65804 15.0294 7.5 14.6478 7.5 14.25C7.56034 13.8424 7.52389 13.4264 7.3936 13.0355C7.2633 12.6446 7.04282 12.2899 6.75 12Z" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* get_notice_template
|
||||
*
|
||||
* Print or Retrieve the notice template.
|
||||
* @param array $notice
|
||||
* @param bool $return
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public static function get_notice_template( array $notice, bool $return = false ) {
|
||||
$default_settings = [
|
||||
'type' => 'info',
|
||||
'icon' => false,
|
||||
'heading' => '',
|
||||
'content' => '',
|
||||
'dismissible' => false,
|
||||
'button_text' => '',
|
||||
'button_event' => '',
|
||||
'button_data' => [],
|
||||
'display' => false,
|
||||
];
|
||||
$notice_settings = array_merge( $default_settings, $notice );
|
||||
|
||||
if ( empty( $notice_settings['heading'] ) && empty( $notice_settings['content'] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( ! in_array( $notice_settings['type'], self::get_notice_types(), true ) ) {
|
||||
$notice_settings['type'] = 'info';
|
||||
}
|
||||
|
||||
$icon = '';
|
||||
$heading = '';
|
||||
$content = '';
|
||||
$dismissible = '';
|
||||
$button = '';
|
||||
|
||||
if ( $notice_settings['icon'] ) {
|
||||
$icon = self::get_notice_icon();
|
||||
}
|
||||
|
||||
if ( ! empty( $notice_settings['heading'] ) ) {
|
||||
$heading = '<div class="elementor-control-notice-main-heading">' . $notice_settings['heading'] . '</div>';
|
||||
}
|
||||
|
||||
if ( ! empty( $notice_settings['content'] ) ) {
|
||||
$content = '<div class="elementor-control-notice-main-content">' . $notice_settings['content'] . '</div>';
|
||||
}
|
||||
|
||||
if ( ! empty( $notice_settings['button_text'] ) ) {
|
||||
$button_settings = ( ! empty( $notice_settings['button_data'] ) ) ? ' data-settings="' . esc_attr( json_encode( $notice_settings['button_data'] ) ) . '"' : '';
|
||||
$button = '<div class="elementor-control-notice-main-actions">
|
||||
<button type="button" class="e-btn e-' . $notice_settings['type'] . ' e-btn-1" data-event="' . $notice_settings['button_event'] . '"' . $button_settings . '>
|
||||
' . $notice_settings['button_text'] . '
|
||||
</button>
|
||||
</div>';
|
||||
}
|
||||
|
||||
if ( $notice_settings['dismissible'] ) {
|
||||
$dismissible = '<button class="elementor-control-notice-dismiss tooltip-target" data-event="' . $notice_settings['dismissible'] . '" data-tooltip="' . esc_attr__( 'Don’t show again.', 'elementor' ) . '">
|
||||
<i class="eicon eicon-close" aria-hidden="true"></i>
|
||||
<span class="elementor-screen-only">' . esc_html__( 'Don’t show again.', 'elementor' ) . '</span>
|
||||
</button>';
|
||||
}
|
||||
|
||||
$notice_template = sprintf( '<div class="elementor-control-notice elementor-control-notice-type-%1$s" data-display="%7$s">
|
||||
%2$s
|
||||
<div class="elementor-control-notice-main">
|
||||
%3$s
|
||||
%4$s
|
||||
%5$s
|
||||
</div>
|
||||
%6$s
|
||||
</div>',
|
||||
$notice_settings['type'],
|
||||
$icon,
|
||||
$heading,
|
||||
$content,
|
||||
$button,
|
||||
$dismissible,
|
||||
$notice_settings['display']
|
||||
);
|
||||
|
||||
if ( $return ) {
|
||||
return $notice_template;
|
||||
}
|
||||
echo wp_kses( $notice_template, self::get_notice_allowed_html() );
|
||||
}
|
||||
|
||||
/**
|
||||
* get_plugin_install_url
|
||||
* @param $plugin_slug
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plugin_install_url( $plugin_slug ): string {
|
||||
$action = 'install-plugin';
|
||||
return wp_nonce_url(
|
||||
add_query_arg(
|
||||
[
|
||||
'action' => $action,
|
||||
'plugin' => $plugin_slug,
|
||||
],
|
||||
admin_url( 'update.php' )
|
||||
),
|
||||
$action . '_' . $plugin_slug
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_plugin_activate_url
|
||||
* @param $plugin_slug
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plugin_activate_url( $plugin_slug ): string {
|
||||
return admin_url( 'plugins.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* is_dismissed
|
||||
* @param $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_dismissed( $key ): bool {
|
||||
$dismissed = User::get_dismissed_editor_notices();
|
||||
return in_array( $key, $dismissed, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* should_display_hint
|
||||
* @param $hint_key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function should_display_hint( $hint_key ): bool {
|
||||
$hint = self::get_hints( $hint_key );
|
||||
if ( empty( $hint ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $hint as $key => $value ) {
|
||||
switch ( $key ) {
|
||||
case self::DISMISSED:
|
||||
if ( self::is_dismissed( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case self::CAPABILITY:
|
||||
if ( ! current_user_can( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case self::DEFINED:
|
||||
if ( defined( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case self::PLUGIN_INSTALLED:
|
||||
if ( ! self::is_plugin_installed( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case self::PLUGIN_ACTIVE:
|
||||
if ( ! self::is_plugin_active( $value ) ) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_plugin_installed
|
||||
* @param $plugin
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_plugin_installed( $plugin ) : bool {
|
||||
$plugins = get_plugins();
|
||||
$plugin = self::ensure_plugin_folder( $plugin );
|
||||
return ! empty( $plugins[ $plugin ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* is_plugin_active
|
||||
* @param $plugin
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_plugin_active( $plugin ): bool {
|
||||
$plugin = self::ensure_plugin_folder( $plugin );
|
||||
return is_plugin_active( $plugin );
|
||||
}
|
||||
|
||||
/**
|
||||
* get_plugin_action_url
|
||||
* @param $plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plugin_action_url( $plugin ): string {
|
||||
if ( ! self::is_plugin_installed( $plugin ) ) {
|
||||
return self::get_plugin_install_url( $plugin );
|
||||
}
|
||||
|
||||
if ( ! self::is_plugin_active( $plugin ) ) {
|
||||
return self::get_plugin_activate_url( $plugin );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* ensure_plugin_folder
|
||||
* @param $plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function ensure_plugin_folder( $plugin ): string {
|
||||
if ( false === strpos( $plugin, '/' ) ) {
|
||||
$plugin = $plugin . '/' . $plugin . '.php';
|
||||
}
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_notice_allowed_html
|
||||
* @return array[]
|
||||
*/
|
||||
public static function get_notice_allowed_html(): array {
|
||||
return [
|
||||
'div' => [
|
||||
'class' => [],
|
||||
'data-display' => [],
|
||||
],
|
||||
'svg' => [
|
||||
'width' => [],
|
||||
'height' => [],
|
||||
'viewbox' => [],
|
||||
'fill' => [],
|
||||
'xmlns' => [],
|
||||
],
|
||||
'path' => [
|
||||
'd' => [],
|
||||
'stroke' => [],
|
||||
'stroke-width' => [],
|
||||
'stroke-linecap' => [],
|
||||
'stroke-linejoin' => [],
|
||||
],
|
||||
'button' => [
|
||||
'class' => [],
|
||||
'data-event' => [],
|
||||
'data-settings' => [],
|
||||
'data-tooltip' => [],
|
||||
],
|
||||
'i' => [
|
||||
'class' => [],
|
||||
'aria-hidden' => [],
|
||||
],
|
||||
'span' => [
|
||||
'class' => [],
|
||||
],
|
||||
'a' => [
|
||||
'href' => [],
|
||||
'style' => [],
|
||||
'target' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
50
wp-content/plugins/elementor/core/utils/http.php
Normal file
50
wp-content/plugins/elementor/core/utils/http.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Http extends \WP_Http {
|
||||
/**
|
||||
* Pass multiple urls to implements a fallback machine when one of the urls
|
||||
* is sending an error or not exists anymore.
|
||||
*
|
||||
* @param array $urls
|
||||
* @param array $args
|
||||
*
|
||||
* @return array|\WP_Error|null
|
||||
*/
|
||||
public function request_with_fallback( array $urls, $args = [] ) {
|
||||
$response = null;
|
||||
|
||||
foreach ( $urls as $url ) {
|
||||
$response = $this->request( $url, $args );
|
||||
|
||||
if ( $this->is_successful_response( $response ) ) {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $response
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_successful_response( $response ) {
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( in_array( $response_code, [ 0, 404, 500 ], true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils\ImportExport\Parsers;
|
||||
|
||||
use WP_Error;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress eXtended RSS file parser implementations
|
||||
* Originally made by WordPress part of WordPress/Importer.
|
||||
* https://plugins.trac.wordpress.org/browser/wordpress-importer/trunk/parsers/class-wxr-parser-regex.php
|
||||
*
|
||||
* What was done:
|
||||
* Reformat of the code.
|
||||
* Changed text domain.
|
||||
* Changed methods visibility.
|
||||
*/
|
||||
|
||||
/**
|
||||
* WXR Parser that uses regular expressions. Fallback for installs without an XML parser.
|
||||
*/
|
||||
class WXR_Parser_Regex {
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $has_gzip;
|
||||
|
||||
private $authors = [];
|
||||
private $posts = [];
|
||||
private $categories = [];
|
||||
private $tags = [];
|
||||
private $terms = [];
|
||||
private $base_url = '';
|
||||
private $base_blog_url = '';
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function parse( $file ) {
|
||||
$wxr_version = '';
|
||||
$in_multiline = false;
|
||||
|
||||
$multiline_content = '';
|
||||
|
||||
$multiline_tags = [
|
||||
'item' => [
|
||||
'posts',
|
||||
function ( $post ) {
|
||||
return $this->process_post( $post );
|
||||
},
|
||||
],
|
||||
'wp:category' => [
|
||||
'categories',
|
||||
function ( $category ) {
|
||||
return $this->process_category( $category );
|
||||
},
|
||||
],
|
||||
'wp:tag' => [
|
||||
'tags',
|
||||
function ( $tag ) {
|
||||
return $this->process_tag( $tag );
|
||||
},
|
||||
],
|
||||
'wp:term' => [
|
||||
'terms',
|
||||
function ( $term ) {
|
||||
return $this->process_term( $term );
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
$fp = $this->fopen( $file, 'r' );
|
||||
if ( $fp ) {
|
||||
while ( ! $this->feof( $fp ) ) {
|
||||
$importline = rtrim( $this->fgets( $fp ) );
|
||||
|
||||
if ( ! $wxr_version && preg_match( '|<wp:wxr_version>(\d+\.\d+)</wp:wxr_version>|', $importline, $version ) ) {
|
||||
$wxr_version = $version[1];
|
||||
}
|
||||
|
||||
if ( false !== strpos( $importline, '<wp:base_site_url>' ) ) {
|
||||
preg_match( '|<wp:base_site_url>(.*?)</wp:base_site_url>|is', $importline, $url );
|
||||
$this->base_url = $url[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( false !== strpos( $importline, '<wp:base_blog_url>' ) ) {
|
||||
preg_match( '|<wp:base_blog_url>(.*?)</wp:base_blog_url>|is', $importline, $blog_url );
|
||||
$this->base_blog_url = $blog_url[1];
|
||||
continue;
|
||||
} else {
|
||||
$this->base_blog_url = $this->base_url;
|
||||
}
|
||||
|
||||
if ( false !== strpos( $importline, '<wp:author>' ) ) {
|
||||
preg_match( '|<wp:author>(.*?)</wp:author>|is', $importline, $author );
|
||||
$a = $this->process_author( $author[1] );
|
||||
$this->authors[ $a['author_login'] ] = $a;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $multiline_tags as $tag => $handler ) {
|
||||
// Handle multi-line tags on a singular line.
|
||||
if ( preg_match( '|<' . $tag . '>(.*?)</' . $tag . '>|is', $importline, $matches ) ) {
|
||||
$this->{$handler[0]}[] = call_user_func( $handler[1], $matches[1] );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$pos = strpos( $importline, "<$tag>" );
|
||||
|
||||
if ( false !== $pos ) {
|
||||
// Take note of any content after the opening tag.
|
||||
$multiline_content = trim( substr( $importline, $pos + strlen( $tag ) + 2 ) );
|
||||
|
||||
// We don't want to have this line added to `$is_multiline` below.
|
||||
$importline = '';
|
||||
$in_multiline = $tag;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$pos = strpos( $importline, "</$tag>" );
|
||||
|
||||
if ( false !== $pos ) {
|
||||
$in_multiline = false;
|
||||
$multiline_content .= trim( substr( $importline, 0, $pos ) );
|
||||
|
||||
$this->{$handler[0]}[] = call_user_func( $handler[1], $multiline_content );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $in_multiline && $importline ) {
|
||||
$multiline_content .= $importline . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$this->fclose( $fp );
|
||||
}
|
||||
|
||||
if ( ! $wxr_version ) {
|
||||
return new WP_Error( 'WXR_parse_error', esc_html__( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'elementor' ) );
|
||||
}
|
||||
|
||||
return [
|
||||
'authors' => $this->authors,
|
||||
'posts' => $this->posts,
|
||||
'categories' => $this->categories,
|
||||
'tags' => $this->tags,
|
||||
'terms' => $this->terms,
|
||||
'base_url' => $this->base_url,
|
||||
'base_blog_url' => $this->base_blog_url,
|
||||
'version' => $wxr_version,
|
||||
];
|
||||
}
|
||||
|
||||
private function process_category( $category ) {
|
||||
$term = [
|
||||
'term_id' => $this->get_tag( $category, 'wp:term_id' ),
|
||||
'cat_name' => $this->get_tag( $category, 'wp:cat_name' ),
|
||||
'category_nicename' => $this->get_tag( $category, 'wp:category_nicename' ),
|
||||
'category_parent' => $this->get_tag( $category, 'wp:category_parent' ),
|
||||
'category_description' => $this->get_tag( $category, 'wp:category_description' ),
|
||||
];
|
||||
|
||||
$term_meta = $this->process_meta( $category, 'wp:termmeta' );
|
||||
if ( ! empty( $term_meta ) ) {
|
||||
$term['termmeta'] = $term_meta;
|
||||
}
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
private function process_tag( $tag ) {
|
||||
$term = [
|
||||
'term_id' => $this->get_tag( $tag, 'wp:term_id' ),
|
||||
'tag_name' => $this->get_tag( $tag, 'wp:tag_name' ),
|
||||
'tag_slug' => $this->get_tag( $tag, 'wp:tag_slug' ),
|
||||
'tag_description' => $this->get_tag( $tag, 'wp:tag_description' ),
|
||||
];
|
||||
|
||||
$term_meta = $this->process_meta( $tag, 'wp:termmeta' );
|
||||
if ( ! empty( $term_meta ) ) {
|
||||
$term['termmeta'] = $term_meta;
|
||||
}
|
||||
|
||||
return $term;
|
||||
}
|
||||
|
||||
private function process_term( $term ) {
|
||||
$term_data = [
|
||||
'term_id' => $this->get_tag( $term, 'wp:term_id' ),
|
||||
'term_taxonomy' => $this->get_tag( $term, 'wp:term_taxonomy' ),
|
||||
'slug' => $this->get_tag( $term, 'wp:term_slug' ),
|
||||
'term_parent' => $this->get_tag( $term, 'wp:term_parent' ),
|
||||
'term_name' => $this->get_tag( $term, 'wp:term_name' ),
|
||||
'term_description' => $this->get_tag( $term, 'wp:term_description' ),
|
||||
];
|
||||
|
||||
$term_meta = $this->process_meta( $term, 'wp:termmeta' );
|
||||
if ( ! empty( $term_meta ) ) {
|
||||
$term_data['termmeta'] = $term_meta;
|
||||
}
|
||||
|
||||
return $term_data;
|
||||
}
|
||||
|
||||
private function process_meta( $string, $tag ) {
|
||||
$parsed_meta = [];
|
||||
|
||||
preg_match_all( "|<$tag>(.+?)</$tag>|is", $string, $meta );
|
||||
|
||||
if ( ! isset( $meta[1] ) ) {
|
||||
return $parsed_meta;
|
||||
}
|
||||
|
||||
foreach ( $meta[1] as $m ) {
|
||||
$parsed_meta[] = [
|
||||
'key' => $this->get_tag( $m, 'wp:meta_key' ),
|
||||
'value' => $this->get_tag( $m, 'wp:meta_value' ),
|
||||
];
|
||||
}
|
||||
|
||||
return $parsed_meta;
|
||||
}
|
||||
|
||||
private function process_author( $a ) {
|
||||
return [
|
||||
'author_id' => $this->get_tag( $a, 'wp:author_id' ),
|
||||
'author_login' => $this->get_tag( $a, 'wp:author_login' ),
|
||||
'author_email' => $this->get_tag( $a, 'wp:author_email' ),
|
||||
'author_display_name' => $this->get_tag( $a, 'wp:author_display_name' ),
|
||||
'author_first_name' => $this->get_tag( $a, 'wp:author_first_name' ),
|
||||
'author_last_name' => $this->get_tag( $a, 'wp:author_last_name' ),
|
||||
];
|
||||
}
|
||||
|
||||
private function process_post( $post ) {
|
||||
$normalize_tag_callback = function ( $matches ) {
|
||||
return $this->normalize_tag( $matches );
|
||||
};
|
||||
|
||||
$post_id = $this->get_tag( $post, 'wp:post_id' );
|
||||
$post_title = $this->get_tag( $post, 'title' );
|
||||
$post_date = $this->get_tag( $post, 'wp:post_date' );
|
||||
$post_date_gmt = $this->get_tag( $post, 'wp:post_date_gmt' );
|
||||
$comment_status = $this->get_tag( $post, 'wp:comment_status' );
|
||||
$ping_status = $this->get_tag( $post, 'wp:ping_status' );
|
||||
$status = $this->get_tag( $post, 'wp:status' );
|
||||
$post_name = $this->get_tag( $post, 'wp:post_name' );
|
||||
$post_parent = $this->get_tag( $post, 'wp:post_parent' );
|
||||
$menu_order = $this->get_tag( $post, 'wp:menu_order' );
|
||||
$post_type = $this->get_tag( $post, 'wp:post_type' );
|
||||
$post_password = $this->get_tag( $post, 'wp:post_password' );
|
||||
$is_sticky = $this->get_tag( $post, 'wp:is_sticky' );
|
||||
$guid = $this->get_tag( $post, 'guid' );
|
||||
$post_author = $this->get_tag( $post, 'dc:creator' );
|
||||
|
||||
$post_excerpt = $this->get_tag( $post, 'excerpt:encoded' );
|
||||
$post_excerpt = preg_replace_callback( '|<(/?[A-Z]+)|', $normalize_tag_callback, $post_excerpt );
|
||||
$post_excerpt = str_replace( '<br>', '<br />', $post_excerpt );
|
||||
$post_excerpt = str_replace( '<hr>', '<hr />', $post_excerpt );
|
||||
|
||||
$post_content = $this->get_tag( $post, 'content:encoded' );
|
||||
$post_content = preg_replace_callback( '|<(/?[A-Z]+)|', $normalize_tag_callback, $post_content );
|
||||
$post_content = str_replace( '<br>', '<br />', $post_content );
|
||||
$post_content = str_replace( '<hr>', '<hr />', $post_content );
|
||||
|
||||
$postdata = compact( 'post_id', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_excerpt', 'post_title', 'status', 'post_name', 'comment_status', 'ping_status', 'guid', 'post_parent', 'menu_order', 'post_type', 'post_password', 'is_sticky' );
|
||||
|
||||
$attachment_url = $this->get_tag( $post, 'wp:attachment_url' );
|
||||
if ( $attachment_url ) {
|
||||
$postdata['attachment_url'] = $attachment_url;
|
||||
}
|
||||
|
||||
preg_match_all( '|<category domain="([^"]+?)" nicename="([^"]+?)">(.+?)</category>|is', $post, $terms, PREG_SET_ORDER );
|
||||
foreach ( $terms as $t ) {
|
||||
$post_terms[] = [
|
||||
'slug' => $t[2],
|
||||
'domain' => $t[1],
|
||||
'name' => str_replace( [ '<![CDATA[', ']]>' ], '', $t[3] ),
|
||||
];
|
||||
}
|
||||
if ( ! empty( $post_terms ) ) {
|
||||
$postdata['terms'] = $post_terms;
|
||||
}
|
||||
|
||||
preg_match_all( '|<wp:comment>(.+?)</wp:comment>|is', $post, $comments );
|
||||
$comments = $comments[1];
|
||||
if ( $comments ) {
|
||||
foreach ( $comments as $comment ) {
|
||||
$post_comments[] = [
|
||||
'comment_id' => $this->get_tag( $comment, 'wp:comment_id' ),
|
||||
'comment_author' => $this->get_tag( $comment, 'wp:comment_author' ),
|
||||
'comment_author_email' => $this->get_tag( $comment, 'wp:comment_author_email' ),
|
||||
'comment_author_IP' => $this->get_tag( $comment, 'wp:comment_author_IP' ),
|
||||
'comment_author_url' => $this->get_tag( $comment, 'wp:comment_author_url' ),
|
||||
'comment_date' => $this->get_tag( $comment, 'wp:comment_date' ),
|
||||
'comment_date_gmt' => $this->get_tag( $comment, 'wp:comment_date_gmt' ),
|
||||
'comment_content' => $this->get_tag( $comment, 'wp:comment_content' ),
|
||||
'comment_approved' => $this->get_tag( $comment, 'wp:comment_approved' ),
|
||||
'comment_type' => $this->get_tag( $comment, 'wp:comment_type' ),
|
||||
'comment_parent' => $this->get_tag( $comment, 'wp:comment_parent' ),
|
||||
'comment_user_id' => $this->get_tag( $comment, 'wp:comment_user_id' ),
|
||||
'commentmeta' => $this->process_meta( $comment, 'wp:commentmeta' ),
|
||||
];
|
||||
}
|
||||
}
|
||||
if ( ! empty( $post_comments ) ) {
|
||||
$postdata['comments'] = $post_comments;
|
||||
}
|
||||
|
||||
$post_meta = $this->process_meta( $post, 'wp:postmeta' );
|
||||
if ( ! empty( $post_meta ) ) {
|
||||
$postdata['postmeta'] = $post_meta;
|
||||
}
|
||||
|
||||
return $postdata;
|
||||
}
|
||||
|
||||
private function get_tag( $string, $tag ) {
|
||||
preg_match( "|<$tag.*?>(.*?)</$tag>|is", $string, $return );
|
||||
if ( isset( $return[1] ) ) {
|
||||
if ( substr( $return[1], 0, 9 ) == '<![CDATA[' ) {
|
||||
if ( strpos( $return[1], ']]]]><![CDATA[>' ) !== false ) {
|
||||
preg_match_all( '|<!\[CDATA\[(.*?)\]\]>|s', $return[1], $matches );
|
||||
$return = '';
|
||||
foreach ( $matches[1] as $match ) {
|
||||
$return .= $match;
|
||||
}
|
||||
} else {
|
||||
$return = preg_replace( '|^<!\[CDATA\[(.*)\]\]>$|s', '$1', $return[1] );
|
||||
}
|
||||
} else {
|
||||
$return = $return[1];
|
||||
}
|
||||
} else {
|
||||
$return = '';
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function normalize_tag( $matches ) {
|
||||
return '<' . strtolower( $matches[1] );
|
||||
}
|
||||
|
||||
private function fopen( $filename, $mode = 'r' ) {
|
||||
if ( $this->has_gzip ) {
|
||||
return gzopen( $filename, $mode );
|
||||
}
|
||||
|
||||
return fopen( $filename, $mode );
|
||||
}
|
||||
|
||||
private function feof( $fp ) {
|
||||
if ( $this->has_gzip ) {
|
||||
return gzeof( $fp );
|
||||
}
|
||||
|
||||
return feof( $fp );
|
||||
}
|
||||
|
||||
private function fgets( $fp, $len = 8192 ) {
|
||||
if ( $this->has_gzip ) {
|
||||
return gzgets( $fp, $len );
|
||||
}
|
||||
|
||||
return fgets( $fp, $len );
|
||||
}
|
||||
|
||||
private function fclose( $fp ) {
|
||||
if ( $this->has_gzip ) {
|
||||
return gzclose( $fp );
|
||||
}
|
||||
|
||||
return fclose( $fp );
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
$this->has_gzip = is_callable( 'gzopen' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils\ImportExport\Parsers;
|
||||
|
||||
use Elementor\Utils;
|
||||
use WP_Error;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress eXtended RSS file parser implementations,
|
||||
* Originally made by WordPress part of WordPress/Importer.
|
||||
* https://plugins.trac.wordpress.org/browser/wordpress-importer/trunk/parsers/class-wxr-parser-simplexml.php
|
||||
*
|
||||
* What was done:
|
||||
* Reformat of the code.
|
||||
* Removed variable '$internal_errors'.
|
||||
* Changed text domain.
|
||||
*/
|
||||
|
||||
/**
|
||||
* WXR Parser that makes use of the SimpleXML PHP extension.
|
||||
*/
|
||||
class WXR_Parser_SimpleXML {
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function parse( $file ) {
|
||||
$authors = [];
|
||||
$posts = [];
|
||||
$categories = [];
|
||||
$tags = [];
|
||||
$terms = [];
|
||||
|
||||
libxml_use_internal_errors( true );
|
||||
|
||||
$dom = new \DOMDocument();
|
||||
$old_value = null;
|
||||
|
||||
$libxml_disable_entity_loader_exists = function_exists( 'libxml_disable_entity_loader' );
|
||||
|
||||
if ( $libxml_disable_entity_loader_exists ) {
|
||||
$old_value = libxml_disable_entity_loader( true ); // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated
|
||||
}
|
||||
|
||||
$success = $dom->loadXML( Utils::file_get_contents( $file ) );
|
||||
|
||||
if ( $libxml_disable_entity_loader_exists && ! is_null( $old_value ) ) {
|
||||
libxml_disable_entity_loader( $old_value ); // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated
|
||||
}
|
||||
|
||||
if ( ! $success || isset( $dom->doctype ) ) {
|
||||
return new WP_Error( 'SimpleXML_parse_error', esc_html__( 'There was an error when reading this WXR file', 'elementor' ), libxml_get_errors() );
|
||||
}
|
||||
|
||||
$xml = simplexml_import_dom( $dom );
|
||||
unset( $dom );
|
||||
|
||||
// Halt if loading produces an error.
|
||||
if ( ! $xml ) {
|
||||
return new WP_Error( 'SimpleXML_parse_error', esc_html__( 'There was an error when reading this WXR file', 'elementor' ), libxml_get_errors() );
|
||||
}
|
||||
|
||||
$wxr_version = $xml->xpath( '/rss/channel/wp:wxr_version' );
|
||||
if ( ! $wxr_version ) {
|
||||
return new WP_Error( 'WXR_parse_error', esc_html__( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'elementor' ) );
|
||||
}
|
||||
|
||||
$wxr_version = (string) trim( $wxr_version[0] );
|
||||
// Confirm that we are dealing with the correct file format.
|
||||
if ( ! preg_match( '/^\d+\.\d+$/', $wxr_version ) ) {
|
||||
return new WP_Error( 'WXR_parse_error', esc_html__( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'elementor' ) );
|
||||
}
|
||||
|
||||
$base_url = $xml->xpath( '/rss/channel/wp:base_site_url' );
|
||||
$base_url = (string) trim( isset( $base_url[0] ) ? $base_url[0] : '' );
|
||||
|
||||
$base_blog_url = $xml->xpath( '/rss/channel/wp:base_blog_url' );
|
||||
if ( $base_blog_url ) {
|
||||
$base_blog_url = (string) trim( $base_blog_url[0] );
|
||||
} else {
|
||||
$base_blog_url = $base_url;
|
||||
}
|
||||
|
||||
$page_on_front = $xml->xpath( '/rss/channel/wp:page_on_front' );
|
||||
|
||||
if ( $page_on_front ) {
|
||||
$page_on_front = (int) $page_on_front[0];
|
||||
}
|
||||
|
||||
$namespaces = $xml->getDocNamespaces();
|
||||
if ( ! isset( $namespaces['wp'] ) ) {
|
||||
$namespaces['wp'] = 'http://wordpress.org/export/1.1/';
|
||||
}
|
||||
if ( ! isset( $namespaces['excerpt'] ) ) {
|
||||
$namespaces['excerpt'] = 'http://wordpress.org/export/1.1/excerpt/';
|
||||
}
|
||||
|
||||
// Grab authors.
|
||||
foreach ( $xml->xpath( '/rss/channel/wp:author' ) as $author_arr ) {
|
||||
$a = $author_arr->children( $namespaces['wp'] );
|
||||
$login = (string) $a->author_login;
|
||||
$authors[ $login ] = [
|
||||
'author_id' => (int) $a->author_id,
|
||||
'author_login' => $login,
|
||||
'author_email' => (string) $a->author_email,
|
||||
'author_display_name' => (string) $a->author_display_name,
|
||||
'author_first_name' => (string) $a->author_first_name,
|
||||
'author_last_name' => (string) $a->author_last_name,
|
||||
];
|
||||
}
|
||||
|
||||
// Grab cats, tags and terms.
|
||||
foreach ( $xml->xpath( '/rss/channel/wp:category' ) as $term_arr ) {
|
||||
$t = $term_arr->children( $namespaces['wp'] );
|
||||
$category = [
|
||||
'term_id' => (int) $t->term_id,
|
||||
'category_nicename' => (string) $t->category_nicename,
|
||||
'category_parent' => (string) $t->category_parent,
|
||||
'cat_name' => (string) $t->cat_name,
|
||||
'category_description' => (string) $t->category_description,
|
||||
];
|
||||
|
||||
foreach ( $t->termmeta as $meta ) {
|
||||
$category['termmeta'][] = [
|
||||
'key' => (string) $meta->meta_key,
|
||||
'value' => (string) $meta->meta_value,
|
||||
];
|
||||
}
|
||||
|
||||
$categories[] = $category;
|
||||
}
|
||||
|
||||
foreach ( $xml->xpath( '/rss/channel/wp:tag' ) as $term_arr ) {
|
||||
$t = $term_arr->children( $namespaces['wp'] );
|
||||
$tag = [
|
||||
'term_id' => (int) $t->term_id,
|
||||
'tag_slug' => (string) $t->tag_slug,
|
||||
'tag_name' => (string) $t->tag_name,
|
||||
'tag_description' => (string) $t->tag_description,
|
||||
];
|
||||
|
||||
foreach ( $t->termmeta as $meta ) {
|
||||
$tag['termmeta'][] = [
|
||||
'key' => (string) $meta->meta_key,
|
||||
'value' => (string) $meta->meta_value,
|
||||
];
|
||||
}
|
||||
|
||||
$tags[] = $tag;
|
||||
}
|
||||
|
||||
foreach ( $xml->xpath( '/rss/channel/wp:term' ) as $term_arr ) {
|
||||
$t = $term_arr->children( $namespaces['wp'] );
|
||||
$term = [
|
||||
'term_id' => (int) $t->term_id,
|
||||
'term_taxonomy' => (string) $t->term_taxonomy,
|
||||
'slug' => (string) $t->term_slug,
|
||||
'term_parent' => (string) $t->term_parent,
|
||||
'term_name' => (string) $t->term_name,
|
||||
'term_description' => (string) $t->term_description,
|
||||
];
|
||||
|
||||
foreach ( $t->termmeta as $meta ) {
|
||||
$term['termmeta'][] = [
|
||||
'key' => (string) $meta->meta_key,
|
||||
'value' => (string) $meta->meta_value,
|
||||
];
|
||||
}
|
||||
|
||||
$terms[] = $term;
|
||||
}
|
||||
|
||||
// Grab posts.
|
||||
foreach ( $xml->channel->item as $item ) {
|
||||
$post = [
|
||||
'post_title' => (string) $item->title,
|
||||
'guid' => (string) $item->guid,
|
||||
];
|
||||
|
||||
$dc = $item->children( 'http://purl.org/dc/elements/1.1/' );
|
||||
$post['post_author'] = (string) $dc->creator;
|
||||
|
||||
$content = $item->children( 'http://purl.org/rss/1.0/modules/content/' );
|
||||
$excerpt = $item->children( $namespaces['excerpt'] );
|
||||
$post['post_content'] = (string) $content->encoded;
|
||||
$post['post_excerpt'] = (string) $excerpt->encoded;
|
||||
|
||||
$wp = $item->children( $namespaces['wp'] );
|
||||
$post['post_id'] = (int) $wp->post_id;
|
||||
$post['post_date'] = (string) $wp->post_date;
|
||||
$post['post_date_gmt'] = (string) $wp->post_date_gmt;
|
||||
$post['comment_status'] = (string) $wp->comment_status;
|
||||
$post['ping_status'] = (string) $wp->ping_status;
|
||||
$post['post_name'] = (string) $wp->post_name;
|
||||
$post['status'] = (string) $wp->status;
|
||||
$post['post_parent'] = (int) $wp->post_parent;
|
||||
$post['menu_order'] = (int) $wp->menu_order;
|
||||
$post['post_type'] = (string) $wp->post_type;
|
||||
$post['post_password'] = (string) $wp->post_password;
|
||||
$post['is_sticky'] = (int) $wp->is_sticky;
|
||||
|
||||
if ( isset( $wp->attachment_url ) ) {
|
||||
$post['attachment_url'] = (string) $wp->attachment_url;
|
||||
}
|
||||
|
||||
foreach ( $item->category as $c ) {
|
||||
$att = $c->attributes();
|
||||
if ( isset( $att['nicename'] ) ) {
|
||||
$post['terms'][] = [
|
||||
'name' => (string) $c,
|
||||
'slug' => (string) $att['nicename'],
|
||||
'domain' => (string) $att['domain'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $wp->postmeta as $meta ) {
|
||||
$post['postmeta'][] = [
|
||||
'key' => (string) $meta->meta_key,
|
||||
'value' => (string) $meta->meta_value,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ( $wp->comment as $comment ) {
|
||||
$meta = [];
|
||||
if ( isset( $comment->commentmeta ) ) {
|
||||
foreach ( $comment->commentmeta as $m ) {
|
||||
$meta[] = [
|
||||
'key' => (string) $m->meta_key,
|
||||
'value' => (string) $m->meta_value,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$post['comments'][] = [
|
||||
'comment_id' => (int) $comment->comment_id,
|
||||
'comment_author' => (string) $comment->comment_author,
|
||||
'comment_author_email' => (string) $comment->comment_author_email,
|
||||
'comment_author_IP' => (string) $comment->comment_author_IP,
|
||||
'comment_author_url' => (string) $comment->comment_author_url,
|
||||
'comment_date' => (string) $comment->comment_date,
|
||||
'comment_date_gmt' => (string) $comment->comment_date_gmt,
|
||||
'comment_content' => (string) $comment->comment_content,
|
||||
'comment_approved' => (string) $comment->comment_approved,
|
||||
'comment_type' => (string) $comment->comment_type,
|
||||
'comment_parent' => (string) $comment->comment_parent,
|
||||
'comment_user_id' => (int) $comment->comment_user_id,
|
||||
'commentmeta' => $meta,
|
||||
];
|
||||
}
|
||||
|
||||
$posts[] = $post;
|
||||
}
|
||||
|
||||
return [
|
||||
'authors' => $authors,
|
||||
'posts' => $posts,
|
||||
'categories' => $categories,
|
||||
'tags' => $tags,
|
||||
'terms' => $terms,
|
||||
'base_url' => $base_url,
|
||||
'base_blog_url' => $base_blog_url,
|
||||
'page_on_front' => $page_on_front,
|
||||
'version' => $wxr_version,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils\ImportExport\Parsers;
|
||||
|
||||
use Elementor\Utils;
|
||||
use WP_Error;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress eXtended RSS file parser implementations,
|
||||
* Originally made by WordPress part of WordPress/Importer.
|
||||
* https://plugins.trac.wordpress.org/browser/wordpress-importer/trunk/parsers/class-wxr-parser-xml.php
|
||||
*
|
||||
* What was done:
|
||||
* Reformat of the code.
|
||||
* Added PHPDOC.
|
||||
* Changed text domain.
|
||||
* Added clear() method.
|
||||
* Added undeclared class properties.
|
||||
* Changed methods visibility.
|
||||
*/
|
||||
|
||||
/**
|
||||
* WXR Parser that makes use of the XML Parser PHP extension.
|
||||
*/
|
||||
class WXR_Parser_XML {
|
||||
private static $wp_tags = [
|
||||
'wp:post_id',
|
||||
'wp:post_date',
|
||||
'wp:post_date_gmt',
|
||||
'wp:comment_status',
|
||||
'wp:ping_status',
|
||||
'wp:attachment_url',
|
||||
'wp:status',
|
||||
'wp:post_name',
|
||||
'wp:post_parent',
|
||||
'wp:menu_order',
|
||||
'wp:post_type',
|
||||
'wp:post_password',
|
||||
'wp:is_sticky',
|
||||
'wp:term_id',
|
||||
'wp:category_nicename',
|
||||
'wp:category_parent',
|
||||
'wp:cat_name',
|
||||
'wp:category_description',
|
||||
'wp:tag_slug',
|
||||
'wp:tag_name',
|
||||
'wp:tag_description',
|
||||
'wp:term_taxonomy',
|
||||
'wp:term_parent',
|
||||
'wp:term_name',
|
||||
'wp:term_description',
|
||||
'wp:author_id',
|
||||
'wp:author_login',
|
||||
'wp:author_email',
|
||||
'wp:author_display_name',
|
||||
'wp:author_first_name',
|
||||
'wp:author_last_name',
|
||||
];
|
||||
|
||||
private static $wp_sub_tags = [
|
||||
'wp:comment_id',
|
||||
'wp:comment_author',
|
||||
'wp:comment_author_email',
|
||||
'wp:comment_author_url',
|
||||
'wp:comment_author_IP',
|
||||
'wp:comment_date',
|
||||
'wp:comment_date_gmt',
|
||||
'wp:comment_content',
|
||||
'wp:comment_approved',
|
||||
'wp:comment_type',
|
||||
'wp:comment_parent',
|
||||
'wp:comment_user_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $wxr_version;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $cdata;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $sub_data;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $in_post;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $in_tag;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
private $in_sub_tag;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $authors;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $posts;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $term;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $tag;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $base_url;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $base_blog_url;
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function parse( $file ) {
|
||||
$this->clear();
|
||||
|
||||
$xml = xml_parser_create( 'UTF-8' );
|
||||
xml_parser_set_option( $xml, XML_OPTION_SKIP_WHITE, 1 );
|
||||
xml_parser_set_option( $xml, XML_OPTION_CASE_FOLDING, 0 );
|
||||
xml_set_object( $xml, $this );
|
||||
|
||||
xml_set_character_data_handler( $xml, function ( $parser, $cdata ) {
|
||||
$this->cdata( $cdata );
|
||||
} );
|
||||
|
||||
$tag_open_callback = function ( $parse, $tag, $attr ) {
|
||||
$this->tag_open( $tag, $attr );
|
||||
};
|
||||
|
||||
$tag_close_callback = function ( $parser, $tag ) {
|
||||
$this->tag_close( $tag );
|
||||
};
|
||||
|
||||
xml_set_element_handler( $xml, $tag_open_callback, $tag_close_callback );
|
||||
|
||||
if ( ! xml_parse( $xml, Utils::file_get_contents( $file ), true ) ) {
|
||||
$current_line = xml_get_current_line_number( $xml );
|
||||
$current_column = xml_get_current_column_number( $xml );
|
||||
$error_code = xml_get_error_code( $xml );
|
||||
$error_string = xml_error_string( $error_code );
|
||||
|
||||
return new WP_Error( 'XML_parse_error', 'There was an error when reading this WXR file', [
|
||||
$current_line,
|
||||
$current_column,
|
||||
$error_string,
|
||||
] );
|
||||
}
|
||||
xml_parser_free( $xml );
|
||||
|
||||
if ( ! preg_match( '/^\d+\.\d+$/', $this->wxr_version ) ) {
|
||||
return new WP_Error( 'WXR_parse_error', esc_html__( 'This does not appear to be a WXR file, missing/invalid WXR version number', 'elementor' ) );
|
||||
}
|
||||
|
||||
return array(
|
||||
'authors' => $this->authors,
|
||||
'posts' => $this->posts,
|
||||
'categories' => $this->category,
|
||||
'tags' => $this->tag,
|
||||
'terms' => $this->term,
|
||||
'base_url' => $this->base_url,
|
||||
'base_blog_url' => $this->base_blog_url,
|
||||
'version' => $this->wxr_version,
|
||||
);
|
||||
}
|
||||
|
||||
private function tag_open( $tag, $attr ) {
|
||||
if ( in_array( $tag, self::$wp_tags ) ) {
|
||||
$this->in_tag = substr( $tag, 3 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( in_array( $tag, self::$wp_sub_tags ) ) {
|
||||
$this->in_sub_tag = substr( $tag, 3 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( $tag ) {
|
||||
case 'category':
|
||||
if ( isset( $attr['domain'], $attr['nicename'] ) ) {
|
||||
$this->sub_data['domain'] = $attr['domain'];
|
||||
$this->sub_data['slug'] = $attr['nicename'];
|
||||
}
|
||||
break;
|
||||
case 'item':
|
||||
$this->in_post = true;
|
||||
// No break !!!.
|
||||
case 'title':
|
||||
if ( $this->in_post ) {
|
||||
$this->in_tag = 'post_title';
|
||||
}
|
||||
break;
|
||||
case 'guid':
|
||||
$this->in_tag = 'guid';
|
||||
break;
|
||||
case 'dc:creator':
|
||||
$this->in_tag = 'post_author';
|
||||
break;
|
||||
case 'content:encoded':
|
||||
$this->in_tag = 'post_content';
|
||||
break;
|
||||
case 'excerpt:encoded':
|
||||
$this->in_tag = 'post_excerpt';
|
||||
break;
|
||||
|
||||
case 'wp:term_slug':
|
||||
$this->in_tag = 'slug';
|
||||
break;
|
||||
case 'wp:meta_key':
|
||||
$this->in_sub_tag = 'key';
|
||||
break;
|
||||
case 'wp:meta_value':
|
||||
$this->in_sub_tag = 'value';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function cdata( $cdata ) {
|
||||
if ( ! trim( $cdata ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( false !== $this->in_tag || false !== $this->in_sub_tag ) {
|
||||
$this->cdata .= $cdata;
|
||||
} else {
|
||||
$this->cdata .= trim( $cdata );
|
||||
}
|
||||
}
|
||||
|
||||
private function tag_close( $tag ) {
|
||||
switch ( $tag ) {
|
||||
case 'wp:comment':
|
||||
unset( $this->sub_data['key'], $this->sub_data['value'] ); // Remove meta sub_data.
|
||||
if ( ! empty( $this->sub_data ) ) {
|
||||
$this->data['comments'][] = $this->sub_data;
|
||||
}
|
||||
$this->sub_data = [];
|
||||
break;
|
||||
case 'wp:commentmeta':
|
||||
$this->sub_data['commentmeta'][] = [
|
||||
'key' => $this->sub_data['key'],
|
||||
'value' => $this->sub_data['value'],
|
||||
];
|
||||
break;
|
||||
case 'category':
|
||||
if ( ! empty( $this->sub_data ) ) {
|
||||
$this->sub_data['name'] = $this->cdata;
|
||||
$this->data['terms'][] = $this->sub_data;
|
||||
}
|
||||
$this->sub_data = [];
|
||||
break;
|
||||
case 'wp:postmeta':
|
||||
if ( ! empty( $this->sub_data ) ) {
|
||||
$this->data['postmeta'][] = $this->sub_data;
|
||||
}
|
||||
$this->sub_data = [];
|
||||
break;
|
||||
case 'item':
|
||||
$this->posts[] = $this->data;
|
||||
$this->data = [];
|
||||
break;
|
||||
case 'wp:category':
|
||||
case 'wp:tag':
|
||||
case 'wp:term':
|
||||
$n = substr( $tag, 3 );
|
||||
array_push( $this->$n, $this->data );
|
||||
$this->data = [];
|
||||
break;
|
||||
case 'wp:termmeta':
|
||||
if ( ! empty( $this->sub_data ) ) {
|
||||
$this->data['termmeta'][] = $this->sub_data;
|
||||
}
|
||||
$this->sub_data = [];
|
||||
break;
|
||||
case 'wp:author':
|
||||
if ( ! empty( $this->data['author_login'] ) ) {
|
||||
$this->authors[ $this->data['author_login'] ] = $this->data;
|
||||
}
|
||||
$this->data = [];
|
||||
break;
|
||||
case 'wp:base_site_url':
|
||||
$this->base_url = $this->cdata;
|
||||
if ( ! isset( $this->base_blog_url ) ) {
|
||||
$this->base_blog_url = $this->cdata;
|
||||
}
|
||||
break;
|
||||
case 'wp:base_blog_url':
|
||||
$this->base_blog_url = $this->cdata;
|
||||
break;
|
||||
case 'wp:wxr_version':
|
||||
$this->wxr_version = $this->cdata;
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( $this->in_sub_tag ) {
|
||||
$this->sub_data[ $this->in_sub_tag ] = $this->cdata;
|
||||
$this->in_sub_tag = false;
|
||||
} else if ( $this->in_tag ) {
|
||||
$this->data[ $this->in_tag ] = $this->cdata;
|
||||
$this->in_tag = false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->cdata = '';
|
||||
}
|
||||
|
||||
private function clear() {
|
||||
$this->wxr_version = '';
|
||||
|
||||
$this->cdata = '';
|
||||
$this->data = [];
|
||||
$this->sub_data = [];
|
||||
|
||||
$this->in_post = false;
|
||||
$this->in_tag = false;
|
||||
$this->in_sub_tag = false;
|
||||
|
||||
$this->authors = [];
|
||||
$this->posts = [];
|
||||
$this->term = [];
|
||||
$this->category = [];
|
||||
$this->tag = [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils\ImportExport\Parsers;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress eXtended RSS file parser implementations,
|
||||
* Originally made by WordPress part of WordPress/Importer.
|
||||
* https://plugins.trac.wordpress.org/browser/wordpress-importer/trunk/parsers/class-wxr-parser.php
|
||||
*
|
||||
* What was done:
|
||||
* Reformat of the code.
|
||||
* Changed text domain.
|
||||
*/
|
||||
|
||||
/**
|
||||
* WordPress Importer class for managing parsing of WXR files.
|
||||
*/
|
||||
class WXR_Parser {
|
||||
|
||||
public function parse( $file ) {
|
||||
// Attempt to use proper XML parsers first.
|
||||
if ( extension_loaded( 'simplexml' ) ) {
|
||||
$parser = new WXR_Parser_SimpleXML();
|
||||
$result = $parser->parse( $file );
|
||||
|
||||
// If SimpleXML succeeds or this is an invalid WXR file then return the results.
|
||||
if ( ! is_wp_error( $result ) || 'SimpleXML_parse_error' != $result->get_error_code() ) {
|
||||
return $result;
|
||||
}
|
||||
} elseif ( extension_loaded( 'xml' ) ) {
|
||||
$parser = new WXR_Parser_XML();
|
||||
$result = $parser->parse( $file );
|
||||
|
||||
// If XMLParser succeeds or this is an invalid WXR file then return the results.
|
||||
if ( ! is_wp_error( $result ) || 'XML_parse_error' != $result->get_error_code() ) {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// Use regular expressions if nothing else available or this is bad XML.
|
||||
$parser = new WXR_Parser_Regex();
|
||||
|
||||
return $parser->parse( $file );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Utils\ImportExport;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Url {
|
||||
|
||||
/**
|
||||
* Migrate url to the current permalink structure.
|
||||
* The function will also check and change absolute url to relative one by the base url.
|
||||
* This is currently supports only "Post Name" permalink structure to any permalink structure.
|
||||
*
|
||||
* @param string $url The url that should be migrated.
|
||||
* @param string|Null $base_url The base url that should be clean from the url.
|
||||
* @return string The migrated url || the $url if it couldn't find a match in the current permalink structure.
|
||||
*/
|
||||
public static function migrate( $url, $base_url = '' ) {
|
||||
$full_url = $url;
|
||||
|
||||
if ( ! empty( $base_url ) ) {
|
||||
$base_url = preg_quote( $base_url, '/' );
|
||||
$url = preg_replace( "/^{$base_url}/", '', $url );
|
||||
}
|
||||
|
||||
$parsed_url = wp_parse_url( $url );
|
||||
|
||||
if ( $url === $full_url && ! empty( $parsed_url['host'] ) ) {
|
||||
return $full_url;
|
||||
}
|
||||
|
||||
if ( ! empty( $parsed_url['path'] ) ) {
|
||||
$page = get_page_by_path( $parsed_url['path'] );
|
||||
|
||||
if ( ! $page ) {
|
||||
return $full_url;
|
||||
}
|
||||
|
||||
$permalink = get_permalink( $page->ID );
|
||||
}
|
||||
|
||||
if ( empty( $permalink ) ) {
|
||||
return $full_url;
|
||||
}
|
||||
|
||||
if ( ! empty( $parsed_url['query'] ) ) {
|
||||
parse_str( $parsed_url['query'], $parsed_query );
|
||||
|
||||
// Clean WP permalinks query args to prevent collision with the new permalink.
|
||||
unset( $parsed_query['p'] );
|
||||
unset( $parsed_query['page_id'] );
|
||||
|
||||
$permalink = add_query_arg( $parsed_query, $permalink );
|
||||
}
|
||||
|
||||
if ( ! empty( $parsed_url['fragment'] ) ) {
|
||||
$permalink .= '#' . $parsed_url['fragment'];
|
||||
}
|
||||
|
||||
return wp_make_link_relative( $permalink );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils\ImportExport;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/*
|
||||
* Originally made by WordPress.
|
||||
*
|
||||
* What changed:
|
||||
* Remove echos.
|
||||
* Fix indents.
|
||||
* Add methods
|
||||
* indent.
|
||||
* wxr_categories_list.
|
||||
* wxr_tags_list.
|
||||
* wxr_terms_list.
|
||||
* wxr_posts_list.
|
||||
*/
|
||||
class WP_Exporter {
|
||||
const WXR_VERSION = '1.2';
|
||||
|
||||
private static $default_args = [
|
||||
'content' => 'all',
|
||||
'author' => false,
|
||||
'category' => false,
|
||||
'start_date' => false,
|
||||
'end_date' => false,
|
||||
'status' => false,
|
||||
'offset' => 0,
|
||||
'limit' => -1,
|
||||
'meta_query' => [], // If specified `meta_key` then will include all post(s) that have this meta_key.
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $args;
|
||||
|
||||
/**
|
||||
* @var \wpdb
|
||||
*/
|
||||
private $wpdb;
|
||||
|
||||
private $terms;
|
||||
|
||||
/**
|
||||
* Run export, by requested args.
|
||||
* Returns XML with exported data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function run() {
|
||||
if ( 'all' !== $this->args['content'] && post_type_exists( $this->args['content'] ) ) {
|
||||
$ptype = get_post_type_object( $this->args['content'] );
|
||||
if ( ! $ptype->can_export ) {
|
||||
$this->args['content'] = 'post';
|
||||
}
|
||||
|
||||
$where = $this->wpdb->prepare( "{$this->wpdb->posts}.post_type = %s", $this->args['content'] );// phpcs:ignore
|
||||
} else {
|
||||
$post_types = get_post_types( [ 'can_export' => true ] );
|
||||
$esses = array_fill( 0, count( $post_types ), '%s' );
|
||||
|
||||
$where = $this->wpdb->prepare( "{$this->wpdb->posts}.post_type IN (" . implode( ',', $esses ) . ')', $post_types );// phpcs:ignore
|
||||
}
|
||||
|
||||
if ( $this->args['status'] && ( 'post' === $this->args['content'] || 'page' === $this->args['content'] || 'nav_menu_item' === $this->args['content'] ) ) {
|
||||
$where .= $this->wpdb->prepare( " AND {$this->wpdb->posts}.post_status = %s", $this->args['status'] );// phpcs:ignore
|
||||
} else {
|
||||
$where .= " AND {$this->wpdb->posts}.post_status != 'auto-draft'";
|
||||
}
|
||||
|
||||
$join = '';
|
||||
if ( $this->args['category'] && 'post' === $this->args['content'] ) {
|
||||
$term = term_exists( $this->args['category'], 'category' );
|
||||
if ( $term ) {
|
||||
$join = "INNER JOIN {$this->wpdb->term_relationships} ON ({$this->wpdb->posts}.ID = {$this->wpdb->term_relationships}.object_id)";
|
||||
$where .= $this->wpdb->prepare( " AND {$this->wpdb->term_relationships}.term_taxonomy_id = %d", $term['term_taxonomy_id'] );// phpcs:ignore
|
||||
}
|
||||
}
|
||||
|
||||
if ( in_array( $this->args['content'], [ 'post', 'page', 'attachment' ], true ) ) {
|
||||
if ( $this->args['author'] ) {
|
||||
$where .= $this->wpdb->prepare( " AND {$this->wpdb->posts}.post_author = %d", $this->args['author'] );// phpcs:ignore
|
||||
}
|
||||
|
||||
if ( $this->args['start_date'] ) {
|
||||
$where .= $this->wpdb->prepare( " AND {$this->wpdb->posts}.post_date >= %s", gmdate( 'Y-m-d', strtotime( $this->args['start_date'] ) ) );// phpcs:ignore
|
||||
}
|
||||
|
||||
if ( $this->args['end_date'] ) {
|
||||
$where .= $this->wpdb->prepare( " AND {$this->wpdb->posts}.post_date < %s", gmdate( 'Y-m-d', strtotime( '+1 month', strtotime( $this->args['end_date'] ) ) ) );// phpcs:ignore
|
||||
}
|
||||
}
|
||||
|
||||
$limit = '';
|
||||
if ( -1 !== (int) $this->args['limit'] ) {
|
||||
$limit = 'LIMIT ' . (int) $this->args['limit'] . ' OFFSET ' . (int) $this->args['offset'];
|
||||
}
|
||||
|
||||
if ( ! empty( $this->args['meta_query'] ) ) {
|
||||
if ( $join ) {
|
||||
$join .= ' ';
|
||||
}
|
||||
|
||||
if ( $where ) {
|
||||
$where .= ' ';
|
||||
}
|
||||
|
||||
$meta_query = new \WP_Meta_Query( $this->args['meta_query'] );
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$query_clauses = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
|
||||
|
||||
$join .= $query_clauses['join'];
|
||||
$where .= $query_clauses['where'];
|
||||
}
|
||||
|
||||
// Grab a snapshot of post IDs, just in case it changes during the export.
|
||||
$post_ids = $this->wpdb->get_col( "SELECT ID FROM {$this->wpdb->posts} $join WHERE $where $limit" );// phpcs:ignore
|
||||
$thumbnail_ids = [];
|
||||
|
||||
if ( ! empty( $this->args['include_post_featured_image_as_attachment'] ) ) {
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
$thumbnail_id = get_post_meta( $post_id, '_thumbnail_id', true );
|
||||
|
||||
if ( $thumbnail_id && ! in_array( $thumbnail_id, $post_ids, true ) ) {
|
||||
$thumbnail_ids [] = $thumbnail_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'ids' => $post_ids,
|
||||
'xml' => $this->get_xml_export( array_merge( $post_ids, $thumbnail_ids ) ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return tabulation characters, by `$columns`.
|
||||
*
|
||||
* @param int $columns
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function indent( $columns = 1 ) {
|
||||
$output = '';
|
||||
|
||||
for ( $i = 0; $i < $columns; $i++ ) {
|
||||
$output .= "\t";
|
||||
}
|
||||
|
||||
return (string) $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return wrapped given string in XML CDATA tag.
|
||||
*
|
||||
* @param string $str String to wrap in XML CDATA tag.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_cdata( $str ) {
|
||||
$str = (string) $str;
|
||||
|
||||
if ( ! seems_utf8( $str ) ) {
|
||||
$str = utf8_encode( $str );
|
||||
}
|
||||
|
||||
$str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL of the site.
|
||||
*
|
||||
* @return string Site URL.
|
||||
*/
|
||||
private function wxr_site_url() {
|
||||
if ( is_multisite() ) {
|
||||
// Multisite: the base URL.
|
||||
return network_home_url();
|
||||
} else {
|
||||
// WordPress (single site): the blog URL.
|
||||
return get_bloginfo_rss( 'url' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a cat_name XML tag from a given category object.
|
||||
*
|
||||
* @param \WP_Term $category Category Object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_cat_name( $category ) {
|
||||
if ( empty( $category->name ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->indent( 3 ) . '<wp:cat_name>' . $this->wxr_cdata( $category->name ) . '</wp:cat_name>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a category_description XML tag from a given category object.
|
||||
*
|
||||
* @param \WP_Term $category Category Object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_category_description( $category ) {
|
||||
if ( empty( $category->description ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->indent( 3 ) . '<wp:category_description>' . $this->wxr_cdata( $category->description ) . "</wp:category_description>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tag_name XML tag from a given tag object.
|
||||
*
|
||||
* @param \WP_Term $tag Tag Object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_tag_name( $tag ) {
|
||||
if ( empty( $tag->name ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->indent( 3 ) . '<wp:tag_name>' . $this->wxr_cdata( $tag->name ) . '</wp:tag_name>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tag_description XML tag from a given tag object.
|
||||
*
|
||||
* @param \WP_Term $tag Tag Object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_tag_description( $tag ) {
|
||||
if ( empty( $tag->description ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->indent( 3 ) . '<wp:tag_description>' . $this->wxr_cdata( $tag->description ) . '</wp:tag_description>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a term_name XML tag from a given term object.
|
||||
*
|
||||
* @param \WP_Term $term Term Object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_term_name( $term ) {
|
||||
if ( empty( $term->name ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->indent( 3 ) . '<wp:term_name>' . $this->wxr_cdata( $term->name ) . '</wp:term_name>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a term_description XML tag from a given term object.
|
||||
*
|
||||
* @param \WP_Term $term Term Object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_term_description( $term ) {
|
||||
if ( empty( $term->description ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->indent( 3 ) . '<wp:term_description>' . $this->wxr_cdata( $term->description ) . '</wp:term_description>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return term meta XML tags for a given term object.
|
||||
*
|
||||
* @param \WP_Term $term Term object.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_term_meta( $term ) {
|
||||
$result = '';
|
||||
$termmeta = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->termmeta} WHERE term_id = %d", $term->term_id ) );// phpcs:ignore
|
||||
|
||||
foreach ( $termmeta as $meta ) {
|
||||
/**
|
||||
* Filters whether to selectively skip term meta used for WXR exports.
|
||||
*
|
||||
* Returning a truthy value from the filter will skip the current meta
|
||||
* object from being exported.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param bool $skip Whether to skip the current piece of term meta. Default false.
|
||||
* @param string $meta_key Current meta key.
|
||||
* @param object $meta Current meta object.
|
||||
*/
|
||||
if ( ! apply_filters( 'wxr_export_skip_termmeta', false, $meta->meta_key, $meta ) ) {
|
||||
$result .= sprintf( $this->indent( 3 ) . "<wp:termmeta>\n\t\t\t<wp:meta_key>%s</wp:meta_key>\n\t\t\t<wp:meta_value>%s</wp:meta_value>\n\t\t</wp:termmeta>\n", $this->wxr_cdata( $meta->meta_key ), $this->wxr_cdata( $meta->meta_value ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of authors with posts.
|
||||
*
|
||||
* @param int[] $post_ids Optional. Array of post IDs to filter the query by.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_authors_list( array $post_ids = null ) {
|
||||
$result = '';
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
$post_ids = array_map( 'absint', $post_ids );
|
||||
$and = 'AND ID IN ( ' . implode( ', ', $post_ids ) . ')';
|
||||
} else {
|
||||
$and = '';
|
||||
}
|
||||
|
||||
$authors = [];
|
||||
$results = $this->wpdb->get_results( "SELECT DISTINCT post_author FROM {$this->wpdb->posts} WHERE post_status != 'auto-draft' $and" );// phpcs:ignore
|
||||
foreach ( (array) $results as $r ) {
|
||||
$authors[] = get_userdata( $r->post_author );
|
||||
}
|
||||
|
||||
$authors = array_filter( $authors );
|
||||
|
||||
foreach ( $authors as $author ) {
|
||||
$result .= $this->indent( 2 ) . '<wp:author>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 3 ) . '<wp:author_id>' . (int) $author->ID . '</wp:author_id>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:author_login>' . $this->wxr_cdata( $author->user_login ) . '</wp:author_login>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:author_email>' . $this->wxr_cdata( $author->user_email ) . '</wp:author_email>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:author_display_name>' . $this->wxr_cdata( $author->display_name ) . '</wp:author_display_name>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:author_first_name>' . $this->wxr_cdata( $author->first_name ) . '</wp:author_first_name>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:author_last_name>' . $this->wxr_cdata( $author->last_name ) . '</wp:author_last_name>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 2 ) . '</wp:author>' . PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of categories.
|
||||
*
|
||||
* @param array $cats
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_categories_list( array $cats ) {
|
||||
$result = '';
|
||||
|
||||
foreach ( $cats as $c ) {
|
||||
$result .= $this->indent( 2 ) . '<wp:category>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 3 ) . '<wp:term_id>' . (int) $c->term_id . '</wp:term_id>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:category_nicename>' . $this->wxr_cdata( $c->slug ) . '</wp:category_nicename>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:category_parent>' . $this->wxr_cdata( $c->parent ? $cats[ $c->parent ]->slug : '' ) . '</wp:category_parent>' . PHP_EOL;
|
||||
$result .= $this->wxr_cat_name( $c ) .
|
||||
$this->wxr_category_description( $c ) .
|
||||
$this->wxr_term_meta( $c );
|
||||
|
||||
$result .= $this->indent( 2 ) . '</wp:category>' . PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of tags.
|
||||
*
|
||||
* @param array $tags
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_tags_list( array $tags ) {
|
||||
$result = '';
|
||||
|
||||
foreach ( $tags as $t ) {
|
||||
$result .= $this->indent( 2 ) . '<wp:tag>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 3 ) . '<wp:term_id>' . (int) $t->term_id . '</wp:term_id>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:tag_slug>' . $this->wxr_cdata( $t->slug ) . '</wp:tag_slug>' . PHP_EOL;
|
||||
$result .= $this->wxr_tag_name( $t ) .
|
||||
$this->wxr_tag_description( $t ) .
|
||||
$this->wxr_term_meta( $t );
|
||||
|
||||
$result .= $this->indent( 2 ) . '</wp:tag>' . PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of terms.
|
||||
*
|
||||
* @param array $terms
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_terms_list( array $terms ) {
|
||||
$result = '';
|
||||
|
||||
foreach ( $terms as $t ) {
|
||||
$result .= $this->indent( 2 ) . '<wp:term>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 3 ) . '<wp:term_id>' . $this->wxr_cdata( $t->term_id ) . '</wp:term_id>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:term_taxonomy>' . $this->wxr_cdata( $t->taxonomy ) . '</wp:term_taxonomy>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:term_slug>' . $this->wxr_cdata( $t->slug ) . '</wp:term_slug>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:term_parent>' . $this->wxr_cdata( $t->parent ? $terms[ $t->parent ]->slug : '' ) . '</wp:term_parent>' . PHP_EOL;
|
||||
$result .= $this->wxr_term_name( $t ) .
|
||||
$this->wxr_term_description( $t ) .
|
||||
$this->wxr_term_meta( $t );
|
||||
|
||||
$result .= $this->indent( 2 ) . '</wp:term>' . PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of posts, by requested `$post_ids`.
|
||||
*
|
||||
* @param array $post_ids
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_posts_list( array $post_ids ) {
|
||||
$result = '';
|
||||
|
||||
if ( $post_ids ) {
|
||||
global $wp_query;
|
||||
|
||||
// Fake being in the loop.
|
||||
$wp_query->in_the_loop = true;
|
||||
|
||||
// Fetch 20 posts at a time rather than loading the entire table into memory.
|
||||
while ( $next_posts = array_splice( $post_ids, 0, 20 ) ) {
|
||||
$where = 'WHERE ID IN (' . implode( ',', $next_posts ) . ')';
|
||||
$posts = $this->wpdb->get_results( "SELECT * FROM {$this->wpdb->posts} $where" );// phpcs:ignore
|
||||
|
||||
// Begin Loop.
|
||||
foreach ( $posts as $post ) {
|
||||
setup_postdata( $post );
|
||||
|
||||
$title = apply_filters( 'the_title_rss', $post->post_title );
|
||||
|
||||
/**
|
||||
* Filters the post content used for WXR exports.
|
||||
*
|
||||
* @since 2.5.0
|
||||
*
|
||||
* @param string $post_content Content of the current post.
|
||||
*/
|
||||
$content = $this->wxr_cdata( apply_filters( 'the_content_export', $post->post_content ) );
|
||||
|
||||
/**
|
||||
* Filters the post excerpt used for WXR exports.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*
|
||||
* @param string $post_excerpt Excerpt for the current post.
|
||||
*/
|
||||
$excerpt = $this->wxr_cdata( apply_filters( 'the_excerpt_export', $post->post_excerpt ) );
|
||||
|
||||
$result .= $this->indent( 2 ) . '<item>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 3 ) . '<title>' . $title . '</title>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<link>' . esc_url( get_permalink() ) . '</link>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<pubDate>' . mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ) . '</pubDate>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<dc:creator>' . $this->wxr_cdata( get_the_author_meta( 'login' ) ) . '</dc:creator>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<guid isPermaLink="false">' . $this->wxr_cdata( get_the_author_meta( 'login' ) ) . '</guid>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<description></description>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<content:encoded>' . $content . '</content:encoded>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<excerpt:encoded>' . $excerpt . '</excerpt:encoded>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:post_id>' . (int) $post->ID . '</wp:post_id>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:post_date>' . $this->wxr_cdata( $post->post_date ) . '</wp:post_date>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:post_date_gmt>' . $this->wxr_cdata( $post->post_date_gmt ) . '</wp:post_date_gmt>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:comment_status>' . $this->wxr_cdata( $post->comment_status ) . '</wp:comment_status>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:ping_status>' . $this->wxr_cdata( $post->ping_status ) . '</wp:ping_status>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:post_name>' . $this->wxr_cdata( $post->post_name ) . '</wp:post_name>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:status>' . $this->wxr_cdata( $post->post_status ) . '</wp:status>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:post_parent>' . $this->wxr_cdata( $post->post_parent ) . '</wp:post_parent>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:menu_order>' . (int) $post->menu_order . '</wp:menu_order>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:post_type>' . $this->wxr_cdata( $post->post_type ) . '</wp:post_type>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:post_password>' . $this->wxr_cdata( $post->post_password ) . '</wp:post_password>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:is_sticky>' . ( is_sticky( $post->ID ) ? 1 : 0 ) . '</wp:is_sticky>' . PHP_EOL;
|
||||
|
||||
if ( 'attachment' === $post->post_type ) {
|
||||
$result .= $this->indent( 3 ) . '<wp:attachment_url>' . $this->wxr_cdata( wp_get_attachment_url( $post->ID ) ) . '</wp:attachment_url>' . PHP_EOL;
|
||||
}
|
||||
|
||||
$result .= $this->wxr_post_taxonomy( $post );
|
||||
|
||||
$postmeta = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->postmeta} WHERE post_id = %d", $post->ID ) );// phpcs:ignore
|
||||
foreach ( $postmeta as $meta ) {
|
||||
/**
|
||||
* Filters whether to selectively skip post meta used for WXR exports.
|
||||
*
|
||||
* Returning a truthy value from the filter will skip the current meta
|
||||
* object from being exported.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param bool $skip Whether to skip the current post meta. Default false.
|
||||
* @param string $meta_key Current meta key.
|
||||
* @param object $meta Current meta object.
|
||||
*/
|
||||
if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result .= $this->indent( 3 ) . '<wp:postmeta>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 4 ) . '<wp:meta_key>' . $this->wxr_cdata( $meta->meta_key ) . '</wp:meta_key>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:meta_value>' . $this->wxr_cdata( $meta->meta_value ) . '</wp:meta_value>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 3 ) . '</wp:postmeta>' . PHP_EOL;
|
||||
}
|
||||
|
||||
$_comments = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->comments} WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) );// phpcs:ignore
|
||||
$comments = array_map( 'get_comment', $_comments );
|
||||
foreach ( $comments as $c ) {
|
||||
|
||||
$result .= $this->indent( 3 ) . '<wp:comment>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_id>' . (int) $c->comment_ID . '</wp:comment_id>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_author>' . $this->wxr_cdata( $c->comment_author ) . '</wp:comment_author>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_author_email>' . $this->wxr_cdata( $c->comment_author_email ) . '</wp:comment_author_email>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_author_url>' . $this->wxr_cdata( $c->comment_author_url ) . '</wp:comment_author_url>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_author_IP>' . $this->wxr_cdata( $c->comment_author_IP ) . '</wp:comment_author_IP>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_date>' . $this->wxr_cdata( $c->comment_date ) . '</wp:comment_date>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_date_gmt>' . $this->wxr_cdata( $c->comment_date_gmt ) . '</wp:comment_date_gmt>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_content>' . $this->wxr_cdata( $c->comment_content ) . '</wp:comment_content>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_approved>' . $this->wxr_cdata( $c->comment_approved ) . '</wp:comment_approved>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_type>' . $this->wxr_cdata( $c->comment_type ) . '</wp:comment_type>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_parent>' . $this->wxr_cdata( $c->comment_parent ) . '</wp:comment_parent>' . PHP_EOL;
|
||||
$result .= $this->indent( 4 ) . '<wp:comment_user_id>' . (int) $c->user_id . '</wp:comment_user_id>' . PHP_EOL;
|
||||
|
||||
$c_meta = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->commentmeta} WHERE comment_id = %d", $c->comment_ID ) );// phpcs:ignore
|
||||
foreach ( $c_meta as $meta ) {
|
||||
/**
|
||||
* Filters whether to selectively skip comment meta used for WXR exports.
|
||||
*
|
||||
* Returning a truthy value from the filter will skip the current meta
|
||||
* object from being exported.
|
||||
*
|
||||
* @since 4.0.0
|
||||
*
|
||||
* @param bool $skip Whether to skip the current comment meta. Default false.
|
||||
* @param string $meta_key Current meta key.
|
||||
* @param object $meta Current meta object.
|
||||
*/
|
||||
if ( apply_filters( 'wxr_export_skip_commentmeta', false, $meta->meta_key, $meta ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result .= $this->indent( 4 ) . '<wp:commentmeta>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 5 ) . '<wp:meta_key>' . $this->wxr_cdata( $meta->meta_key ) . '</wp:meta_key>' . PHP_EOL;
|
||||
$result .= $this->indent( 5 ) . '<wp:meta_value>' . $this->wxr_cdata( $meta->meta_key ) . '</wp:meta_value>' . PHP_EOL;
|
||||
|
||||
$result .= $this->indent( 4 ) . '</wp:commentmeta>' . PHP_EOL;
|
||||
}
|
||||
|
||||
$result .= $this->indent( 3 ) . '</wp:comment>' . PHP_EOL;
|
||||
}
|
||||
|
||||
$result .= $this->indent( 2 ) . '</item>' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all navigation menu terms
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_nav_menu_terms() {
|
||||
$nav_menus = wp_get_nav_menus();
|
||||
if ( empty( $nav_menus ) || ! is_array( $nav_menus ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
foreach ( $nav_menus as $menu ) {
|
||||
$this->terms[ $menu->term_id ] = $menu;
|
||||
|
||||
$result .= $this->indent( 2 ) . '<wp:term>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:term_id>' . (int) $menu->term_id . '</wp:term_id>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:term_taxonomy>nav_menu</wp:term_taxonomy>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:term_slug>' . $this->wxr_cdata( $menu->slug ) . '</wp:term_slug>' . PHP_EOL;
|
||||
$result .= $this->indent( 3 ) . '<wp:term_name>' . $this->wxr_cdata( $menu->name ) . '</wp:term_name>' . PHP_EOL;
|
||||
$result .= $this->indent( 2 ) . '</wp:term>' . PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of taxonomy terms, in XML tag format, associated with a post
|
||||
*
|
||||
* @param \WP_Post $post
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function wxr_post_taxonomy( $post ) {
|
||||
$result = '';
|
||||
|
||||
$taxonomies = get_object_taxonomies( $post->post_type );
|
||||
|
||||
if ( empty( $taxonomies ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$terms = wp_get_object_terms( $post->ID, $taxonomies );
|
||||
|
||||
foreach ( (array) $terms as $term ) {
|
||||
$result .= $this->indent( 3 ) . "<category domain=\"{$term->taxonomy}\" nicename=\"{$term->slug}\">" . $this->wxr_cdata( $term->name ) . '</category>' . PHP_EOL;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the XML export.
|
||||
*
|
||||
* @param $post_ids
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_xml_export( array $post_ids ) {
|
||||
$charset = get_bloginfo( 'charset' );
|
||||
$generator = get_the_generator( 'export' );
|
||||
$wxr_version = self::WXR_VERSION;
|
||||
$wxr_site_url = $this->wxr_site_url();
|
||||
$rss_info_name = get_bloginfo_rss( 'name' );
|
||||
$rss_info_url = get_bloginfo_rss( 'url' );
|
||||
$rss_info_description = get_bloginfo_rss( 'description' );
|
||||
$rss_info_language = get_bloginfo_rss( 'language' );
|
||||
$pub_date = gmdate( 'D, d M Y H:i:s +0000' );
|
||||
|
||||
$show_page_on_front = 'page' === get_option( 'show_on_front' );
|
||||
|
||||
$page_on_front_xml = '';
|
||||
|
||||
if ( $show_page_on_front ) {
|
||||
$page_on_front_id = (int) get_option( 'page_on_front' );
|
||||
|
||||
if ( in_array( $page_on_front_id, $post_ids ) ) {
|
||||
$page_on_front_xml = "<wp:page_on_front>$page_on_front_id</wp:page_on_front>";
|
||||
}
|
||||
}
|
||||
|
||||
$dynamic = $this->wxr_authors_list( $post_ids );
|
||||
|
||||
ob_start();
|
||||
/** This action is documented in wp-includes/feed-rss2.php */
|
||||
do_action( 'rss2_head' );
|
||||
$rss2_head = ob_get_clean();
|
||||
|
||||
$dynamic .= $rss2_head;
|
||||
|
||||
if ( 'all' === $this->args['content'] || 'nav_menu_item' === $this->args['content'] ) {
|
||||
$dynamic .= $this->wxr_nav_menu_terms();
|
||||
}
|
||||
|
||||
$dynamic .= $this->wxr_posts_list( $post_ids );
|
||||
|
||||
$result = <<<EOT
|
||||
<?xml version="1.0" encoding="$charset" ?>
|
||||
<!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. -->
|
||||
<!-- It contains information about your site's posts, pages, comments, categories, and other content. -->
|
||||
<!-- You may use this file to transfer that content from one site to another. -->
|
||||
<!-- This file is not intended to serve as a complete backup of your site. -->
|
||||
|
||||
<!-- To import this information into a WordPress site follow these steps: -->
|
||||
<!-- 1. Log in to that site as an administrator. -->
|
||||
<!-- 2. Go to Tools: Import in the WordPress admin panel. -->
|
||||
<!-- 3. Install the "WordPress" importer from the list. -->
|
||||
<!-- 4. Activate & Run Importer. -->
|
||||
<!-- 5. Upload this file using the form provided on that page. -->
|
||||
<!-- 6. You will first be asked to map the authors in this export file to users -->
|
||||
<!-- on the site. For each author, you may choose to map to an -->
|
||||
<!-- existing user on the site or to create a new user. -->
|
||||
<!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. -->
|
||||
<!-- contained in this file into your site. -->
|
||||
$generator
|
||||
<rss version="2.0"
|
||||
xmlns:excerpt="http://wordpress.org/export/$wxr_version/excerpt/"
|
||||
xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
||||
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:wp="http://wordpress.org/export/$wxr_version/"
|
||||
>
|
||||
<channel>
|
||||
<title>$rss_info_name</title>
|
||||
<link>$rss_info_url</link>
|
||||
<description>$rss_info_description</description>
|
||||
<pubDate>$pub_date</pubDate>
|
||||
<language>$rss_info_language</language>
|
||||
<wp:wxr_version>$wxr_version</wp:wxr_version>
|
||||
<wp:base_site_url>$wxr_site_url</wp:base_site_url>
|
||||
<wp:base_blog_url>$rss_info_url</wp:base_blog_url>
|
||||
$page_on_front_xml
|
||||
$dynamic
|
||||
</channel>
|
||||
</rss>
|
||||
EOT;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __construct( array $args = [] ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->args = wp_parse_args( $args, self::$default_args );
|
||||
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
}
|
||||
1355
wp-content/plugins/elementor/core/utils/import-export/wp-import.php
Normal file
1355
wp-content/plugins/elementor/core/utils/import-export/wp-import.php
Normal file
File diff suppressed because it is too large
Load Diff
137
wp-content/plugins/elementor/core/utils/plugins-manager.php
Normal file
137
wp-content/plugins/elementor/core/utils/plugins-manager.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
|
||||
|
||||
use Elementor\Plugin;
|
||||
use Plugin_Upgrader;
|
||||
use WP_Ajax_Upgrader_Skin;
|
||||
|
||||
class Plugins_Manager {
|
||||
|
||||
/**
|
||||
* @var Plugin_Upgrader
|
||||
*/
|
||||
private $upgrader;
|
||||
|
||||
public function __construct( $upgrader = null ) {
|
||||
|
||||
// For tests
|
||||
if ( $upgrader ) {
|
||||
$this->upgrader = $upgrader;
|
||||
} else {
|
||||
$skin = new WP_Ajax_Upgrader_Skin();
|
||||
$this->upgrader = new Plugin_Upgrader( $skin );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install plugin or an array of plugins.
|
||||
*
|
||||
* @since 3.6.2
|
||||
*
|
||||
* @param string|array $plugins
|
||||
* @return array [ 'succeeded' => [] , 'failed' => [] ]
|
||||
*/
|
||||
public function install( $plugins ) {
|
||||
$succeeded = [];
|
||||
$failed = [];
|
||||
$already_installed_plugins = Plugin::$instance->wp->get_plugins();
|
||||
|
||||
if ( ! is_array( $plugins ) ) {
|
||||
$plugins = [ $plugins ];
|
||||
}
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( in_array( $plugin, $already_installed_plugins->keys(), true ) ) {
|
||||
$succeeded[] = $plugin;
|
||||
continue;
|
||||
}
|
||||
|
||||
$slug = $this->clean_slug( $plugin );
|
||||
|
||||
$api = Plugin::$instance->wp->plugins_api('plugin_information',
|
||||
[
|
||||
'slug' => $slug,
|
||||
'fields' => array(
|
||||
'short_description' => false,
|
||||
'sections' => false,
|
||||
'requires' => false,
|
||||
'rating' => false,
|
||||
'ratings' => false,
|
||||
'downloaded' => false,
|
||||
'last_updated' => false,
|
||||
'added' => false,
|
||||
'tags' => false,
|
||||
'compatibility' => false,
|
||||
'homepage' => false,
|
||||
'donate_link' => false,
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
if ( ! isset( $api->download_link ) ) {
|
||||
$failed[] = $plugin;
|
||||
continue;
|
||||
}
|
||||
|
||||
$installation = $this->upgrader->install( $api->download_link );
|
||||
|
||||
if ( $installation ) {
|
||||
$succeeded[] = $plugin;
|
||||
} else {
|
||||
$failed[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'succeeded' => $succeeded,
|
||||
'failed' => $failed,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate plugin or array off plugins.
|
||||
*
|
||||
* @since 3.6.2
|
||||
*
|
||||
* @param array|string $plugins
|
||||
* @return array [ 'succeeded' => [] , 'failed' => [] ]
|
||||
*/
|
||||
public function activate( $plugins ) {
|
||||
$succeeded = [];
|
||||
$failed = [];
|
||||
|
||||
if ( ! is_array( $plugins ) ) {
|
||||
$plugins = [ $plugins ];
|
||||
}
|
||||
|
||||
foreach ( $plugins as $plugin ) {
|
||||
if ( Plugin::$instance->wp->is_plugin_active( $plugin ) ) {
|
||||
$succeeded[] = $plugin;
|
||||
continue;
|
||||
}
|
||||
|
||||
Plugin::$instance->wp->activate_plugin( $plugin );
|
||||
|
||||
if ( Plugin::$instance->wp->is_plugin_active( $plugin ) ) {
|
||||
$succeeded[] = $plugin;
|
||||
} else {
|
||||
$failed[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'succeeded' => $succeeded,
|
||||
'failed' => $failed,
|
||||
];
|
||||
}
|
||||
|
||||
private function clean_slug( $initial_slug ) {
|
||||
return explode( '/', $initial_slug )[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Elementor\Core\Utils\Promotions;
|
||||
|
||||
use function DI\string;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Filtered_Promotions_Manager {
|
||||
|
||||
/**
|
||||
* @param array $promotion_data
|
||||
* @param string $filter_name
|
||||
* @param string $url_key
|
||||
* @return array
|
||||
*/
|
||||
public static function get_filtered_promotion_data( array $promotion_data, string $filter_name, string $url_key, string $url_sub_key = '' ): array {
|
||||
$new_promotion_data = apply_filters( $filter_name, $promotion_data );
|
||||
|
||||
if ( ! is_array( $new_promotion_data ) ) {
|
||||
return $promotion_data;
|
||||
}
|
||||
|
||||
$new_promotion_data = self::retain_original_keys( $new_promotion_data, $promotion_data );
|
||||
|
||||
$new_promotion_data = self::filter_invalid_url( $new_promotion_data, $url_key, $url_sub_key );
|
||||
|
||||
return array_replace( $promotion_data, $new_promotion_data );
|
||||
}
|
||||
|
||||
private static function domain_is_on_elementor_dot_com( $url ): bool {
|
||||
$domain = wp_parse_url( $url, PHP_URL_HOST );
|
||||
|
||||
return isset( $domain ) && str_contains( $domain, 'elementor.com' );
|
||||
}
|
||||
|
||||
private static function filter_invalid_url( $new_promotion_data, string $url_key, string $url_sub_key ) {
|
||||
if ( ! isset( $new_promotion_data[ $url_key ] ) ) {
|
||||
return $new_promotion_data;
|
||||
}
|
||||
|
||||
if ( empty( $url_sub_key ) ) {
|
||||
$new_promotion_data = self::filter_invalid_url_in_flat_array( $new_promotion_data, $url_key );
|
||||
} else {
|
||||
$new_promotion_data[ $url_key ] = self::filter_invalid_url_in_flat_array( $new_promotion_data[ $url_key ], $url_sub_key );
|
||||
}
|
||||
|
||||
return $new_promotion_data;
|
||||
}
|
||||
|
||||
private static function filter_invalid_url_in_flat_array( array $new_promotion_data, string $url_key ): array {
|
||||
if ( ! self::domain_is_on_elementor_dot_com( $new_promotion_data[ $url_key ] ) ) {
|
||||
unset( $new_promotion_data[ $url_key ] );
|
||||
} else {
|
||||
$new_promotion_data[ $url_key ] = esc_url( $new_promotion_data[ $url_key ] );
|
||||
}
|
||||
|
||||
return $new_promotion_data;
|
||||
}
|
||||
|
||||
private static function retain_original_keys( array $new_promotion_data, array $promotion_data ): array {
|
||||
return array_intersect_key( $new_promotion_data, array_flip( array_keys( $promotion_data ) ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Static_Collection {
|
||||
|
||||
/**
|
||||
* The current Collection instance.
|
||||
*
|
||||
* @var Collection
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/**
|
||||
* Return only unique values.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $unique_values = false;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __construct( array $items = [], $unique_values = false ) {
|
||||
$this->collection = new Collection( $items );
|
||||
$this->unique_values = $unique_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since this class is a wrapper, every call will be forwarded to wrapped class.
|
||||
* Most of the collection methods returns a new collection instance, and therefore
|
||||
* it will be assigned as the current collection instance after executing any method.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*/
|
||||
public function __call( $name, $arguments ) {
|
||||
$call = call_user_func_array( [ $this->collection, $name ], $arguments );
|
||||
|
||||
if ( $call instanceof Collection ) {
|
||||
$this->collection = $this->unique_values ?
|
||||
$call->unique() :
|
||||
$call;
|
||||
}
|
||||
|
||||
return $call;
|
||||
}
|
||||
}
|
||||
41
wp-content/plugins/elementor/core/utils/str.php
Normal file
41
wp-content/plugins/elementor/core/utils/str.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class Str {
|
||||
|
||||
/**
|
||||
* Convert a non-latin URL to an IDN one.
|
||||
* Note: Max length is 64 chars.
|
||||
*
|
||||
* @param string $url - A URL to encode.
|
||||
*
|
||||
* @return string - IDN encoded URL ( e.g. `http://é.com` will be encoded to `http://xn--9ca.com` ).
|
||||
*/
|
||||
public static function encode_idn_url( $url ) {
|
||||
return preg_replace_callback( '/(https?:\/\/)(.+)/', function ( $matches ) {
|
||||
// WP >= 6.2-alpha
|
||||
if ( class_exists( '\WpOrg\Requests\IdnaEncoder' ) ) {
|
||||
$class = \WpOrg\Requests\IdnaEncoder::class;
|
||||
} else {
|
||||
$class = \Requests_IDNAEncoder::class;
|
||||
}
|
||||
|
||||
return $matches[1] . $class::encode( $matches[2] );
|
||||
}, $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string ends with a given substring
|
||||
*
|
||||
* @param $haystack
|
||||
* @param $needle
|
||||
* @return bool
|
||||
*/
|
||||
public static function ends_with( $haystack, $needle ) {
|
||||
return substr( $haystack, -strlen( $needle ) ) === $needle;
|
||||
}
|
||||
}
|
||||
661
wp-content/plugins/elementor/core/utils/svg/svg-sanitizer.php
Normal file
661
wp-content/plugins/elementor/core/utils/svg/svg-sanitizer.php
Normal file
@@ -0,0 +1,661 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils\Svg;
|
||||
|
||||
use Elementor\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor SVG Sanitizer.
|
||||
*
|
||||
* A class that is responsible for sanitizing SVG files.
|
||||
*
|
||||
* @since 3.16.0
|
||||
*/
|
||||
class Svg_Sanitizer {
|
||||
|
||||
/**
|
||||
* @var \DOMDocument
|
||||
*/
|
||||
private $svg_dom = null;
|
||||
|
||||
/**
|
||||
* Sanitize File
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access public
|
||||
*
|
||||
* @param $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function sanitize_file( $filename ) {
|
||||
$original_content = Utils::file_get_contents( $filename );
|
||||
$is_encoded = $this->is_encoded( $original_content );
|
||||
|
||||
if ( $is_encoded ) {
|
||||
$decoded = $this->decode_svg( $original_content );
|
||||
if ( false === $decoded ) {
|
||||
return false;
|
||||
}
|
||||
$original_content = $decoded;
|
||||
}
|
||||
|
||||
$valid_svg = $this->sanitize( $original_content );
|
||||
|
||||
if ( false === $valid_svg ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we were gzipped, we need to re-zip
|
||||
if ( $is_encoded ) {
|
||||
$valid_svg = $this->encode_svg( $valid_svg );
|
||||
}
|
||||
file_put_contents( $filename, $valid_svg );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access public
|
||||
*
|
||||
* @param $content
|
||||
* @return bool|string
|
||||
*/
|
||||
public function sanitize( $content ) {
|
||||
// Strip php tags
|
||||
$content = $this->strip_comments( $content );
|
||||
$content = $this->strip_php_tags( $content );
|
||||
$content = $this->strip_line_breaks( $content );
|
||||
|
||||
// Find the start and end tags so we can cut out miscellaneous garbage.
|
||||
$start = strpos( $content, '<svg' );
|
||||
$end = strrpos( $content, '</svg>' );
|
||||
if ( false === $start || false === $end ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = substr( $content, $start, ( $end - $start + 6 ) );
|
||||
|
||||
// If the server's PHP version is 8 or up, make sure to Disable the ability to load external entities
|
||||
$php_version_under_eight = version_compare( PHP_VERSION, '8.0.0', '<' );
|
||||
if ( $php_version_under_eight ) {
|
||||
$libxml_disable_entity_loader = libxml_disable_entity_loader( true ); // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated
|
||||
}
|
||||
// Suppress the errors
|
||||
$libxml_use_internal_errors = libxml_use_internal_errors( true );
|
||||
|
||||
// Create DomDocument instance
|
||||
$this->svg_dom = new \DOMDocument();
|
||||
$this->svg_dom->formatOutput = false;
|
||||
$this->svg_dom->preserveWhiteSpace = false;
|
||||
$this->svg_dom->strictErrorChecking = false;
|
||||
|
||||
$open_svg = $this->svg_dom->loadXML( $content );
|
||||
if ( ! $open_svg ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->strip_doctype();
|
||||
$this->sanitize_elements();
|
||||
|
||||
// Export sanitized svg to string
|
||||
// Using documentElement to strip out <?xml version="1.0" encoding="UTF-8"...
|
||||
$sanitized = $this->svg_dom->saveXML( $this->svg_dom->documentElement, LIBXML_NOEMPTYTAG );
|
||||
|
||||
// Restore defaults
|
||||
if ( $php_version_under_eight ) {
|
||||
libxml_disable_entity_loader( $libxml_disable_entity_loader ); // phpcs:ignore Generic.PHP.DeprecatedFunctions.Deprecated
|
||||
}
|
||||
libxml_use_internal_errors( $libxml_use_internal_errors );
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Encoded
|
||||
*
|
||||
* Check if the contents of the SVG file are gzipped
|
||||
* @see http://www.gzip.org/zlib/rfc-gzip.html#member-format
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $contents
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_encoded( $contents ) {
|
||||
$needle = "\x1f\x8b\x08";
|
||||
if ( function_exists( 'mb_strpos' ) ) {
|
||||
return 0 === mb_strpos( $contents, $needle );
|
||||
} else {
|
||||
return 0 === strpos( $contents, $needle );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode SVG
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $content
|
||||
* @return string
|
||||
*/
|
||||
private function encode_svg( $content ) {
|
||||
return gzencode( $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode SVG
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function decode_svg( $content ) {
|
||||
return gzdecode( $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Allowed Tag
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $element
|
||||
* @return bool
|
||||
*/
|
||||
private function is_allowed_tag( $element ) {
|
||||
static $allowed_tags = false;
|
||||
if ( false === $allowed_tags ) {
|
||||
$allowed_tags = $this->get_allowed_elements();
|
||||
}
|
||||
|
||||
$tag_name = $element->tagName; // phpcs:ignore -- php DomDocument
|
||||
|
||||
if ( ! in_array( strtolower( $tag_name ), $allowed_tags ) ) {
|
||||
$this->remove_element( $element );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Element
|
||||
*
|
||||
* Removes the passed element from its DomDocument tree
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $element
|
||||
*/
|
||||
private function remove_element( $element ) {
|
||||
$element->parentNode->removeChild( $element ); // phpcs:ignore -- php DomDocument
|
||||
}
|
||||
|
||||
/**
|
||||
* Is It An Attribute
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $name
|
||||
* @param $check
|
||||
* @return bool
|
||||
*/
|
||||
private function is_a_attribute( $name, $check ) {
|
||||
return 0 === strpos( $name, $check . '-' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Remote Value
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $value
|
||||
* @return string
|
||||
*/
|
||||
private function is_remote_value( $value ) {
|
||||
$value = trim( preg_replace( '/[^ -~]/xu', '', $value ) );
|
||||
$wrapped_in_url = preg_match( '~^url\(\s*[\'"]\s*(.*)\s*[\'"]\s*\)$~xi', $value, $match );
|
||||
if ( ! $wrapped_in_url ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = trim( $match[1], '\'"' );
|
||||
return preg_match( '~^((https?|ftp|file):)?//~xi', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Has JS Value
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $value
|
||||
* @return false|int
|
||||
*/
|
||||
private function has_js_value( $value ) {
|
||||
return preg_match( '/base64|data|(?:java)?script|alert\(|window\.|document/i', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Allowed Attributes
|
||||
*
|
||||
* Returns an array of allowed tag attributes in SVG files.
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_allowed_attributes() {
|
||||
$allowed_attributes = [
|
||||
'class',
|
||||
'clip-path',
|
||||
'clip-rule',
|
||||
'fill',
|
||||
'fill-opacity',
|
||||
'fill-rule',
|
||||
'filter',
|
||||
'id',
|
||||
'mask',
|
||||
'opacity',
|
||||
'stroke',
|
||||
'stroke-dasharray',
|
||||
'stroke-dashoffset',
|
||||
'stroke-linecap',
|
||||
'stroke-linejoin',
|
||||
'stroke-miterlimit',
|
||||
'stroke-opacity',
|
||||
'stroke-width',
|
||||
'style',
|
||||
'systemlanguage',
|
||||
'transform',
|
||||
'href',
|
||||
'xlink:href',
|
||||
'xlink:title',
|
||||
'cx',
|
||||
'cy',
|
||||
'r',
|
||||
'requiredfeatures',
|
||||
'clippathunits',
|
||||
'type',
|
||||
'rx',
|
||||
'ry',
|
||||
'color-interpolation-filters',
|
||||
'stddeviation',
|
||||
'filterres',
|
||||
'filterunits',
|
||||
'height',
|
||||
'primitiveunits',
|
||||
'width',
|
||||
'x',
|
||||
'y',
|
||||
'font-size',
|
||||
'display',
|
||||
'font-family',
|
||||
'font-style',
|
||||
'font-weight',
|
||||
'text-anchor',
|
||||
'marker-end',
|
||||
'marker-mid',
|
||||
'marker-start',
|
||||
'x1',
|
||||
'x2',
|
||||
'y1',
|
||||
'y2',
|
||||
'gradienttransform',
|
||||
'gradientunits',
|
||||
'spreadmethod',
|
||||
'markerheight',
|
||||
'markerunits',
|
||||
'markerwidth',
|
||||
'orient',
|
||||
'preserveaspectratio',
|
||||
'refx',
|
||||
'refy',
|
||||
'viewbox',
|
||||
'maskcontentunits',
|
||||
'maskunits',
|
||||
'd',
|
||||
'patterncontentunits',
|
||||
'patterntransform',
|
||||
'patternunits',
|
||||
'points',
|
||||
'fx',
|
||||
'fy',
|
||||
'offset',
|
||||
'stop-color',
|
||||
'stop-opacity',
|
||||
'xmlns',
|
||||
'xmlns:se',
|
||||
'xmlns:xlink',
|
||||
'xml:space',
|
||||
'method',
|
||||
'spacing',
|
||||
'startoffset',
|
||||
'dx',
|
||||
'dy',
|
||||
'rotate',
|
||||
'textlength',
|
||||
];
|
||||
|
||||
/**
|
||||
* Allowed attributes in SVG file.
|
||||
*
|
||||
* Filters the list of allowed attributes in SVG files.
|
||||
*
|
||||
* Since SVG files can run JS code that may inject malicious code, all attributes
|
||||
* are removed except the allowed attributes.
|
||||
*
|
||||
* This hook can be used to manage allowed SVG attributes. To either add new
|
||||
* attributes or delete existing attributes. To strengthen or weaken site security.
|
||||
*
|
||||
* @param array $allowed_attributes A list of allowed attributes.
|
||||
*/
|
||||
$allowed_attributes = apply_filters( 'elementor/files/svg/allowed_attributes', $allowed_attributes );
|
||||
|
||||
return $allowed_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Allowed Elements
|
||||
*
|
||||
* Returns an array of allowed element tags to be in SVG files.
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_allowed_elements() {
|
||||
$allowed_elements = [
|
||||
'a',
|
||||
'circle',
|
||||
'clippath',
|
||||
'defs',
|
||||
'style',
|
||||
'desc',
|
||||
'ellipse',
|
||||
'fegaussianblur',
|
||||
'filter',
|
||||
'foreignobject',
|
||||
'g',
|
||||
'image',
|
||||
'line',
|
||||
'lineargradient',
|
||||
'marker',
|
||||
'mask',
|
||||
'metadata',
|
||||
'path',
|
||||
'pattern',
|
||||
'polygon',
|
||||
'polyline',
|
||||
'radialgradient',
|
||||
'rect',
|
||||
'stop',
|
||||
'svg',
|
||||
'switch',
|
||||
'symbol',
|
||||
'text',
|
||||
'textpath',
|
||||
'title',
|
||||
'tspan',
|
||||
'use',
|
||||
];
|
||||
|
||||
/**
|
||||
* Allowed elements in SVG file.
|
||||
*
|
||||
* Filters the list of allowed elements in SVG files.
|
||||
*
|
||||
* Since SVG files can run JS code that may inject malicious code, all elements
|
||||
* are removed except the allowed elements.
|
||||
*
|
||||
* This hook can be used to manage SVG elements. To either add new elements or
|
||||
* delete existing elements. To strengthen or weaken site security.
|
||||
*
|
||||
* @param array $allowed_elements A list of allowed elements.
|
||||
*/
|
||||
$allowed_elements = apply_filters( 'elementor/files/svg/allowed_elements', $allowed_elements );
|
||||
|
||||
return $allowed_elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Allowed Attributes
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
*/
|
||||
private function validate_allowed_attributes( $element ) {
|
||||
static $allowed_attributes = false;
|
||||
if ( false === $allowed_attributes ) {
|
||||
$allowed_attributes = $this->get_allowed_attributes();
|
||||
}
|
||||
|
||||
for ( $index = $element->attributes->length - 1; $index >= 0; $index-- ) {
|
||||
// get attribute name
|
||||
$attr_name = $element->attributes->item( $index )->name;
|
||||
$attr_name_lowercase = strtolower( $attr_name );
|
||||
// Remove attribute if not in whitelist
|
||||
if ( ! in_array( $attr_name_lowercase, $allowed_attributes ) && ! $this->is_a_attribute( $attr_name_lowercase, 'aria' ) && ! $this->is_a_attribute( $attr_name_lowercase, 'data' ) ) {
|
||||
$element->removeAttribute( $attr_name );
|
||||
continue;
|
||||
}
|
||||
|
||||
$attr_value = $element->attributes->item( $index )->value;
|
||||
|
||||
// Remove attribute if it has a remote reference or js or data-URI/base64
|
||||
if ( ! empty( $attr_value ) && ( $this->is_remote_value( $attr_value ) || $this->has_js_value( $attr_value ) ) ) {
|
||||
$element->removeAttribute( $attr_name );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip xlinks
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
*/
|
||||
private function strip_xlinks( $element ) {
|
||||
$xlinks = $element->getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
|
||||
|
||||
if ( ! $xlinks ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->is_safe_href( $xlinks ) ) {
|
||||
$element->removeAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/darylldoyle/svg-sanitizer/blob/2321a914e/src/Sanitizer.php#L454
|
||||
*/
|
||||
private function is_safe_href( $value ) {
|
||||
// Allow empty values.
|
||||
if ( empty( $value ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow fragment identifiers.
|
||||
if ( '#' === substr( $value, 0, 1 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow relative URIs.
|
||||
if ( '/' === substr( $value, 0, 1 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow HTTPS domains.
|
||||
if ( 'https://' === substr( $value, 0, 8 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow HTTP domains.
|
||||
if ( 'http://' === substr( $value, 0, 7 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow known data URIs.
|
||||
if ( in_array( substr( $value, 0, 14 ), [
|
||||
'data:image/png', // PNG
|
||||
'data:image/gif', // GIF
|
||||
'data:image/jpg', // JPG
|
||||
'data:image/jpe', // JPEG
|
||||
'data:image/pjp', // PJPEG
|
||||
], true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow known short data URIs.
|
||||
if ( in_array( substr( $value, 0, 12 ), [
|
||||
'data:img/png', // PNG
|
||||
'data:img/gif', // GIF
|
||||
'data:img/jpg', // JPG
|
||||
'data:img/jpe', // JPEG
|
||||
'data:img/pjp', // PJPEG
|
||||
], true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate Use Tag
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $element
|
||||
*/
|
||||
private function validate_use_tag( $element ) {
|
||||
$xlinks = $element->getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
|
||||
if ( $xlinks && '#' !== substr( $xlinks, 0, 1 ) ) {
|
||||
$element->parentNode->removeChild( $element ); // phpcs:ignore -- php DomNode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip Doctype
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
*/
|
||||
private function strip_doctype() {
|
||||
foreach ( $this->svg_dom->childNodes as $child ) {
|
||||
if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) { // phpcs:ignore -- php DomDocument
|
||||
$child->parentNode->removeChild( $child ); // phpcs:ignore -- php DomDocument
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize Elements
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*/
|
||||
private function sanitize_elements() {
|
||||
$elements = $this->svg_dom->getElementsByTagName( '*' );
|
||||
// loop through all elements
|
||||
// we do this backwards so we don't skip anything if we delete a node
|
||||
// see comments at: http://php.net/manual/en/class.domnamednodemap.php
|
||||
for ( $index = $elements->length - 1; $index >= 0; $index-- ) {
|
||||
/**
|
||||
* @var \DOMElement $current_element
|
||||
*/
|
||||
$current_element = $elements->item( $index );
|
||||
// If the tag isn't in the whitelist, remove it and continue with next iteration
|
||||
if ( ! $this->is_allowed_tag( $current_element ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//validate element attributes
|
||||
$this->validate_allowed_attributes( $current_element );
|
||||
|
||||
$this->strip_xlinks( $current_element );
|
||||
|
||||
if ( 'use' === strtolower( $current_element->tagName ) ) { // phpcs:ignore -- php DomDocument
|
||||
$this->validate_use_tag( $current_element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip PHP Tags
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
private function strip_php_tags( $string ) {
|
||||
$string = preg_replace( '/<\?(=|php)(.+?)\?>/i', '', $string );
|
||||
// Remove XML, ASP, etc.
|
||||
$string = preg_replace( '/<\?(.*)\?>/Us', '', $string );
|
||||
$string = preg_replace( '/<\%(.*)\%>/Us', '', $string );
|
||||
|
||||
if ( ( false !== strpos( $string, '<?' ) ) || ( false !== strpos( $string, '<%' ) ) ) {
|
||||
return '';
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip Comments
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
private function strip_comments( $string ) {
|
||||
// Remove comments.
|
||||
$string = preg_replace( '/<!--(.*)-->/Us', '', $string );
|
||||
$string = preg_replace( '/\/\*(.*)\*\//Us', '', $string );
|
||||
if ( ( false !== strpos( $string, '<!--' ) ) || ( false !== strpos( $string, '/*' ) ) ) {
|
||||
return '';
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip Line Breaks
|
||||
*
|
||||
* @since 3.16.0
|
||||
* @access private
|
||||
*
|
||||
* @param $string
|
||||
* @return string
|
||||
*/
|
||||
private function strip_line_breaks( $string ) {
|
||||
// Remove line breaks.
|
||||
return preg_replace( '/\r|\n/', '', $string );
|
||||
}
|
||||
}
|
||||
184
wp-content/plugins/elementor/core/utils/version.php
Normal file
184
wp-content/plugins/elementor/core/utils/version.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
namespace Elementor\Core\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Version {
|
||||
const PART_MAJOR_1 = 'major1';
|
||||
const PART_MAJOR_2 = 'major2';
|
||||
const PART_PATCH = 'patch';
|
||||
const PART_STAGE = 'stage';
|
||||
|
||||
/**
|
||||
* First number of a version 0.x.x
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $major1;
|
||||
|
||||
/**
|
||||
* Second number of a version x.0.x
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $major2;
|
||||
|
||||
/**
|
||||
* Third number of a version x.x.0
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $patch;
|
||||
|
||||
/**
|
||||
* The stage of a version x.x.x-stage.
|
||||
* e.g: x.x.x-dev1, x.x.x-beta3, x.x.x-rc
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $stage;
|
||||
|
||||
/**
|
||||
* Version constructor.
|
||||
*
|
||||
* @param $major1
|
||||
* @param $major2
|
||||
* @param $patch
|
||||
* @param $stage
|
||||
*/
|
||||
public function __construct( $major1, $major2, $patch, $stage = null ) {
|
||||
$this->major1 = $major1;
|
||||
$this->major2 = $major2;
|
||||
$this->patch = $patch;
|
||||
$this->stage = $stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Version instance.
|
||||
*
|
||||
* @param string $major1
|
||||
* @param string $major2
|
||||
* @param string $patch
|
||||
* @param null $stage
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create( $major1 = '0', $major2 = '0', $patch = '0', $stage = null ) {
|
||||
return new static( $major1, $major2, $patch, $stage );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current version string is valid.
|
||||
*
|
||||
* @param $version
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_valid_version( $version ) {
|
||||
return ! ! preg_match( '/^(\d+\.)?(\d+\.)?(\*|\d+)(-.+)?$/', $version );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Version instance from a string.
|
||||
*
|
||||
* @param $version
|
||||
* @param bool $should_validate
|
||||
*
|
||||
* @return static
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function create_from_string( $version, $should_validate = true ) {
|
||||
if ( $should_validate && ! static::is_valid_version( $version ) ) {
|
||||
throw new \Exception( "{$version} is an invalid version." );
|
||||
}
|
||||
|
||||
$parts = explode( '.', $version );
|
||||
$patch_parts = [];
|
||||
|
||||
$major1 = '0';
|
||||
$major2 = '0';
|
||||
$patch = '0';
|
||||
$stage = null;
|
||||
|
||||
if ( isset( $parts[0] ) ) {
|
||||
$major1 = $parts[0];
|
||||
}
|
||||
|
||||
if ( isset( $parts[1] ) ) {
|
||||
$major2 = $parts[1];
|
||||
}
|
||||
|
||||
if ( isset( $parts[2] ) ) {
|
||||
$patch_parts = explode( '-', $parts[2] );
|
||||
|
||||
$patch = $patch_parts[0];
|
||||
}
|
||||
|
||||
if ( isset( $patch_parts[1] ) ) {
|
||||
$stage = $patch_parts[1];
|
||||
}
|
||||
|
||||
return static::create( $major1, $major2, $patch, $stage );
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the current version instance with another version.
|
||||
*
|
||||
* @param $operator
|
||||
* @param $version
|
||||
* @param string $part
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function compare( $operator, $version, $part = self::PART_STAGE ) {
|
||||
if ( ! ( $version instanceof Version ) ) {
|
||||
if ( ! static::is_valid_version( $version ) ) {
|
||||
$version = '0.0.0';
|
||||
}
|
||||
|
||||
$version = static::create_from_string( $version, false );
|
||||
}
|
||||
|
||||
$current_version = clone $this;
|
||||
$compare_version = clone $version;
|
||||
|
||||
if ( in_array( $part, [ self::PART_PATCH, self::PART_MAJOR_2, self::PART_MAJOR_1 ], true ) ) {
|
||||
$current_version->stage = null;
|
||||
$compare_version->stage = null;
|
||||
}
|
||||
|
||||
if ( in_array( $part, [ self::PART_MAJOR_2, self::PART_MAJOR_1 ], true ) ) {
|
||||
$current_version->patch = '0';
|
||||
$compare_version->patch = '0';
|
||||
}
|
||||
|
||||
if ( self::PART_MAJOR_1 === $part ) {
|
||||
$current_version->major2 = '0';
|
||||
$compare_version->major2 = '0';
|
||||
}
|
||||
|
||||
return version_compare(
|
||||
$current_version,
|
||||
$compare_version,
|
||||
$operator
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implode the version and return it as string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
$version = implode( '.', [ $this->major1, $this->major2, $this->patch ] );
|
||||
|
||||
if ( $this->stage ) {
|
||||
$version .= '-' . $this->stage;
|
||||
}
|
||||
|
||||
return $version;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user