feat: initial ACRIB WordPress deployment
- WordPress 6.9.4 (es_ES) with Kadence theme - Homepage: Hero, La Asociación, Pilares, Beneficios, Eventos, Miembros, Hazte Miembro, Contacto - Brand identity: #13294b navy, #a12932 burgundy, #c69c48 gold - Fonts: Raleway (headings) + Source Sans 3 (body) + Lato (UI) - Plugins: Kadence Blocks, Polylang, Contact Form 7 - Custom CSS with full brand styling and responsive layout - HTTPS enforced via wp-config.php proxy detection
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities;
|
||||
|
||||
use WP_User;
|
||||
use WP_Syntex\Polylang\Capabilities\User\Creator;
|
||||
use WP_Syntex\Polylang\Capabilities\User\User_Interface;
|
||||
use WP_Syntex\Polylang\Capabilities\User\Creator_Interface;
|
||||
|
||||
/**
|
||||
* A class allowing to map Polylang's custom user capabilities to WP's native ones.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class Capabilities {
|
||||
public const LANGUAGES = 'manage_languages';
|
||||
public const TRANSLATIONS = 'manage_translations';
|
||||
|
||||
/**
|
||||
* The user creator to be used for capability checks.
|
||||
*
|
||||
* @var Creator_Interface|null
|
||||
*/
|
||||
private static ?Creator_Interface $creator = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter( 'map_meta_cap', array( $this, 'map_custom_caps' ), 1, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters user capabilities to handle PLL's custom capabilities.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string[] $caps Primitive capabilities required by the user.
|
||||
* @param string $cap Capability being checked.
|
||||
* @return string[]
|
||||
*/
|
||||
public function map_custom_caps( $caps, $cap ) {
|
||||
if ( in_array( $cap, array( self::TRANSLATIONS, self::LANGUAGES ), true ) ) {
|
||||
$caps = array_diff( $caps, array( $cap ) );
|
||||
$caps[] = 'manage_options';
|
||||
}
|
||||
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user instance to be used for capability checks.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_User|null $user The user to decorate. If null, the current user is used.
|
||||
* @return User_Interface The user instance.
|
||||
*/
|
||||
public static function get_user( ?WP_User $user = null ): User_Interface {
|
||||
if ( ! self::$creator ) {
|
||||
self::$creator = new Creator();
|
||||
}
|
||||
|
||||
return self::$creator->get( $user ?? wp_get_current_user() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user creator to be used for capability checks.
|
||||
*
|
||||
* Having a separate class to create the decorated user allows for better decoupling.
|
||||
* This allows to set a creator object without dependence to a `WP_User`.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Creator_Interface $creator The user creator to be used for capability checks.
|
||||
* @return void
|
||||
*/
|
||||
public static function set_user_creator( Creator_Interface $creator ): void {
|
||||
self::$creator = $creator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities\Create;
|
||||
|
||||
use PLL_Model;
|
||||
use PLL_Language;
|
||||
use WP_Syntex\Polylang\REST\Request;
|
||||
|
||||
/**
|
||||
* Class to manage the language context for posts creation or update.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
abstract class Abstract_Object {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
protected $pref_lang;
|
||||
|
||||
/**
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
protected $curlang;
|
||||
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Model $model The model instance.
|
||||
* @param Request $request The request instance.
|
||||
* @param PLL_Language|null $pref_lang The preferred language.
|
||||
* @param PLL_Language|null $curlang The current language.
|
||||
*/
|
||||
public function __construct( PLL_Model $model, Request $request, ?PLL_Language $pref_lang, ?PLL_Language $curlang ) {
|
||||
$this->model = $model;
|
||||
$this->request = $request;
|
||||
$this->pref_lang = $pref_lang;
|
||||
$this->curlang = $curlang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language to set for an object creation or update based on the global context.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param int $id The object ID.
|
||||
* @return PLL_Language The language defined from the global context.
|
||||
*/
|
||||
abstract public function get_language( int $id = 0 ): PLL_Language;
|
||||
}
|
||||
70
wp-content/plugins/polylang/src/Capabilities/Create/Post.php
Normal file
70
wp-content/plugins/polylang/src/Capabilities/Create/Post.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities\Create;
|
||||
|
||||
use PLL_Language;
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
/**
|
||||
* Class to manage the language context for posts creation or update.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class Post extends Abstract_Object {
|
||||
/**
|
||||
* Returns the language to set for a post creation or update.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param int $id The post ID for which to set the language. Default `0`.
|
||||
* @return PLL_Language The language context.
|
||||
*/
|
||||
public function get_language( int $id = 0 ): PLL_Language {
|
||||
/** @var PLL_Language $default_language The default language is always defined. */
|
||||
$default_language = $this->model->get_default_language();
|
||||
$user = Capabilities::get_user();
|
||||
|
||||
if ( ! empty( $_GET['new_lang'] ) && $lang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
// Defined only on admin.
|
||||
return $lang;
|
||||
}
|
||||
if ( ! isset( $this->pref_lang ) && ! empty( $_REQUEST['lang'] ) && $lang = $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
// Testing $this->pref_lang makes this test pass only on frontend.
|
||||
return $lang;
|
||||
}
|
||||
if ( $this->request && $lang = $this->request->get_language() ) {
|
||||
// REST request.
|
||||
return $lang;
|
||||
}
|
||||
if ( ( $parent_id = wp_get_post_parent_id( $id ) ) && $parent_lang = $this->model->post->get_language( $parent_id ) ) {
|
||||
// Use parent if exists.
|
||||
return $parent_lang;
|
||||
}
|
||||
if ( isset( $this->pref_lang ) && $user->can_translate( $this->pref_lang ) ) {
|
||||
// Always defined on admin, never defined on frontend.
|
||||
return $this->pref_lang;
|
||||
}
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
// Only on frontend due to the previous test always true on admin.
|
||||
return $this->curlang;
|
||||
}
|
||||
if ( $user->is_translator() ) {
|
||||
// Use default language if user can translate into it...
|
||||
if ( $user->can_translate( $default_language ) ) {
|
||||
return $default_language;
|
||||
}
|
||||
|
||||
// ... or its preferred one.
|
||||
$preferred_language = $this->model->get_language( $user->get_preferred_language_slug() );
|
||||
if ( $preferred_language ) {
|
||||
return $preferred_language;
|
||||
}
|
||||
}
|
||||
|
||||
// In all other cases use default language because we must have a language to set.
|
||||
return $default_language;
|
||||
}
|
||||
}
|
||||
77
wp-content/plugins/polylang/src/Capabilities/Create/Term.php
Normal file
77
wp-content/plugins/polylang/src/Capabilities/Create/Term.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities\Create;
|
||||
|
||||
use PLL_Language;
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
/**
|
||||
* Class to manage the language context for terms creation or update.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class Term extends Abstract_Object {
|
||||
/**
|
||||
* Returns the language to set for a post creation.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param int $id The term ID for which to set the language. Default `0`.
|
||||
* @param string $taxonomy The taxonomy for which to set the language. Default `''`.
|
||||
* @return PLL_Language The language context.
|
||||
*/
|
||||
public function get_language( int $id = 0, string $taxonomy = '' ): PLL_Language {
|
||||
/** @var PLL_Language $default_language The default language is always defined. */
|
||||
$default_language = $this->model->get_default_language();
|
||||
$user = Capabilities::get_user();
|
||||
|
||||
if ( ! empty( $_GET['new_lang'] ) && $lang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
// Defined only on admin.
|
||||
return $lang;
|
||||
}
|
||||
if ( ! empty( $_POST['term_lang_choice'] ) && is_string( $_POST['term_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['term_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return $lang;
|
||||
}
|
||||
if ( ! empty( $_POST['inline_lang_choice'] ) && is_string( $_POST['inline_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return $lang;
|
||||
}
|
||||
if ( ! isset( $this->pref_lang ) && ! empty( $_REQUEST['lang'] ) && $lang = $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
// Testing $this->pref_lang makes this test pass only on frontend.
|
||||
return $lang;
|
||||
}
|
||||
if ( $this->request && $lang = $this->request->get_language() ) {
|
||||
// REST request.
|
||||
return $lang;
|
||||
}
|
||||
if ( ( $term = get_term( $id, $taxonomy ) ) && ! empty( $term->parent ) && $parent_lang = $this->model->term->get_language( $term->parent ) ) {
|
||||
// Sets language from term parent if exists thanks to Scott Kingsley Clark.
|
||||
return $parent_lang;
|
||||
}
|
||||
if ( isset( $this->pref_lang ) && $user->can_translate( $this->pref_lang ) ) {
|
||||
// Always defined on admin, never defined on frontend.
|
||||
return $this->pref_lang;
|
||||
}
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
// Only on frontend due to the previous test always true on admin.
|
||||
return $this->curlang;
|
||||
}
|
||||
if ( $user->is_translator() ) {
|
||||
// Use default language if user can translate into it...
|
||||
if ( $user->can_translate( $default_language ) ) {
|
||||
return $default_language;
|
||||
}
|
||||
|
||||
// ... or its preferred one.
|
||||
$preferred_language = $this->model->get_language( $user->get_preferred_language_slug() );
|
||||
if ( $preferred_language ) {
|
||||
return $preferred_language;
|
||||
}
|
||||
}
|
||||
|
||||
// In all other cases use default language because we must have a language to set.
|
||||
return $default_language;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities\User;
|
||||
|
||||
use WP_User;
|
||||
|
||||
/**
|
||||
* A class to create a decorated user.
|
||||
* Always returns a `NOOP` instance so capabilities features are disabled by default.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class Creator implements Creator_Interface {
|
||||
/**
|
||||
* The user instance to be used for capability checks.
|
||||
*
|
||||
* @var NOOP
|
||||
*/
|
||||
private ?NOOP $instance = null;
|
||||
|
||||
/**
|
||||
* Creates and returns the user.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_User $user The user to decorate.
|
||||
* @return User_Interface Instance of `NOOP`.
|
||||
*/
|
||||
public function get( WP_User $user ): User_Interface {
|
||||
if ( $this->instance && $user->ID === $this->instance->get_id() ) {
|
||||
return $this->instance;
|
||||
}
|
||||
|
||||
$this->instance = new NOOP( $user );
|
||||
|
||||
return $this->instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities\User;
|
||||
|
||||
use WP_User;
|
||||
|
||||
/**
|
||||
* An interface for user creators.
|
||||
* Allows to change the user decoration behavior at runtime.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
interface Creator_Interface {
|
||||
/**
|
||||
* Returns the user instance to be used for capability checks.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_User $user The user to decorate.
|
||||
* @return User_Interface The user instance.
|
||||
*/
|
||||
public function get( WP_User $user ): User_Interface;
|
||||
}
|
||||
125
wp-content/plugins/polylang/src/Capabilities/User/NOOP.php
Normal file
125
wp-content/plugins/polylang/src/Capabilities/User/NOOP.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities\User;
|
||||
|
||||
use WP_User;
|
||||
use PLL_Language;
|
||||
|
||||
/**
|
||||
* A NOOP user class that decorates `WP_User` and deactivates language-related methods.
|
||||
* This class allows all translations but doesn't consider the user as a translator.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class NOOP implements User_Interface {
|
||||
/**
|
||||
* User instance to decorate.
|
||||
*
|
||||
* @var WP_User
|
||||
*/
|
||||
private WP_User $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_User $user An instance of `WP_User`.
|
||||
*/
|
||||
public function __construct( WP_User $user ) {
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user ID.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_id(): int {
|
||||
return $this->user->ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the user is a translator (has a translator capability).
|
||||
* Always returns false for NOOP user.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_translator(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the user can translate to the given language.
|
||||
* Always returns true for NOOP user.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Language $language A language object.
|
||||
* @return bool
|
||||
*/
|
||||
public function can_translate( PLL_Language $language ): bool { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the user can translate to all the given languages.
|
||||
* Always returns true for NOOP user.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param array $languages List of language slugs.
|
||||
* @return bool
|
||||
*
|
||||
* @phpstan-param array<non-empty-string> $languages
|
||||
*/
|
||||
public function can_translate_all( array $languages ): bool { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the user has the specified capability.
|
||||
* Delegates to WP_User.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $capability Capability name.
|
||||
* @param mixed ...$args Optional further parameters, typically starting with an object ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function has_cap( $capability, ...$args ): bool {
|
||||
return $this->user->has_cap( $capability, ...$args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred language of the user.
|
||||
* Always returns an empty string for NOOP user.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string The preferred language slug, empty string if no preferred language is found.
|
||||
*/
|
||||
public function get_preferred_language_slug(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current user has the rights to assign a language to an object and dies if not.
|
||||
* Does nothing for NOOP user (always allows).
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Language $language The language to assign.
|
||||
* @return void|never Dies if the user does not have the rights, does nothing otherwise.
|
||||
*/
|
||||
public function can_translate_or_die( PLL_Language $language ): void { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
// Never say die!
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Capabilities\User;
|
||||
|
||||
use WP_User;
|
||||
use PLL_Language;
|
||||
|
||||
/**
|
||||
* An interface for user with translation management feature.
|
||||
* Implements decorator pattern for `WP_User`.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
interface User_Interface {
|
||||
/**
|
||||
* Returns the user ID.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_id(): int;
|
||||
|
||||
/**
|
||||
* Tells if the user is a translator (has a translator capability).
|
||||
* Note: returns `true` if the user has a capability for a language that doesn't exist anymore. This is intentional,
|
||||
* to prevent the user to suddenly have the rights to translate in all languages while it wasn't allowed until then.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_translator(): bool;
|
||||
|
||||
/**
|
||||
* Tells if the user can translate to the given language.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Language $language A language object.
|
||||
* @return bool
|
||||
*/
|
||||
public function can_translate( PLL_Language $language ): bool;
|
||||
|
||||
/**
|
||||
* Tells if the user can translate to all the given languages.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param array $languages List of language slugs.
|
||||
* @return bool
|
||||
*
|
||||
* @phpstan-param array<non-empty-string> $languages
|
||||
*/
|
||||
public function can_translate_all( array $languages ): bool;
|
||||
|
||||
/**
|
||||
* Tells if the user has the specified capability.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $capability Capability name.
|
||||
* @param mixed ...$args Optional further parameters, typically starting with an object ID.
|
||||
* @return bool
|
||||
*/
|
||||
public function has_cap( $capability, ...$args ): bool;
|
||||
|
||||
/**
|
||||
* Returns the preferred language of the user.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string The preferred language slug, empty string if no preferred language is found.
|
||||
*/
|
||||
public function get_preferred_language_slug(): string;
|
||||
|
||||
/**
|
||||
* Checks if the current user has the rights to assign a language to an object and dies if not.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Language $language The language to assign.
|
||||
* @return void|never Dies if the user does not have the rights, does nothing otherwise.
|
||||
*/
|
||||
public function can_translate_or_die( PLL_Language $language ): void;
|
||||
}
|
||||
39
wp-content/plugins/polylang/src/Model/Hide_Default.php
Normal file
39
wp-content/plugins/polylang/src/Model/Hide_Default.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Model;
|
||||
|
||||
/**
|
||||
* Class to filter the list of languages to only include non-default languages.
|
||||
*/
|
||||
class Hide_Default implements Languages_Proxy_Interface {
|
||||
/**
|
||||
* Returns the proxy's key.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key(): string {
|
||||
return 'hide_default';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of non-default languages after passing it through this proxy.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param \PLL_Language[] $languages List of languages to filter.
|
||||
* @return \PLL_Language[]
|
||||
*/
|
||||
public function filter( array $languages ): array {
|
||||
return array_filter(
|
||||
$languages,
|
||||
static function ( $lang ) {
|
||||
return ! $lang->is_default;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
39
wp-content/plugins/polylang/src/Model/Hide_Empty.php
Normal file
39
wp-content/plugins/polylang/src/Model/Hide_Empty.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Model;
|
||||
|
||||
/**
|
||||
* Class to filter the list of languages to only include non-empty languages.
|
||||
*/
|
||||
class Hide_Empty implements Languages_Proxy_Interface {
|
||||
/**
|
||||
* Returns the proxy's key.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key(): string {
|
||||
return 'hide_empty';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of non-empty languages after passing it through this proxy.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param \PLL_Language[] $languages List of languages to filter.
|
||||
* @return \PLL_Language[] Filtered languages.
|
||||
*/
|
||||
public function filter( array $languages ): array {
|
||||
return array_filter(
|
||||
$languages,
|
||||
static function ( $lang ) {
|
||||
return $lang->get_tax_prop( 'language', 'count' ) > 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
1396
wp-content/plugins/polylang/src/Model/Languages.php
Normal file
1396
wp-content/plugins/polylang/src/Model/Languages.php
Normal file
File diff suppressed because it is too large
Load Diff
88
wp-content/plugins/polylang/src/Model/Languages_Proxies.php
Normal file
88
wp-content/plugins/polylang/src/Model/Languages_Proxies.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Model;
|
||||
|
||||
use PLL_Language;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class allowing to chain language proxies.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class Languages_Proxies {
|
||||
/**
|
||||
* @var Languages
|
||||
*/
|
||||
protected $languages;
|
||||
|
||||
/**
|
||||
* @var Languages_Proxy_Interface[]
|
||||
*
|
||||
* @phpstan-var array<non-falsy-string, Languages_Proxy_Interface>
|
||||
*/
|
||||
private $proxies = array();
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $stack = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Languages $languages Languages' model.
|
||||
* @param array $proxies List of registered proxies.
|
||||
* @param string $parent Key of the first item of the proxies stack to traverse.
|
||||
*/
|
||||
public function __construct( Languages $languages, array $proxies, string $parent ) {
|
||||
$this->languages = $languages;
|
||||
$this->proxies = $proxies;
|
||||
$this->stack[] = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available languages after passing it through proxies.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param array $args Optional arguments to pass to `Languages::get_list()`.
|
||||
* @return array List of `PLL_Language` objects or `PLL_Language` object properties.
|
||||
*/
|
||||
public function get_list( array $args = array() ): array {
|
||||
$all_args = $args;
|
||||
unset( $args['fields'] );
|
||||
|
||||
$languages = $this->languages->get_list( $args );
|
||||
|
||||
foreach ( $this->stack as $key ) {
|
||||
if ( ! isset( $this->proxies[ $key ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$languages = $this->proxies[ $key ]->filter( $languages );
|
||||
}
|
||||
|
||||
$languages = array_values( $languages ); // Re-index.
|
||||
|
||||
return $this->languages->maybe_convert_list( $languages, $all_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stacks a proxy that will filter the list of languages.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $key Proxy's key.
|
||||
* @return Languages_Proxies
|
||||
*/
|
||||
public function filter( string $key ): Languages_Proxies {
|
||||
$this->stack[] = $key;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Model;
|
||||
|
||||
use PLL_Language;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Interface allowing to proxy the list of languages.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
interface Languages_Proxy_Interface {
|
||||
/**
|
||||
* Returns the proxy's key.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return non-falsy-string
|
||||
*/
|
||||
public function key(): string;
|
||||
|
||||
/**
|
||||
* Returns the list of available languages after passing it through this proxy.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Language[] $languages List of languages to filter.
|
||||
* @return PLL_Language[]
|
||||
*/
|
||||
public function filter( array $languages ): array;
|
||||
}
|
||||
69
wp-content/plugins/polylang/src/Model/Post_Types.php
Normal file
69
wp-content/plugins/polylang/src/Model/Post_Types.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Model;
|
||||
|
||||
use PLL_Translated_Post;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Model for post types translated by Polylang.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Post_Types {
|
||||
/**
|
||||
* Translated post model.
|
||||
*
|
||||
* @var PLL_Translated_Post
|
||||
*/
|
||||
public $translated_object;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param PLL_Translated_Post $translated_object Posts model.
|
||||
*/
|
||||
public function __construct( PLL_Translated_Post $translated_object ) {
|
||||
$this->translated_object = $translated_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns post types that need to be translated.
|
||||
* The post types list is cached for better better performance.
|
||||
* The method waits for 'after_setup_theme' to apply the cache
|
||||
* to allow themes adding the filter in functions.php.
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.7 Moved from `PLL_Model::get_translated_post_types()` to `WP_Syntex\Polylang\Model\Taxonomies::get_translated()`.
|
||||
*
|
||||
* @param bool $filter True if we should return only valid registered post types.
|
||||
* @return string[] Post type names for which Polylang manages languages and translations.
|
||||
*/
|
||||
public function get_translated( $filter = true ): array {
|
||||
return $this->translated_object->get_translated_object_types( $filter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Polylang manages languages and translations for this post type.
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.7 Moved from `PLL_Model::is_translated_post_type()` to `WP_Syntex\Polylang\Model\Taxonomies::is_translated()`.
|
||||
*
|
||||
* @param string|string[] $post_type Post type name or array of post type names.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_translated( $post_type ): bool {
|
||||
if ( empty( array_filter( (array) $post_type ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @phpstan-var non-empty-array<non-empty-string>|non-empty-string $post_type */
|
||||
return $this->translated_object->is_translated_object_type( $post_type );
|
||||
}
|
||||
}
|
||||
135
wp-content/plugins/polylang/src/Model/Taxonomies.php
Normal file
135
wp-content/plugins/polylang/src/Model/Taxonomies.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Model;
|
||||
|
||||
use PLL_Translated_Term;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Model for taxonomies filtered/translated by Polylang.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Taxonomies {
|
||||
/**
|
||||
* Translated term model.
|
||||
*
|
||||
* @var PLL_Translated_Term
|
||||
*/
|
||||
public $translated_object;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param PLL_Translated_Term $translated_object Terms model.
|
||||
*/
|
||||
public function __construct( PLL_Translated_Term $translated_object ) {
|
||||
$this->translated_object = $translated_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns taxonomies that need to be translated.
|
||||
* The taxonomies list is cached for better better performance.
|
||||
* The method waits for 'after_setup_theme' to apply the cache
|
||||
* to allow themes adding the filter in functions.php.
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.7 Moved from `PLL_Model::get_translated_taxonomies()` to `WP_Syntex\Polylang\Model\Taxonomies::get_translated()`.
|
||||
*
|
||||
* @param bool $filter True if we should return only valid registered taxonomies.
|
||||
* @return string[] Array of registered taxonomy names for which Polylang manages languages and translations.
|
||||
*/
|
||||
public function get_translated( $filter = true ): array {
|
||||
return $this->translated_object->get_translated_object_types( $filter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Polylang manages languages and translations for this taxonomy.
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.7 Moved from `PLL_Model::is_translated_taxonomy()` to `WP_Syntex\Polylang\Model\Taxonomies::is_translated()`.
|
||||
*
|
||||
* @param string|string[] $tax Taxonomy name or array of taxonomy names.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_translated( $tax ): bool {
|
||||
if ( empty( array_filter( (array) $tax ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->translated_object->is_translated_object_type( $tax );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return taxonomies that need to be filtered (post_format like).
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 3.7 Moved from `PLL_Model::get_filtered_taxonomies()` to `WP_Syntex\Polylang\Model\Taxonomies::get_filtered()`.
|
||||
*
|
||||
* @param bool $filter True if we should return only valid registered taxonomies.
|
||||
* @return string[] Array of registered taxonomy names.
|
||||
*/
|
||||
public function get_filtered( $filter = true ): array {
|
||||
if ( did_action( 'after_setup_theme' ) ) {
|
||||
static $taxonomies = null;
|
||||
}
|
||||
|
||||
if ( empty( $taxonomies ) ) {
|
||||
$taxonomies = array( 'post_format' => 'post_format' );
|
||||
|
||||
/**
|
||||
* Filters the list of taxonomies not translatable but filtered by language.
|
||||
* Includes only the post format by default
|
||||
* The filter must be added soon in the WordPress loading process:
|
||||
* in a function hooked to ‘plugins_loaded’ or directly in functions.php for themes.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string[] $taxonomies List of taxonomy names.
|
||||
* @param bool $is_settings True when displaying the list of custom taxonomies in Polylang settings.
|
||||
*/
|
||||
$taxonomies = apply_filters( 'pll_filtered_taxonomies', $taxonomies, false );
|
||||
}
|
||||
|
||||
return $filter ? array_intersect( $taxonomies, get_taxonomies() ) : $taxonomies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Polylang filters this taxonomy per language.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 3.7 Moved from `PLL_Model::is_filtered_taxonomy()` to `WP_Syntex\Polylang\Model\Taxonomies::is_filtered()`.
|
||||
*
|
||||
* @param string|string[] $tax Taxonomy name or array of taxonomy names.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_filtered( $tax ): bool {
|
||||
$taxonomies = $this->get_filtered( false );
|
||||
return ( is_array( $tax ) && array_intersect( $tax, $taxonomies ) ) || in_array( $tax, $taxonomies );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query vars of all filtered taxonomies.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 3.7 Moved from `PLL_Model::get_filtered_taxonomies_query_vars()` to `WP_Syntex\Polylang\Model\Taxonomies::get_filtered_query_vars()`.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_filtered_query_vars(): array {
|
||||
$query_vars = array();
|
||||
foreach ( $this->get_filtered() as $filtered_tax ) {
|
||||
$tax = get_taxonomy( $filtered_tax );
|
||||
if ( ! empty( $tax ) && is_string( $tax->query_var ) ) {
|
||||
$query_vars[] = $tax->query_var;
|
||||
}
|
||||
}
|
||||
return $query_vars;
|
||||
}
|
||||
}
|
||||
338
wp-content/plugins/polylang/src/Options/Abstract_Option.php
Normal file
338
wp-content/plugins/polylang/src/Options/Abstract_Option.php
Normal file
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Term;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining a single option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @phpstan-type SchemaType 'string'|'null'|'number'|'integer'|'boolean'|'array'|'object'
|
||||
* @phpstan-type Schema array{
|
||||
* type: SchemaType,
|
||||
* description: string,
|
||||
* default: mixed
|
||||
* }
|
||||
*/
|
||||
abstract class Abstract_Option {
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Cached option JSON schema.
|
||||
*
|
||||
* @var array|null
|
||||
*
|
||||
* @phpstan-var Schema|null
|
||||
*/
|
||||
private $schema;
|
||||
|
||||
/**
|
||||
* Validation and sanitization errors.
|
||||
*
|
||||
* @var WP_Error
|
||||
*/
|
||||
protected $errors;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param mixed $value Optional. Option value.
|
||||
*/
|
||||
public function __construct( $value = null ) {
|
||||
$this->errors = new WP_Error();
|
||||
|
||||
if ( ! isset( $value ) ) {
|
||||
$this->value = $this->get_default();
|
||||
return;
|
||||
}
|
||||
|
||||
$value = rest_sanitize_value_from_schema( $this->prepare( $value ), $this->get_data_structure(), static::key() );
|
||||
|
||||
if ( ! is_wp_error( $value ) ) {
|
||||
$this->value = $value;
|
||||
} else {
|
||||
$this->value = $this->get_default();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return non-falsy-string
|
||||
*/
|
||||
abstract public static function key(): string;
|
||||
|
||||
/**
|
||||
* Sets option's value if valid, does nothing otherwise.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param mixed $value Value to set.
|
||||
* @param Options $options All options.
|
||||
* @return bool True if the value has been assigned. False in case of errors.
|
||||
*/
|
||||
public function set( $value, Options $options ): bool {
|
||||
$this->errors = new WP_Error(); // Reset errors.
|
||||
$value = $this->prepare( $value );
|
||||
$is_valid = rest_validate_value_from_schema( $value, $this->get_data_structure(), static::key() );
|
||||
|
||||
if ( is_wp_error( $is_valid ) ) {
|
||||
// Blocking validation error.
|
||||
$this->errors->merge_from( $is_valid );
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = $this->sanitize( $value, $options );
|
||||
|
||||
if ( is_wp_error( $value ) ) {
|
||||
// Blocking sanitization error.
|
||||
$this->errors->merge_from( $value );
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns option's value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default option value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return mixed The new value.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->value = $this->get_default();
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON schema of the option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array The schema.
|
||||
*
|
||||
* @phpstan-return Schema
|
||||
*/
|
||||
public function get_schema(): array {
|
||||
if ( is_array( $this->schema ) ) {
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
$this->schema = array_merge(
|
||||
array(
|
||||
'description' => $this->get_description(),
|
||||
'default' => $this->get_default(),
|
||||
),
|
||||
$this->get_data_structure()
|
||||
);
|
||||
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-blocking sanitization errors.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return WP_Error
|
||||
*/
|
||||
public function get_errors(): WP_Error {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a value before validation.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param mixed $value Value to format.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function prepare( $value ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes option's value, can be overridden for specific cases not handled by `rest_sanitize_value_from_schema()`.
|
||||
* Can populate the `$errors` property with blocking and non-blocking errors: in case of non-blocking errors,
|
||||
* the value is sanitized and can be stored.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param mixed $value Value to sanitize.
|
||||
* @param Options $options All options.
|
||||
* @return mixed The sanitized value. An instance of `WP_Error` in case of blocking error.
|
||||
*/
|
||||
protected function sanitize( $value, Options $options ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return rest_sanitize_value_from_schema( $value, $this->get_data_structure(), static::key() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function get_default();
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: SchemaType}
|
||||
*/
|
||||
abstract protected function get_data_structure(): array;
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_description(): string;
|
||||
|
||||
/**
|
||||
* Returns a list of language terms.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @phpstan-return list<WP_Term>
|
||||
*/
|
||||
protected function get_language_terms(): array {
|
||||
$language_terms = get_terms(
|
||||
array(
|
||||
'taxonomy' => 'language',
|
||||
'hide_empty' => false,
|
||||
)
|
||||
);
|
||||
return is_array( $language_terms ) ? $language_terms : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a non-blocking error warning about unknown language slugs.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array $language_slugs List of language slugs.
|
||||
* @return void
|
||||
*/
|
||||
protected function add_unknown_languages_warning( array $language_slugs ): void {
|
||||
if ( 1 === count( $language_slugs ) ) {
|
||||
/* translators: %s is a language slug. */
|
||||
$message = __( 'The language %s is unknown and has been discarded.', 'polylang' );
|
||||
} else {
|
||||
/* translators: %s is a list of language slugs. */
|
||||
$message = __( 'The languages %s are unknown and have been discarded.', 'polylang' );
|
||||
}
|
||||
|
||||
$this->errors->add(
|
||||
sprintf( 'pll_unknown_%s_languages', static::key() ),
|
||||
sprintf(
|
||||
$message,
|
||||
wp_sprintf_l(
|
||||
'%l',
|
||||
array_map(
|
||||
function ( $slug ) {
|
||||
return "<code>{$slug}</code>";
|
||||
},
|
||||
$language_slugs
|
||||
)
|
||||
)
|
||||
),
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
* Does nothing by default.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders site health information by appending additional fields.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param mixed $value The value to be added to the site health fields.
|
||||
*
|
||||
* @return array Updated array of site health information including the new fields.
|
||||
*/
|
||||
protected function format_single_value_for_site_health_info( $value ): array {
|
||||
if ( empty( $value ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'label' => ucfirst( static::key() ),
|
||||
'value' => $value,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an array to display in options information.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param array<scalar> $array An array of formatted data.
|
||||
* @return string
|
||||
*/
|
||||
protected function format_array_for_site_health_info( array $array ): string {
|
||||
array_walk(
|
||||
$array,
|
||||
function ( &$value, $key ) {
|
||||
$value = "$key => $value";
|
||||
}
|
||||
);
|
||||
|
||||
return '[' . implode( ', ', $array ) . ']';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_List;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining object types list option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
abstract class Abstract_Object_Types extends Abstract_List {
|
||||
/**
|
||||
* Sanitizes option's value.
|
||||
* Can return a `WP_Error` object in case of blocking sanitization error: the value must be rejected then.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array $value Value to filter.
|
||||
* @param Options $options All options.
|
||||
* @return array|WP_Error The sanitized value. An instance of `WP_Error` in case of blocking error.
|
||||
*
|
||||
* @phpstan-return list<non-falsy-string>|WP_Error
|
||||
*/
|
||||
protected function sanitize( $value, Options $options ) {
|
||||
$value = parent::sanitize( $value, $options );
|
||||
|
||||
if ( is_wp_error( $value ) ) {
|
||||
// Blocking sanitization error.
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @var array $value */
|
||||
return array_values( array_intersect( $value, $this->get_object_types() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-core object types.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string[] Object type names list.
|
||||
*
|
||||
* @phpstan-return array<non-falsy-string>
|
||||
*/
|
||||
abstract protected function get_object_types(): array;
|
||||
}
|
||||
90
wp-content/plugins/polylang/src/Options/Business/Browser.php
Normal file
90
wp-content/plugins/polylang/src/Options/Business/Browser.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_Boolean;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "Detect browser language" boolean option.
|
||||
* /!\ Sanitization depends on `force_lang`: this option must be set AFTER `force_lang`.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Browser extends Abstract_Boolean {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'browser'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'browser';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( ! $this->get() ) {
|
||||
$value = '0: ' . __( 'Detect browser language deactivated', 'polylang' );
|
||||
} else {
|
||||
$value = '1: ' . __( 'Detect browser language activated', 'polylang' );
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes option's value.
|
||||
* Can populate the `$errors` property with blocking and non-blocking errors: in case of non-blocking errors,
|
||||
* the value is sanitized and can be stored.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param bool $value Value to sanitize.
|
||||
* @param Options $options All options.
|
||||
* @return bool|WP_Error The sanitized value. An instance of `WP_Error` in case of blocking error.
|
||||
*/
|
||||
protected function sanitize( $value, Options $options ) {
|
||||
if ( 3 === $options->get( 'force_lang' ) && ! class_exists( 'PLL_Xdata_Domain', true ) ) {
|
||||
// Cannot share cookies between domains without Polylang Pro.
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var bool|WP_Error */
|
||||
$value = parent::sanitize( $value, $options );
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return sprintf(
|
||||
/* translators: %1$s and %2$s are "true/false" values. */
|
||||
__( 'Detect preferred browser language on front page: %1$s to detect, %2$s to not detect.', 'polylang' ),
|
||||
'`true`',
|
||||
'`false`'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Model\Languages;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_String;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining language slug string option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Default_Lang extends Abstract_String {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'default_lang'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'default_lang';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'string', pattern: Languages::SLUG_PATTERN}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
$string_schema = parent::get_data_structure();
|
||||
$string_schema['pattern'] = Languages::SLUG_PATTERN;
|
||||
|
||||
return $string_schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'Slug of the default language.', 'polylang' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes option's value.
|
||||
* Can populate the `$errors` property with blocking and non-blocking errors: in case of non-blocking errors,
|
||||
* the value is sanitized and can be stored.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $value Value to sanitize.
|
||||
* @param Options $options All options.
|
||||
* @return string|WP_Error The sanitized value. An instance of `WP_Error` in case of error.
|
||||
*/
|
||||
protected function sanitize( $value, Options $options ) {
|
||||
$value = parent::sanitize( $value, $options );
|
||||
|
||||
if ( is_wp_error( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @var string $value */
|
||||
if ( ! get_term_by( 'slug', $value, 'language' ) ) {
|
||||
return new WP_Error( 'pll_invalid_language', sprintf( 'The language slug \'%s\' is not a valid language.', $value ) );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
210
wp-content/plugins/polylang/src/Options/Business/Domains.php
Normal file
210
wp-content/plugins/polylang/src/Options/Business/Domains.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Model\Languages;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_Map;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining single associative array of domain as value and language slug as key option.
|
||||
* /!\ Sanitization depends on `force_lang`: this option must be set AFTER `force_lang`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @phpstan-type DomainsValue array<non-falsy-string, string>
|
||||
*/
|
||||
class Domains extends Abstract_Map {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'domains'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'domains';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to the inner structure of this option.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return array Inner structure.
|
||||
*/
|
||||
protected function get_inner_structure(): array {
|
||||
return array(
|
||||
'patternProperties' => array(
|
||||
Languages::SLUG_PATTERN => array( // Language slug as key.
|
||||
'type' => 'string',
|
||||
'format' => 'uri',
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes option's value.
|
||||
* Can populate the `$errors` property with blocking and non-blocking errors: in case of non-blocking errors,
|
||||
* the value is sanitized and can be stored.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array $value Value to sanitize.
|
||||
* @param Options $options All options.
|
||||
* @return array|WP_Error The sanitized value. An instance of `WP_Error` in case of blocking error.
|
||||
*
|
||||
* @phpstan-return DomainsValue|WP_Error
|
||||
*/
|
||||
protected function sanitize( $value, Options $options ) {
|
||||
// Sanitize new URLs.
|
||||
$value = parent::sanitize( $value, $options );
|
||||
|
||||
if ( is_wp_error( $value ) ) {
|
||||
// Blocking error.
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @phpstan-var DomainsValue */
|
||||
$current_value = $this->get();
|
||||
/** @phpstan-var DomainsValue $value */
|
||||
$all_values = array(); // Previous and new values.
|
||||
$missing_langs = array(); // Lang names corresponding to the empty values.
|
||||
$language_terms = $this->get_language_terms();
|
||||
|
||||
// Detect empty values, fill missing keys with previous values.
|
||||
foreach ( $language_terms as $lang ) {
|
||||
if ( array_key_exists( $lang->slug, $value ) ) {
|
||||
// Use the new value.
|
||||
$all_values[ $lang->slug ] = $value[ $lang->slug ];
|
||||
unset( $value[ $lang->slug ] );
|
||||
} else {
|
||||
// Use previous value.
|
||||
$all_values[ $lang->slug ] = $current_value[ $lang->slug ] ?? '';
|
||||
}
|
||||
|
||||
if ( empty( $all_values[ $lang->slug ] ) ) {
|
||||
// The value is empty.
|
||||
$missing_langs[] = $lang->name;
|
||||
}
|
||||
}
|
||||
|
||||
// Detect invalid language slugs.
|
||||
if ( ! empty( $value ) ) {
|
||||
// Non-blocking error.
|
||||
$this->add_unknown_languages_warning( array_keys( $value ) );
|
||||
}
|
||||
|
||||
if ( 3 === $options->get( 'force_lang' ) && ! empty( $missing_langs ) ) {
|
||||
// Non-blocking error.
|
||||
if ( 1 === count( $missing_langs ) ) {
|
||||
/* translators: %s is a native language name. */
|
||||
$message = __( 'Please enter a valid URL for %s.', 'polylang' );
|
||||
} else {
|
||||
/* translators: %s is a list of native language names. */
|
||||
$message = __( 'Please enter valid URLs for %s.', 'polylang' );
|
||||
}
|
||||
|
||||
$this->errors->add(
|
||||
'pll_empty_domains',
|
||||
sprintf( $message, wp_sprintf_l( '%l', $missing_langs ) ),
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
|
||||
// Ping all URLs to make sure they are valid.
|
||||
if ( $options->get( 'force_lang' ) > 1 ) {
|
||||
$failed_urls = array();
|
||||
|
||||
foreach ( array_filter( $all_values ) as $url ) {
|
||||
$url = add_query_arg( 'deactivate-polylang', 1, $url );
|
||||
// Don't redefine vip_safe_wp_remote_get() as it has not the same signature as wp_remote_get().
|
||||
$response = function_exists( 'vip_safe_wp_remote_get' ) ? vip_safe_wp_remote_get( $url ) : wp_remote_get( $url );
|
||||
|
||||
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
||||
$failed_urls[] = $url;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $failed_urls ) ) {
|
||||
// Non-blocking error.
|
||||
if ( 1 === count( $failed_urls ) ) {
|
||||
/* translators: %s is a URL. */
|
||||
$message = __( 'Polylang was unable to access the %s URL. Please check that the URL is valid.', 'polylang' );
|
||||
} else {
|
||||
/* translators: %s is a list of URLs. */
|
||||
$message = __( 'Polylang was unable to access the %s URLs. Please check that the URLs are valid.', 'polylang' );
|
||||
}
|
||||
$this->errors->add(
|
||||
'pll_invalid_domains',
|
||||
sprintf( $message, wp_sprintf_l( '%l', $failed_urls ) ),
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** @phpstan-var DomainsValue */
|
||||
return $all_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'Domains used when the language is set from different domains.', 'polylang' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( 3 === $options->get( 'force_lang' ) ) {
|
||||
return $this->format_single_value_for_site_health_info( $this->get() );
|
||||
}
|
||||
|
||||
return parent::get_site_health_info( $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reset value for a key.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $key The key to reset. Unused.
|
||||
* @return mixed The reset value.
|
||||
*/
|
||||
protected function reset_value( string $key ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the first activation option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class First_Activation extends Abstract_Option {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'first_activation'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'first_activation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @phpstan-return int<0, max>
|
||||
*/
|
||||
protected function get_default() {
|
||||
return time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'integer', minimum: 0, maximum: int<0, max>, readonly: true}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array(
|
||||
'type' => 'integer',
|
||||
'minimum' => 0,
|
||||
'maximum' => PHP_INT_MAX,
|
||||
'readonly' => true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'Time of first activation of Polylang.', 'polylang' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return $this->format_single_value_for_site_health_info( wp_date( get_option( 'date_format' ), $this->get() ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "Determine how the current language is defined" option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Force_Lang extends Abstract_Option {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'force_lang'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'force_lang';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
switch ( $this->get() ) {
|
||||
case '0':
|
||||
$value = '0: ' . __( 'The language is set from content', 'polylang' );
|
||||
break;
|
||||
case '1':
|
||||
$value = '1: ' . __( 'The language is set from the directory name in pretty permalinks', 'polylang' );
|
||||
break;
|
||||
case '2':
|
||||
$value = '2: ' . __( 'The language is set from the subdomain name in pretty permalinks', 'polylang' );
|
||||
break;
|
||||
case '3':
|
||||
$value = '3: ' . __( 'The language is set from different domains', 'polylang' );
|
||||
break;
|
||||
default:
|
||||
$value = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( $value );
|
||||
}
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_default() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'integer', enum: list<0|1|2|3>|list<1|2|3>}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array(
|
||||
'type' => 'integer',
|
||||
'enum' => 'yes' === get_option( 'pll_language_from_content_available' ) ? array( 0, 1, 2, 3 ) : array( 1, 2, 3 ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'Determine how the current language is defined.', 'polylang' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_Boolean;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "Display/Hide URL language information for default language" boolean option.
|
||||
* /!\ Sanitization depends on `force_lang`: this option must be set AFTER `force_lang`.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Hide_Default extends Abstract_Boolean {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'hide_default'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'hide_default';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( $this->get() ) {
|
||||
$value = '1: ' . __( 'Hide URL language information for default language', 'polylang' );
|
||||
} else {
|
||||
$value = '0: ' . __( 'Display URL language information for default language', 'polylang' );
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( $value );
|
||||
}
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function get_default() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes option's value.
|
||||
* Can populate the `$errors` property with blocking and non-blocking errors: in case of non-blocking errors,
|
||||
* the value is sanitized and can be stored.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param bool $value Value to sanitize.
|
||||
* @param Options $options All options.
|
||||
* @return bool|WP_Error The sanitized value. An instance of `WP_Error` in case of blocking error.
|
||||
*/
|
||||
protected function sanitize( $value, Options $options ) {
|
||||
if ( 3 === $options->get( 'force_lang' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var bool|WP_Error */
|
||||
return parent::sanitize( $value, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return sprintf(
|
||||
/* translators: %1$s and %2$s are "true/false" values. */
|
||||
__( 'Remove the language code in URL for the default language: %1$s to hide, %2$s to display.', 'polylang' ),
|
||||
'`true`',
|
||||
'`false`'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_Boolean;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "Translate media" boolean option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Media_Support extends Abstract_Boolean {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'media_support'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'media_support';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( $this->get() ) {
|
||||
$value = '1: ' . __( 'The media are translated', 'polylang' );
|
||||
} else {
|
||||
$value = '0: ' . __( 'The media are not translated', 'polylang' );
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return sprintf(
|
||||
/* translators: %1$s and %2$s are "true/false" values. */
|
||||
__( 'Translate media: %1$s to translate, %2$s otherwise.', 'polylang' ),
|
||||
'`true`',
|
||||
'`false`'
|
||||
);
|
||||
}
|
||||
}
|
||||
188
wp-content/plugins/polylang/src/Options/Business/Nav_Menus.php
Normal file
188
wp-content/plugins/polylang/src/Options/Business/Nav_Menus.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Model\Languages;
|
||||
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining navigation menus array option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @phpstan-type NavMenusValue array<
|
||||
* non-falsy-string,
|
||||
* array<
|
||||
* non-falsy-string,
|
||||
* array<non-falsy-string, int<0, max>>
|
||||
* >
|
||||
* >
|
||||
*/
|
||||
class Nav_Menus extends Abstract_Option {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'nav_menus'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'nav_menus';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$current_theme = get_stylesheet();
|
||||
/** @phpstan-var NavMenusValue $nav_menus */
|
||||
$nav_menus = $this->get();
|
||||
if ( empty( $nav_menus[ $current_theme ] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$parts = array();
|
||||
foreach ( $nav_menus[ $current_theme ] as $location => $lang ) {
|
||||
if ( empty( $lang ) ) {
|
||||
$parts[] = sprintf( '%1$s: %2$s', $location, __( 'Not used', 'polylang' ) );
|
||||
} else {
|
||||
$parts[] = sprintf(
|
||||
'%1$s: %2$s',
|
||||
$location,
|
||||
$this->format_array_for_site_health_info( $lang )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( implode( ' | ', $parts ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array(
|
||||
'type' => 'object', // Correspond to associative array in PHP, @see{https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#primitive-types}.
|
||||
'patternProperties' => array(
|
||||
'[^\/:<>\*\?"\|]+' => array( // Excludes invalid directory name characters @see https://developer.wordpress.org/reference/classes/wp_rest_themes_controller/register_routes/
|
||||
'type' => 'object',
|
||||
'patternProperties' => array(
|
||||
'[\w-]+' => array( // Accepted characters for menu locations @see https://developer.wordpress.org/reference/classes/wp_rest_menu_locations_controller/register_routes/
|
||||
'type' => 'object',
|
||||
'patternProperties' => array(
|
||||
Languages::SLUG_PATTERN => array( // Language slug as key.
|
||||
'type' => 'integer',
|
||||
'minimum' => 0, // A post ID.
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes option's value.
|
||||
* Can populate the `$errors` property with blocking and non-blocking errors: in case of non-blocking errors,
|
||||
* the value is sanitized and can be stored.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array $value Value to sanitize.
|
||||
* @param Options $options All options.
|
||||
* @return array|WP_Error The sanitized value. An instance of `WP_Error` in case of blocking error.
|
||||
*
|
||||
* @phpstan-return NavMenusValue|WP_Error
|
||||
*/
|
||||
protected function sanitize( $value, Options $options ) {
|
||||
// Sanitize new value.
|
||||
$value = parent::sanitize( $value, $options );
|
||||
|
||||
if ( is_wp_error( $value ) ) {
|
||||
// Blocking error.
|
||||
return $value;
|
||||
}
|
||||
|
||||
/** @phpstan-var NavMenusValue $value */
|
||||
if ( empty( $value ) ) {
|
||||
// Nothing to validate.
|
||||
return $value;
|
||||
}
|
||||
|
||||
$all_langs = array();
|
||||
$language_terms = wp_list_pluck( $this->get_language_terms(), 'slug' );
|
||||
|
||||
foreach ( $value as $theme_slug => $menu_ids_by_location ) {
|
||||
foreach ( $menu_ids_by_location as $location => $menu_ids ) {
|
||||
// Make sure the language slugs correspond to an existing language.
|
||||
$value[ $theme_slug ][ $location ] = array();
|
||||
|
||||
foreach ( $language_terms as $lang_slug ) {
|
||||
if ( ! empty( $menu_ids[ $lang_slug ] ) ) {
|
||||
$value[ $theme_slug ][ $location ][ $lang_slug ] = $menu_ids[ $lang_slug ];
|
||||
}
|
||||
}
|
||||
|
||||
// Detect unknown languages.
|
||||
$all_langs = array_merge( $all_langs, $menu_ids );
|
||||
}
|
||||
}
|
||||
|
||||
/** @phpstan-var NavMenusValue $value */
|
||||
$unknown_langs = array_diff_key( $all_langs, array_flip( $language_terms ) );
|
||||
|
||||
// Detect invalid language slugs.
|
||||
if ( ! empty( $unknown_langs ) ) {
|
||||
// Non-blocking error.
|
||||
$this->add_unknown_languages_warning( array_keys( $unknown_langs ) );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'Translated navigation menus for each theme.', 'polylang' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining post types list option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Post_Types extends Abstract_Object_Types {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'post_types'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'post_types';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-core post types.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string[] Object type names list.
|
||||
*
|
||||
* @phpstan-return array<non-falsy-string>
|
||||
*/
|
||||
protected function get_object_types(): array {
|
||||
/** @phpstan-var array<non-falsy-string> */
|
||||
return get_post_types( array( '_builtin' => false ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'List of post types to translate.', 'polylang' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "previous version" option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Previous_Version extends Version {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'previous_version'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'previous_version';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( "Polylang's previous version.", 'polylang' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( ! $this->get() ) {
|
||||
return $this->format_single_value_for_site_health_info( __( 'This is the first activation', 'polylang' ) );
|
||||
}
|
||||
return parent::get_site_health_info( $options );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_Boolean;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "Remove the page name or page id from the URL of the front page" boolean option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Redirect_Lang extends Abstract_Boolean {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'redirect_lang'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'redirect_lang';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( $this->get() ) {
|
||||
$value = '1: ' . __( 'The front page URL contains the language code instead of the page name or page id', 'polylang' );
|
||||
} else {
|
||||
$value = '0: ' . __( 'The front page URL contains the page name or page id instead of the language code', 'polylang' );
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return sprintf(
|
||||
/* translators: %1$s and %2$s are "true/false" values. */
|
||||
__( 'Remove the page name or page ID from the URL of the front page: %1$s to remove, %2$s to keep.', 'polylang' ),
|
||||
'`true`',
|
||||
'`false`'
|
||||
);
|
||||
}
|
||||
}
|
||||
86
wp-content/plugins/polylang/src/Options/Business/Rewrite.php
Normal file
86
wp-content/plugins/polylang/src/Options/Business/Rewrite.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_Boolean;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "Remove /language/ in pretty permalinks" boolean option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Rewrite extends Abstract_Boolean {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'rewrite'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'rewrite';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( $this->get() ) {
|
||||
$value = '1: ' . sprintf(
|
||||
/* translators: %s is a URL slug: `/language/`. */
|
||||
__( 'Remove %s in pretty permalinks', 'polylang' ),
|
||||
'`/language/`'
|
||||
);
|
||||
} else {
|
||||
$value = '0: ' . sprintf(
|
||||
/* translators: %s is a URL slug: `/language/`. */
|
||||
__( 'Keep %s in pretty permalinks', 'polylang' ),
|
||||
'`/language/`'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( $value );
|
||||
}
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function get_default() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return sprintf(
|
||||
/* translators: %1$s is a URL slug: `/language/`. %2$s and %3$s are "true/false" values. */
|
||||
__( 'Remove %1$s in pretty permalinks: %2$s to remove, %3$s to keep.', 'polylang' ),
|
||||
'`/language/`',
|
||||
'`true`',
|
||||
'`false`'
|
||||
);
|
||||
}
|
||||
}
|
||||
89
wp-content/plugins/polylang/src/Options/Business/Sync.php
Normal file
89
wp-content/plugins/polylang/src/Options/Business/Sync.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use NOOP_Translations;
|
||||
use PLL_Settings_Sync;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_List;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining synchronization settings list option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @phpstan-import-type SchemaType from \WP_Syntex\Polylang\Options\Abstract_Option
|
||||
*/
|
||||
class Sync extends Abstract_List {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'sync'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'sync';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( empty( $this->get() ) ) {
|
||||
$value = '0: ' . __( 'Synchronization disabled', 'polylang' );
|
||||
} else {
|
||||
$value = implode( ', ', $this->get() );
|
||||
}
|
||||
|
||||
return $this->format_single_value_for_site_health_info( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'array', items: array{type: SchemaType, enum: non-empty-list<non-falsy-string>}}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
$GLOBALS['l10n']['polylang'] = new NOOP_Translations(); // Prevents loading the translations too early.
|
||||
$enum = array_keys( PLL_Settings_Sync::list_metas_to_sync() );
|
||||
unset( $GLOBALS['l10n']['polylang'] );
|
||||
|
||||
return array(
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => $this->get_type(),
|
||||
'enum' => $enum,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'List of data to synchronize.', 'polylang' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining taxonomies list option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Taxonomies extends Abstract_Object_Types {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'taxonomies'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'taxonomies';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-core taxonomies.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string[] Object type names list.
|
||||
*
|
||||
* @phpstan-return array<non-falsy-string>
|
||||
*/
|
||||
protected function get_object_types(): array {
|
||||
$public_taxonomies = get_taxonomies( array( '_builtin' => false ) );
|
||||
/** @phpstan-var array<non-falsy-string> */
|
||||
return array_diff( $public_taxonomies, get_taxonomies( array( '_pll' => true ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( 'List of taxonomies to translate.', 'polylang' );
|
||||
}
|
||||
}
|
||||
54
wp-content/plugins/polylang/src/Options/Business/Version.php
Normal file
54
wp-content/plugins/polylang/src/Options/Business/Version.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Business;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_String;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining the "version" option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Version extends Abstract_String {
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return 'version'
|
||||
*/
|
||||
public static function key(): string {
|
||||
return 'version';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description used in the JSON schema.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return __( "Polylang's version.", 'polylang' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'string', readonly: true, readonly: true}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array_merge( parent::get_data_structure(), array( 'readonly' => true ) );
|
||||
}
|
||||
}
|
||||
139
wp-content/plugins/polylang/src/Options/Inactive_Option.php
Normal file
139
wp-content/plugins/polylang/src/Options/Inactive_Option.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options;
|
||||
|
||||
use WP_Error;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining a decorator for options when Polylang is not active on the current site.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class Inactive_Option extends Abstract_Option {
|
||||
public const ERROR_CODE = 'pll_not_active';
|
||||
|
||||
/**
|
||||
* The option to decorate.
|
||||
*
|
||||
* @var Abstract_Option
|
||||
*/
|
||||
private $option;
|
||||
|
||||
/**
|
||||
* The key of the option to decorate.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @phpstan-var non-falsy-string
|
||||
*/
|
||||
private static $key = 'not-an-option';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Abstract_Option $option The option to wrap.
|
||||
*/
|
||||
public function __construct( Abstract_Option $option ) {
|
||||
$this->option = $option;
|
||||
$this->errors = new WP_Error();
|
||||
|
||||
// Make sure the option doesn't contain any value.
|
||||
$this->option->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns option key.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return non-falsy-string
|
||||
*/
|
||||
public static function key(): string {
|
||||
return self::$key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing except adding an error.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param mixed $value Value to set.
|
||||
* @param Options $options All options.
|
||||
* @return bool True if the value has been assigned. False in case of errors.
|
||||
*/
|
||||
public function set( $value, Options $options ): bool { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
if ( ! in_array( self::ERROR_CODE, $this->errors->get_error_codes(), true ) ) {
|
||||
$this->errors->add(
|
||||
self::ERROR_CODE,
|
||||
/* translators: %s is a blog ID. */
|
||||
sprintf( __( 'Polylang is not active on site %s.', 'polylang' ), (int) get_current_blog_id() )
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the option, usually the default value for inactive options.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get() {
|
||||
return $this->option->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an empty schema.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return array The schema.
|
||||
*/
|
||||
public function get_schema(): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function get_default() {
|
||||
return $this->option->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not used but required by `Abstract_Option`.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not used but required by `Abstract_Option`.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_description(): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
740
wp-content/plugins/polylang/src/Options/Options.php
Normal file
740
wp-content/plugins/polylang/src/Options/Options.php
Normal file
@@ -0,0 +1,740 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options;
|
||||
|
||||
use WP_Error;
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_Map;
|
||||
use WP_Syntex\Polylang\Options\Primitive\Abstract_List;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class that manages Polylang's options:
|
||||
* - Automatically stores the options into the database on `shutdown` if they have been modified.
|
||||
* - Behaves almost like an array, meaning only values can be get/set (implements `ArrayAccess`).
|
||||
* - Handles `switch_to_blog()`.
|
||||
* - Options are always defined: it is not possible to unset them from the list, they are set to their default value instead.
|
||||
* - If an option is not registered but exists in database, its raw value will be kept and remain untouched.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @implements ArrayAccess<non-falsy-string, mixed>
|
||||
* @implements IteratorAggregate<non-empty-string, mixed>
|
||||
*
|
||||
* @phpstan-import-type Schema from Abstract_Option as OptionSchema
|
||||
* @phpstan-type Schema array{
|
||||
* '$schema': non-falsy-string,
|
||||
* title: non-falsy-string,
|
||||
* description: string,
|
||||
* type: 'object',
|
||||
* properties: array<non-falsy-string, OptionSchema>,
|
||||
* additionalProperties: false
|
||||
* }
|
||||
*/
|
||||
class Options implements ArrayAccess, IteratorAggregate {
|
||||
public const OPTION_NAME = 'polylang';
|
||||
|
||||
/**
|
||||
* Polylang's options, by blog ID.
|
||||
* Raw value if option is not registered yet, `Abstract_Option` instance otherwise.
|
||||
*
|
||||
* @var Abstract_Option[][]|mixed[][]
|
||||
* @phpstan-var array<int, array<non-falsy-string, mixed>>
|
||||
*/
|
||||
private $options = array();
|
||||
|
||||
/**
|
||||
* Tells if the options have been modified, by blog ID.
|
||||
*
|
||||
* @var bool[]
|
||||
* @phpstan-var array<int, true>
|
||||
*/
|
||||
private $modified = array();
|
||||
|
||||
/**
|
||||
* The original blog ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $blog_id;
|
||||
|
||||
/**
|
||||
* The current blog ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $current_blog_id;
|
||||
|
||||
/**
|
||||
* Map of memoized values of blog IDs to tell if Polylang is active.
|
||||
*
|
||||
* @var bool[]
|
||||
* @phpstan-var array<int, bool>
|
||||
*/
|
||||
private $is_plugin_active = array();
|
||||
|
||||
/**
|
||||
* Cached options JSON schema by blog ID.
|
||||
*
|
||||
* @var array[]|null
|
||||
* @phpstan-var array<int, Schema>|null
|
||||
*/
|
||||
private $schema;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
public function __construct() {
|
||||
// Keep track of the blog ID.
|
||||
$this->blog_id = (int) get_current_blog_id();
|
||||
$this->current_blog_id = $this->blog_id;
|
||||
$this->is_plugin_active = array( $this->blog_id => true );
|
||||
|
||||
// Handle options.
|
||||
$this->init_options_for_current_blog();
|
||||
|
||||
add_filter( 'pre_update_option_polylang', array( $this, 'protect_wp_option_storage' ), 1 );
|
||||
add_action( 'switch_blog', array( $this, 'on_blog_switch' ), -1000 ); // Options must be ready early.
|
||||
add_action( 'shutdown', array( $this, 'save_all' ), 1000 ); // Make sure to save options after everything.
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an option.
|
||||
* Options must be registered in the right order: some options depend on other options' value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $class_name Option class to register.
|
||||
* @return self
|
||||
*
|
||||
* @phpstan-param class-string<Abstract_Option> $class_name
|
||||
*/
|
||||
public function register( string $class_name ): self {
|
||||
$key = $class_name::key();
|
||||
|
||||
if ( ! array_key_exists( $key, $this->options[ $this->current_blog_id ] ) ) {
|
||||
// Option raw value doesn't exist in database, use default instead.
|
||||
$this->options[ $this->current_blog_id ][ $key ] = $this->maybe_make_option_inactive(
|
||||
new $class_name()
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// If option exists in database, use this value.
|
||||
if ( $this->options[ $this->current_blog_id ][ $key ] instanceof Abstract_Option ) {
|
||||
// Already registered, do nothing.
|
||||
$this->options[ $this->current_blog_id ][ $key ] = $this->maybe_make_option_inactive(
|
||||
$this->options[ $this->current_blog_id ][ $key ]
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Option raw value exists in database, use it.
|
||||
$this->options[ $this->current_blog_id ][ $key ] = $this->maybe_make_option_inactive(
|
||||
new $class_name( $this->options[ $this->current_blog_id ][ $key ] )
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents storing an instance of `Options` into the database.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array|Options $value The options to store.
|
||||
* @return array
|
||||
*/
|
||||
public function protect_wp_option_storage( $value ) {
|
||||
if ( $value instanceof self ) {
|
||||
return $value->get_all();
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes options for the newly switched blog if applicable.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param int $blog_id The blog ID.
|
||||
* @return void
|
||||
*/
|
||||
public function on_blog_switch( $blog_id ): void {
|
||||
$this->current_blog_id = (int) $blog_id;
|
||||
|
||||
if ( isset( $this->options[ $blog_id ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->init_options_for_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the options into the database for all blogs.
|
||||
* Hooked to `shutdown`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save_all(): void {
|
||||
// Find blog with modified options.
|
||||
$modified = $this->get_modified();
|
||||
|
||||
if ( empty( $modified ) ) {
|
||||
// Not modified.
|
||||
return;
|
||||
}
|
||||
|
||||
remove_action( 'switch_blog', array( $this, 'on_blog_switch' ), -1000 );
|
||||
|
||||
// Handle the original blog first, maybe this will prevent the use of `switch_to_blog()`.
|
||||
if ( isset( $modified[ $this->blog_id ] ) && $this->current_blog_id === $this->blog_id ) {
|
||||
$this->save();
|
||||
unset( $modified[ $this->blog_id ] );
|
||||
|
||||
if ( empty( $modified ) ) {
|
||||
// All done, no need of `switch_to_blog()`.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $modified as $blog_id => $_yup ) {
|
||||
switch_to_blog( $blog_id );
|
||||
$this->save();
|
||||
restore_current_blog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the options into the database.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return bool True if the options were updated, false otherwise.
|
||||
*/
|
||||
public function save(): bool {
|
||||
if ( empty( $this->modified[ $this->current_blog_id ] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset( $this->modified[ $this->current_blog_id ] );
|
||||
|
||||
if ( is_multisite() && ! get_site( $this->current_blog_id ) ) { // Cached by `$this->get_modified()` if called from `$this->save_all()`.
|
||||
// Deleted. Should not happen if called from `$this->save_all()`.
|
||||
return false;
|
||||
}
|
||||
|
||||
$options = get_option( self::OPTION_NAME, array() );
|
||||
|
||||
if ( is_array( $options ) ) {
|
||||
// Preserve options that are not from Polylang.
|
||||
$options = array_merge( $options, $this->get_all() );
|
||||
} else {
|
||||
$options = $this->get_all();
|
||||
}
|
||||
|
||||
return update_option( self::OPTION_NAME, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all options.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return mixed[] All options values.
|
||||
*/
|
||||
public function get_all(): array {
|
||||
if ( empty( $this->options[ $this->current_blog_id ] ) ) {
|
||||
// No options.
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_map(
|
||||
function ( $value ) {
|
||||
return $value->get();
|
||||
},
|
||||
array_filter(
|
||||
$this->options[ $this->current_blog_id ],
|
||||
function ( $value ) {
|
||||
return $value instanceof Abstract_Option;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a subset of options into the current blog ones.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array $values Array of raw options.
|
||||
* @return WP_Error
|
||||
*/
|
||||
public function merge( array $values ): WP_Error {
|
||||
$errors = new WP_Error();
|
||||
|
||||
foreach ( $this->options[ $this->current_blog_id ] as $key => $option ) {
|
||||
if ( ! isset( $values[ $key ] ) || ! $this->has( $key ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$option_errors = $this->set( $key, $values[ $key ] );
|
||||
|
||||
if ( $option_errors->has_errors() ) {
|
||||
// Blocking and non-blocking errors.
|
||||
$errors->merge_from( $option_errors );
|
||||
}
|
||||
|
||||
unset( $values[ $key ] );
|
||||
}
|
||||
|
||||
if ( empty( $values ) ) {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
// Merge all "unknown option" errors into a single error message.
|
||||
if ( 1 === count( $values ) ) {
|
||||
/* translators: %s is the name of an option. */
|
||||
$message = __( 'Unknown option key %s.', 'polylang' );
|
||||
} else {
|
||||
/* translators: %s is a list of option names. */
|
||||
$message = __( 'Unknown option keys %s.', 'polylang' );
|
||||
}
|
||||
|
||||
$errors->add(
|
||||
'pll_unknown_option_keys',
|
||||
sprintf(
|
||||
$message,
|
||||
wp_sprintf_l(
|
||||
'%l',
|
||||
array_map(
|
||||
function ( $value ) {
|
||||
return "'$value'";
|
||||
},
|
||||
array_keys( $values )
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns JSON schema for all options of the current blog.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array The schema.
|
||||
*
|
||||
* @phpstan-return Schema
|
||||
*/
|
||||
public function get_schema(): array {
|
||||
if ( isset( $this->schema[ $this->current_blog_id ] ) ) {
|
||||
return $this->schema[ $this->current_blog_id ];
|
||||
}
|
||||
|
||||
$properties = array();
|
||||
|
||||
if ( $this->is_plugin_active() ) {
|
||||
foreach ( $this->options[ $this->current_blog_id ] as $option ) {
|
||||
if ( ! $option instanceof Abstract_Option || empty( $option->get_schema() ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$properties[ $option->key() ] = $option->get_schema();
|
||||
}
|
||||
}
|
||||
|
||||
$this->schema[ $this->current_blog_id ] = array(
|
||||
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
||||
'title' => static::OPTION_NAME,
|
||||
'description' => __( 'Polylang options', 'polylang' ),
|
||||
'type' => 'object',
|
||||
'properties' => $properties,
|
||||
'additionalProperties' => false,
|
||||
);
|
||||
|
||||
return $this->schema[ $this->current_blog_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if an option exists.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $key The name of the option to check for.
|
||||
* @return bool
|
||||
*/
|
||||
public function has( string $key ): bool {
|
||||
return isset( $this->options[ $this->current_blog_id ][ $key ] ) && $this->options[ $this->current_blog_id ][ $key ] instanceof Abstract_Option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the specified option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $key The name of the option to retrieve.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get( string $key ) {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
$v = null;
|
||||
return $v;
|
||||
}
|
||||
|
||||
/** @var Abstract_Option */
|
||||
$option = $this->options[ $this->current_blog_id ][ $key ];
|
||||
return $option->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a value to the specified option.
|
||||
*
|
||||
* This doesn't allow to set an unknown option.
|
||||
* When doing multiple `set()`, options must be set in the right order: some options depend on other options' value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $key The name of the option to assign the value to.
|
||||
* @param mixed $value The value to set.
|
||||
* @return WP_Error
|
||||
*/
|
||||
public function set( string $key, $value ): WP_Error {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
/* translators: %s is the name of an option. */
|
||||
return new WP_Error( 'pll_unknown_option_key', sprintf( __( 'Unknown option key %s.', 'polylang' ), "'$key'" ) );
|
||||
}
|
||||
|
||||
/** @var Abstract_Option */
|
||||
$option = $this->options[ $this->current_blog_id ][ $key ];
|
||||
$old_value = $option->get();
|
||||
|
||||
if ( $option->set( $value, $this ) && $option->get() !== $old_value ) {
|
||||
// No blocking errors: the value can be stored.
|
||||
$this->modified[ $this->current_blog_id ] = true;
|
||||
}
|
||||
|
||||
// Return errors.
|
||||
return $option->get_errors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets an option to its default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $key The name of the option to reset.
|
||||
* @return mixed The new value.
|
||||
*/
|
||||
public function reset( string $key ) {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var Abstract_Option */
|
||||
$option = $this->options[ $this->current_blog_id ][ $key ];
|
||||
|
||||
if ( $option->get() !== $option->reset() ) {
|
||||
$this->modified[ $this->current_blog_id ] = true;
|
||||
}
|
||||
|
||||
return $option->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an option sub value from its array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $key The name of the option to splice.
|
||||
* @param mixed $value The value to remove.
|
||||
* @return WP_Error An error object, empty if the value was removed successfully.
|
||||
*/
|
||||
public function remove( string $key, $value ): WP_Error {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
return new WP_Error(
|
||||
'pll_unknown_option_key',
|
||||
/* translators: %s is the name of an option. */
|
||||
sprintf( __( 'Unknown option key %s.', 'polylang' ), "'$key'" )
|
||||
);
|
||||
}
|
||||
|
||||
$option = $this->options[ $this->current_blog_id ][ $key ];
|
||||
|
||||
if ( ! $option instanceof Abstract_List && ! $option instanceof Abstract_Map ) {
|
||||
return new WP_Error(
|
||||
'pll_invalid_option_type',
|
||||
/* translators: %s is the name of an option. */
|
||||
sprintf( __( 'Option %s is not a list or map.', 'polylang' ), "'$key'" )
|
||||
);
|
||||
}
|
||||
|
||||
if ( $option->remove( $value ) ) {
|
||||
$this->modified[ $this->current_blog_id ] = true;
|
||||
return new WP_Error();
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'pll_remove_failed',
|
||||
/* translators: %1$s is the value to remove. %2$s is the name of an option. */
|
||||
sprintf( __( 'Failed to remove %1$s from %2$s.', 'polylang' ), print_r( $value, true ), "'$key'" ) // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a value to an option.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $key The name of the option to add the value to.
|
||||
* @param mixed $value The value to add.
|
||||
* @return WP_Error An error object, empty if the value was added successfully.
|
||||
*/
|
||||
public function add( string $key, $value ): WP_Error {
|
||||
if ( ! $this->has( $key ) ) {
|
||||
return new WP_Error(
|
||||
'pll_unknown_option_key',
|
||||
/* translators: %s is the name of an option. */
|
||||
sprintf( __( 'Unknown option key %s.', 'polylang' ), "'$key'" )
|
||||
);
|
||||
}
|
||||
|
||||
$option = $this->options[ $this->current_blog_id ][ $key ];
|
||||
|
||||
if ( ! $option instanceof Abstract_List && ! $option instanceof Abstract_Map ) {
|
||||
return new WP_Error(
|
||||
'pll_invalid_option_type',
|
||||
/* translators: %s is the name of an option. */
|
||||
sprintf( __( 'Option %s is not a list or map.', 'polylang' ), "'$key'" )
|
||||
);
|
||||
}
|
||||
|
||||
if ( $option->add( $value, $this ) ) {
|
||||
$this->modified[ $this->current_blog_id ] = true;
|
||||
return new WP_Error();
|
||||
}
|
||||
|
||||
return new WP_Error(
|
||||
'pll_add_failed',
|
||||
/* translators: %1$s is the value to add. %2$s is the name of an option. */
|
||||
sprintf( __( 'Failed to add %1$s to %2$s.', 'polylang' ), print_r( $value, true ), "'$key'" ) // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if an option exists.
|
||||
* Required by interface `ArrayAccess`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $offset The name of the option to check for.
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists( $offset ): bool {
|
||||
return $this->has( (string) $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the specified option.
|
||||
* Required by interface `ArrayAccess`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $offset The name of the option to retrieve.
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet( $offset ) {
|
||||
return $this->get( (string) $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a value to the specified option.
|
||||
* This doesn't allow to set an unknown option.
|
||||
* Required by interface `ArrayAccess`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $offset The name of the option to assign the value to.
|
||||
* @param mixed $value The value to set.
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet( $offset, $value ): void {
|
||||
$this->set( (string) $offset, $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets an option.
|
||||
* This doesn't allow to unset an option, this resets it to its default value instead.
|
||||
* Required by interface `ArrayAccess`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $offset The name of the option to unset.
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset( $offset ): void {
|
||||
$this->reset( (string) $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all current site's option values.
|
||||
* Required by interface `IteratorAggregate`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return ArrayIterator
|
||||
*
|
||||
* @phpstan-return ArrayIterator<non-empty-string, mixed>
|
||||
*/
|
||||
public function getIterator(): ArrayIterator {
|
||||
return new ArrayIterator( $this->get_all() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves site health information based on the current blog's options.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return array The site health information array.
|
||||
*/
|
||||
public function get_site_health_info(): array {
|
||||
$infos = array();
|
||||
foreach ( $this->options[ $this->current_blog_id ] as $option ) {
|
||||
if ( ! $option instanceof Abstract_Option ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$info = $option->get_site_health_info( $this );
|
||||
|
||||
if ( ! empty( $info ) ) {
|
||||
$infos[ $option::key() ] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
return $infos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of modified sites.
|
||||
* On multisite, sites are cached.
|
||||
* /!\ At this point, some sites may have been deleted. They are removed from `$this->modified` here.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return bool[]
|
||||
* @phpstan-return array<int, true>
|
||||
*/
|
||||
private function get_modified(): array {
|
||||
if ( empty( $this->modified ) ) {
|
||||
// Not modified.
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
// Cleanup deleted sites and cache existing ones.
|
||||
if ( ! is_multisite() ) {
|
||||
// Not multisite: no need to cache or verify existence.
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
// Fetch all the data instead of only the IDs, so it is cached.
|
||||
$sites = get_sites(
|
||||
array(
|
||||
'site__in' => array_keys( $this->modified ),
|
||||
'number' => count( $this->modified ),
|
||||
)
|
||||
);
|
||||
|
||||
// Keep only existing blogs.
|
||||
$this->modified = array();
|
||||
foreach ( $sites as $site ) {
|
||||
$this->modified[ $site->id ] = true;
|
||||
}
|
||||
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes options for the current blog.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_options_for_current_blog(): void {
|
||||
if ( ! $this->is_plugin_active() ) {
|
||||
// Don't try to get the options from the DB.
|
||||
$this->options[ $this->current_blog_id ] = array();
|
||||
} else {
|
||||
$options = get_option( self::OPTION_NAME );
|
||||
|
||||
if ( empty( $options ) || ! is_array( $options ) ) {
|
||||
$this->options[ $this->current_blog_id ] = array();
|
||||
$this->modified[ $this->current_blog_id ] = true;
|
||||
} else {
|
||||
$this->options[ $this->current_blog_id ] = $options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after the options have been init for the current blog.
|
||||
* This is the best place to register options.
|
||||
*
|
||||
* @since 3.7
|
||||
* @since 3.8 New parameter `$is_plugin_active`.
|
||||
*
|
||||
* @param Options $options Instance of the options.
|
||||
* @param int $current_blog_id Current blog ID.
|
||||
* @param bool $is_plugin_active True if Polylang is active on the current site, false otherwise.
|
||||
* This can be false after calling `switch_to_blog()`.
|
||||
*/
|
||||
do_action( 'pll_init_options_for_blog', $this, $this->current_blog_id, $this->is_plugin_active() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if Polylang is active on the current blog.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_plugin_active(): bool {
|
||||
if ( isset( $this->is_plugin_active[ $this->current_blog_id ] ) ) {
|
||||
return $this->is_plugin_active[ $this->current_blog_id ];
|
||||
}
|
||||
|
||||
$this->is_plugin_active[ $this->current_blog_id ] = pll_is_plugin_active( POLYLANG_BASENAME ) || doing_action( 'activate_' . POLYLANG_BASENAME );
|
||||
|
||||
return $this->is_plugin_active[ $this->current_blog_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates options if we are on a site where Polylang is not active.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Abstract_Option $option The option to decorate.
|
||||
* @return Abstract_Option
|
||||
*/
|
||||
private function maybe_make_option_inactive( Abstract_Option $option ): Abstract_Option {
|
||||
if ( $this->is_plugin_active() || $option instanceof Inactive_Option ) {
|
||||
return $option;
|
||||
}
|
||||
|
||||
return new Inactive_Option( $option );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Primitive;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining single boolean option.
|
||||
* Note that for historic reason, boolean are stored as 0 or 1.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
abstract class Abstract_Boolean extends Abstract_Option {
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function get_default() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'boolean'}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array(
|
||||
'type' => 'boolean',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Primitive;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining single list option, default value type to mixed.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @phpstan-import-type SchemaType from Abstract_Option
|
||||
*/
|
||||
abstract class Abstract_List extends Abstract_Option {
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Prepares a value before validation.
|
||||
* Allows to receive a string-keyed array but returns an integer-keyed array.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param mixed $value Value to format.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function prepare( $value ) {
|
||||
if ( is_array( $value ) ) {
|
||||
return array_values( array_unique( $value ) );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema value type for the list items.
|
||||
* Possible values are `'string'`, `'null'`, `'number'` (float), `'integer'`, `'boolean'`,
|
||||
* `'array'` (array with integer keys), and `'object'` (array with string keys).
|
||||
*
|
||||
* @since 3.7
|
||||
* @see https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#primitive-types
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return SchemaType
|
||||
*/
|
||||
protected function get_type(): string {
|
||||
return 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_default() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'array', items: array{type: SchemaType}}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array(
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => $this->get_type(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the list.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param mixed $item The item to remove.
|
||||
* @return bool True if the value has been removed. False otherwise.
|
||||
*/
|
||||
public function remove( $item ): bool {
|
||||
if ( ! in_array( $item, $this->value, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->value = array_diff(
|
||||
$this->value,
|
||||
array( $item )
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the list.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param mixed $item The item to add.
|
||||
* @param Options $options The options instance.
|
||||
* @return bool True if the value was added successfully. False otherwise.
|
||||
*/
|
||||
public function add( $item, Options $options ): bool {
|
||||
/** @var array $updated_value */
|
||||
$updated_value = $this->get();
|
||||
$updated_value[] = $item;
|
||||
|
||||
return $this->set(
|
||||
$updated_value,
|
||||
$options
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Primitive;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining a map option.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
abstract class Abstract_Map extends Abstract_Option {
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array_merge(
|
||||
$this->get_inner_structure(),
|
||||
array(
|
||||
'type' => 'object', // Correspond to associative array in PHP, @see{https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#primitive-types}.
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a key from the map.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $key The key to remove.
|
||||
* @return bool True if the key has been removed. False otherwise.
|
||||
*/
|
||||
public function remove( string $key ): bool {
|
||||
if ( ! array_key_exists( $key, $this->value ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->value[ $key ] = $this->reset_value( $key );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an item to the map.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param array<string, mixed> $item The item(s) to add. Must be a key-value pair.
|
||||
* @param Options $options The options instance.
|
||||
* @return bool True if the value was added successfully. False otherwise.
|
||||
*/
|
||||
public function add( $item, Options $options ): bool {
|
||||
if ( ! is_array( $item ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $old_value */
|
||||
$old_value = $this->get();
|
||||
$updated_value = array_merge(
|
||||
$old_value,
|
||||
$item
|
||||
);
|
||||
|
||||
return $this->set(
|
||||
$updated_value,
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to the inner structure of this option.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*/
|
||||
abstract protected function get_inner_structure(): array;
|
||||
|
||||
/**
|
||||
* Returns the reset value for a key.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $key The key to reset.
|
||||
* @return mixed The reset value.
|
||||
*/
|
||||
abstract protected function reset_value( string $key );
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options\Primitive;
|
||||
|
||||
use WP_Syntex\Polylang\Options\Abstract_Option;
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class defining single string option.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
abstract class Abstract_String extends Abstract_Option {
|
||||
/**
|
||||
* Returns the default value.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_default() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON schema part specific to this option.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array Partial schema.
|
||||
*
|
||||
* @phpstan-return array{type: 'string'}
|
||||
*/
|
||||
protected function get_data_structure(): array {
|
||||
return array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds information to the site health info array.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param Options $options An instance of the Options class providing additional configuration.
|
||||
*
|
||||
* @return array The updated site health information.
|
||||
*/
|
||||
public function get_site_health_info( Options $options ): array { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
return $this->format_single_value_for_site_health_info( $this->get() );
|
||||
}
|
||||
}
|
||||
52
wp-content/plugins/polylang/src/Options/Registry.php
Normal file
52
wp-content/plugins/polylang/src/Options/Registry.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
namespace WP_Syntex\Polylang\Options;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Polylang's options registry.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class Registry {
|
||||
protected const OPTIONS = array(
|
||||
// URL modifications.
|
||||
Business\Force_Lang::class,
|
||||
Business\Domains::class,
|
||||
Business\Hide_Default::class,
|
||||
Business\Rewrite::class,
|
||||
Business\Redirect_Lang::class,
|
||||
// Detect browser language.
|
||||
Business\Browser::class,
|
||||
// Media.
|
||||
Business\Media_Support::class,
|
||||
// Custom post types and taxonomies.
|
||||
Business\Post_Types::class,
|
||||
Business\Taxonomies::class,
|
||||
// Synchronization.
|
||||
Business\Sync::class,
|
||||
// Internal.
|
||||
Business\Default_Lang::class,
|
||||
Business\Nav_Menus::class,
|
||||
// Read only.
|
||||
Business\First_Activation::class,
|
||||
Business\Previous_Version::class,
|
||||
Business\Version::class,
|
||||
);
|
||||
|
||||
/**
|
||||
* Registers Polylang's options.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param Options $options Instance of the options.
|
||||
* @return void
|
||||
*/
|
||||
public static function register( Options $options ): void {
|
||||
array_map( array( $options, 'register' ), static::OPTIONS );
|
||||
}
|
||||
}
|
||||
706
wp-content/plugins/polylang/src/admin/admin-base.php
Normal file
706
wp-content/plugins/polylang/src/admin/admin-base.php
Normal file
@@ -0,0 +1,706 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
/**
|
||||
* Setup features available on all admin pages.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
abstract class PLL_Admin_Base extends PLL_Base {
|
||||
/**
|
||||
* @since 3.8
|
||||
*/
|
||||
public const SCREEN_PREFIX = 'languages';
|
||||
|
||||
/**
|
||||
* Current language (used to filter the content).
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Language selected in the admin language filter.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $filter_lang;
|
||||
|
||||
/**
|
||||
* Preferred language to assign to new contents.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $pref_lang;
|
||||
|
||||
/**
|
||||
* @var PLL_Filters_Links|null
|
||||
*/
|
||||
public $filters_links;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Links|null
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Notices|null
|
||||
*/
|
||||
public $notices;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Static_Pages|null
|
||||
*/
|
||||
public $static_pages;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Default_Term|null
|
||||
*/
|
||||
public $default_term;
|
||||
|
||||
/**
|
||||
* Setups actions needed on all admin pages.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param PLL_Links_Model $links_model Reference to the links model.
|
||||
*/
|
||||
public function __construct( &$links_model ) {
|
||||
parent::__construct( $links_model );
|
||||
|
||||
// Adds the link to the languages panel in the WordPress admin menu
|
||||
add_action( 'admin_menu', array( $this, 'add_menus' ) );
|
||||
|
||||
add_action( 'admin_menu', array( $this, 'remove_customize_submenu' ) );
|
||||
|
||||
// Setup js scripts and css styles
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), 0 ); // High priority in case an ajax request is sent by an immediately invoked function
|
||||
|
||||
add_action( 'customize_controls_enqueue_scripts', array( $this, 'customize_controls_enqueue_scripts' ) );
|
||||
|
||||
// Early instantiated to be able to correctly initialize language properties.
|
||||
$this->static_pages = new PLL_Admin_Static_Pages( $this );
|
||||
$this->model->set_languages_ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups filters and action needed on all admin pages and on plugins page
|
||||
* Loads the settings pages or the filters base on the request
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
$this->notices = new PLL_Admin_Notices( $this );
|
||||
|
||||
$this->default_term = new PLL_Admin_Default_Term( $this );
|
||||
$this->default_term->add_hooks();
|
||||
|
||||
if ( ! $this->model->has_languages() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->links = new PLL_Admin_Links( $this ); // FIXME needed here ?
|
||||
$this->filters_links = new PLL_Filters_Links( $this ); // FIXME needed here ?
|
||||
|
||||
// Filter admin language for users
|
||||
// We must not call user info before WordPress defines user roles in wp-settings.php
|
||||
add_action( 'setup_theme', array( $this, 'init_user' ) );
|
||||
add_filter( 'request', array( $this, 'request' ) );
|
||||
|
||||
// Adds the languages in admin bar
|
||||
add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 100 ); // 100 determines the position
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds links to Polylang's admin panels to the WordPress admin menu.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_menus(): void {
|
||||
global $admin_page_hooks;
|
||||
|
||||
$parent = '';
|
||||
$first_tab = '';
|
||||
|
||||
foreach ( $this->get_menu_items() as $tab => $title ) {
|
||||
$page = self::get_screen_slug( $tab );
|
||||
$capa = $this->get_menu_capability( $tab );
|
||||
|
||||
if ( empty( $parent ) ) {
|
||||
$parent = $page;
|
||||
$first_tab = $tab;
|
||||
|
||||
/*
|
||||
* WP actually doesn't care about the user capability used here, as long as it has sub-menus: it will
|
||||
* use the ones from the sub-menus. See `_wp_menu_output()`.
|
||||
* Ex: a user with `manage_translations` will still be able to access the Translations page, even if the
|
||||
* main menu has `manage_options`.
|
||||
*/
|
||||
add_menu_page( $title, __( 'Languages', 'polylang' ), $capa, $parent, '__return_null', 'dashicons-translation' );
|
||||
$admin_page_hooks[ $parent ] = self::SCREEN_PREFIX; // Hack to avoid the localization of the hook name. See: https://core.trac.wordpress.org/ticket/18857
|
||||
}
|
||||
|
||||
add_submenu_page( $parent, $title, $title, $capa, $page, array( $this, 'languages_page' ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* Get rid of the `toplevel` prefix in hook names.
|
||||
*
|
||||
* In the WP admin, if an admin screen is the first of its menu (like the PLL's "Languages" screen), the hooks
|
||||
* fired in the screen get a `toplevel` prefix (ex: `toplevel_page_mlang`) while all the other screens get a
|
||||
* slug based on the parent screen title (ex: `languages_page_mlang_strings`, where `languages` is the parent
|
||||
* screen's slug). This will not prevent the `toplevel` hooks to fire, but it will fire the `languages` hooks in
|
||||
* addition: this way, screens can be removed or moved around without the need of hooking both prefixes: using
|
||||
* the hooks with the `languages` prefix will work in both cases.
|
||||
*
|
||||
* @see get_plugin_page_hookname()
|
||||
*/
|
||||
foreach ( array( 'load-', 'admin_print_styles-', 'admin_print_scripts-', 'admin_head-', '', 'admin_print_footer_scripts-', 'admin_footer-' ) as $prefix ) {
|
||||
add_action(
|
||||
"{$prefix}toplevel_page_{$parent}",
|
||||
static function () use ( $prefix, $first_tab ) {
|
||||
do_action( $prefix . self::get_screen_id( $first_tab ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure a common CSS class to the `<body>` tag.
|
||||
*
|
||||
* Due to the `toplevel` "issue" described earlier, the CSS class `toplevel_page_mlang` (for example) is added
|
||||
* to the body. This adds a class with the `languages` prefix. This ensures we have a common CSS class, even if
|
||||
* the screen is moved to the 1st position in the menu.
|
||||
*/
|
||||
add_action(
|
||||
// Target the screen in 1st position only.
|
||||
"admin_head-toplevel_page_{$parent}",
|
||||
static function () use ( $first_tab ) {
|
||||
add_filter(
|
||||
'admin_body_class',
|
||||
static function ( $admin_body_classes ) use ( $first_tab ) {
|
||||
return $admin_body_classes . ' ' . self::get_screen_id( $first_tab );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Also modify the screen ID and base.
|
||||
*
|
||||
* Note: the global variables `$page_hook` and `$hook_suffix` are not changed, their value is still
|
||||
* `toplevel_page_mlang`. Changing them breaks things because we can't filter `get_plugin_page_hookname()`.
|
||||
* This is why the above hooks are still needed.
|
||||
*/
|
||||
add_action(
|
||||
'current_screen',
|
||||
static function ( $current_screen ) use ( $parent, $first_tab ) {
|
||||
if ( "toplevel_page_{$parent}" !== $current_screen->id ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$current_screen->id = self::get_screen_id( $first_tab );
|
||||
$current_screen->base = self::get_screen_id( $first_tab );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy method to display the 3 tabs pages: languages, strings translations, settings.
|
||||
* Overwritten in `PLL_Settings`.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function languages_page() {}
|
||||
|
||||
/**
|
||||
* Setup js scripts & css styles ( only on the relevant pages )
|
||||
*
|
||||
* @since 0.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function admin_enqueue_scripts() {
|
||||
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
|
||||
|
||||
wp_enqueue_script( 'pll_admin', plugins_url( "/js/build/admin{$suffix}.js", POLYLANG_ROOT_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
|
||||
$inline_script = sprintf( 'let pll_admin = %s;', wp_json_encode( array( 'ajax_filter' => $this->get_ajax_filter_data() ) ) );
|
||||
wp_add_inline_script( 'pll_admin', $inline_script, 'before' );
|
||||
|
||||
$screen = get_current_screen();
|
||||
if ( empty( $screen ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each script:
|
||||
* 0 => the pages on which to load the script
|
||||
* 1 => the scripts it needs to work
|
||||
* 2 => true if loaded even if languages have not been defined yet, false otherwise
|
||||
* 3 => true if loaded in footer
|
||||
*/
|
||||
$scripts = array(
|
||||
'widgets' => array( array( 'widgets' ), array( 'jquery' ), false, false ),
|
||||
);
|
||||
|
||||
$block_screens = array( 'widgets', 'site-editor' );
|
||||
|
||||
if ( ! empty( $screen->post_type ) && $this->model->is_translated_post_type( $screen->post_type ) ) {
|
||||
$scripts['post'] = array( array( 'edit' ), array( 'jquery', 'wp-ajax-response' ), false, true );
|
||||
|
||||
// Classic editor.
|
||||
if ( ! method_exists( $screen, 'is_block_editor' ) || ! $screen->is_block_editor() ) {
|
||||
$scripts['classic-editor'] = array( array( 'post', 'media', 'async-upload' ), array( 'jquery', 'wp-ajax-response', 'post', 'jquery-ui-dialog', 'wp-i18n' ), false, true );
|
||||
}
|
||||
|
||||
// Block editor with legacy metabox in WP 5.0+.
|
||||
$block_screens[] = 'post';
|
||||
}
|
||||
|
||||
if ( $this->options['media_support'] ) {
|
||||
$scripts['media'] = array( array( 'upload' ), array( 'jquery' ), false, true );
|
||||
}
|
||||
|
||||
if ( $this->is_block_editor( $screen ) ) {
|
||||
$scripts['block-editor'] = array( $block_screens, array( 'jquery', 'wp-ajax-response', 'wp-api-fetch', 'jquery-ui-dialog', 'wp-i18n' ), false, true );
|
||||
}
|
||||
|
||||
if ( ! empty( $screen->taxonomy ) && $this->model->is_translated_taxonomy( $screen->taxonomy ) ) {
|
||||
$scripts['term'] = array( array( 'edit-tags', 'term' ), array( 'jquery', 'wp-ajax-response', 'jquery-ui-autocomplete' ), false, true );
|
||||
}
|
||||
|
||||
foreach ( $scripts as $script => $v ) {
|
||||
if ( in_array( $screen->base, $v[0] ) && ( $v[2] || $this->model->has_languages() ) ) {
|
||||
wp_enqueue_script( "pll_{$script}", plugins_url( "/js/build/{$script}{$suffix}.js", POLYLANG_ROOT_FILE ), $v[1], POLYLANG_VERSION, $v[3] );
|
||||
if ( 'classic-editor' === $script || 'block-editor' === $script ) {
|
||||
wp_set_script_translations( "pll_{$script}", 'polylang' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wp_register_style( 'polylang_admin', plugins_url( "/css/build/admin{$suffix}.css", POLYLANG_ROOT_FILE ), array( 'wp-jquery-ui-dialog' ), POLYLANG_VERSION );
|
||||
wp_enqueue_style( 'polylang_dialog', plugins_url( "/css/build/dialog{$suffix}.css", POLYLANG_ROOT_FILE ), array( 'polylang_admin' ), POLYLANG_VERSION );
|
||||
|
||||
$this->add_inline_scripts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not the given screen is block editor kind.
|
||||
* e.g. widget, site or post editor.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param WP_Screen $screen Screen object.
|
||||
* @return bool True if the screen is a block editor, false otherwise.
|
||||
*/
|
||||
protected function is_block_editor( $screen ) {
|
||||
return method_exists( $screen, 'is_block_editor' ) && $screen->is_block_editor() && ! pll_use_block_editor_plugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts to the WP Customizer.
|
||||
*
|
||||
* @since 2.4.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function customize_controls_enqueue_scripts() {
|
||||
if ( $this->model->has_languages() ) {
|
||||
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
|
||||
wp_enqueue_script( 'pll_widgets', plugins_url( '/js/build/widgets' . $suffix . '.js', POLYLANG_ROOT_FILE ), array( 'jquery' ), POLYLANG_VERSION, true );
|
||||
$this->add_inline_scripts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inline scripts to set the default language in JS
|
||||
* and localizes scripts.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_inline_scripts() {
|
||||
if ( wp_script_is( 'pll_block-editor', 'enqueued' ) ) {
|
||||
$default_lang_script = 'const pllDefaultLanguage = "' . $this->options['default_lang'] . '";';
|
||||
wp_add_inline_script(
|
||||
'pll_block-editor',
|
||||
$default_lang_script,
|
||||
'before'
|
||||
);
|
||||
}
|
||||
if ( wp_script_is( 'pll_widgets', 'enqueued' ) ) {
|
||||
wp_localize_script(
|
||||
'pll_widgets',
|
||||
'pll_widgets',
|
||||
array(
|
||||
'flags' => wp_list_pluck( $this->model->get_languages_list(), 'flag', 'slug' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data to use with the AJAX filter.
|
||||
* The final goal is to detect if an ajax request is made on admin or frontend.
|
||||
*
|
||||
* Takes care to various situations:
|
||||
* - When the AJAX request has no `options.data` thanks to ScreenfeedFr.
|
||||
* See: https://wordpress.org/support/topic/ajaxprefilter-may-not-work-as-expected.
|
||||
* - When `options.data` is a JSON string.
|
||||
* See: https://wordpress.org/support/topic/polylang-breaking-third-party-ajax-requests-on-admin-panels.
|
||||
* - When `options.data` is an empty string (GET request with the method 'load').
|
||||
* See: https://wordpress.org/support/topic/invalid-url-during-wordpress-new-dashboard-widget-operation.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_ajax_filter_data(): array {
|
||||
global $post, $tag;
|
||||
|
||||
$params = array( 'pll_ajax_backend' => 1 );
|
||||
if ( $post instanceof WP_Post && $this->model->post_types->is_translated( $post->post_type ) ) {
|
||||
$params['pll_post_id'] = $post->ID;
|
||||
}
|
||||
|
||||
if ( $tag instanceof WP_Term && $this->model->taxonomies->is_translated( $tag->taxonomy ) ) {
|
||||
$params['pll_term_id'] = $tag->term_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the list of parameters to add to the admin ajax request.
|
||||
*
|
||||
* @since 3.4.5
|
||||
*
|
||||
* @param array $params List of parameters to add to the admin ajax request.
|
||||
*/
|
||||
return (array) apply_filters( 'pll_admin_ajax_params', $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the admin current language, used to filter the content
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_current_language() {
|
||||
$this->curlang = $this->filter_lang;
|
||||
|
||||
// Edit Post
|
||||
if ( isset( $_REQUEST['pll_post_id'] ) && $lang = $this->model->post->get_language( (int) $_REQUEST['pll_post_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->curlang = $lang;
|
||||
} elseif ( 'post.php' === $GLOBALS['pagenow'] && isset( $_GET['post'] ) && $this->model->is_translated_post_type( get_post_type( (int) $_GET['post'] ) ) && $lang = $this->model->post->get_language( (int) $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->curlang = $lang;
|
||||
} elseif ( 'post-new.php' === $GLOBALS['pagenow'] && ( empty( $_GET['post_type'] ) || $this->model->is_translated_post_type( sanitize_key( $_GET['post_type'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->curlang = empty( $_GET['new_lang'] ) ? $this->pref_lang : $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
// Edit Term
|
||||
elseif ( isset( $_REQUEST['pll_term_id'] ) && $lang = $this->model->term->get_language( (int) $_REQUEST['pll_term_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->curlang = $lang;
|
||||
} elseif ( in_array( $GLOBALS['pagenow'], array( 'edit-tags.php', 'term.php' ) ) && isset( $_GET['taxonomy'] ) && $this->model->is_translated_taxonomy( sanitize_key( $_GET['taxonomy'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
if ( isset( $_GET['tag_ID'] ) && $lang = $this->model->term->get_language( (int) $_GET['tag_ID'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->curlang = $lang;
|
||||
} elseif ( ! empty( $_GET['new_lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->curlang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
} elseif ( empty( $this->curlang ) ) {
|
||||
$this->curlang = $this->pref_lang;
|
||||
}
|
||||
}
|
||||
|
||||
// Ajax
|
||||
if ( wp_doing_ajax() && ! empty( $_REQUEST['lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->curlang = $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the current language used by Polylang in the admin context.
|
||||
*
|
||||
* @since 3.2
|
||||
*
|
||||
* @param PLL_Language|false|null $curlang Instance of the current language.
|
||||
* @param PLL_Admin_Base $polylang Instance of the main Polylang's object.
|
||||
*/
|
||||
$this->curlang = apply_filters( 'pll_admin_current_language', $this->curlang, $this );
|
||||
|
||||
// Inform that the admin language has been set.
|
||||
if ( $this->curlang instanceof PLL_Language ) {
|
||||
/** This action is documented in src/frontend/choose-lang.php */
|
||||
do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
|
||||
} else {
|
||||
/** This action is documented in src/class-polylang.php */
|
||||
do_action( 'pll_no_language_defined' ); // To load overridden textdomains.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the backend language and the admin language filter based on user preferences.
|
||||
*
|
||||
* @since 1.2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_user() {
|
||||
/*
|
||||
* $_GET['lang'] is numeric when editing a language, not when selecting a new language in the filter.
|
||||
* We intentionally don't use a nonce to update the language filter.
|
||||
*/
|
||||
if ( ! wp_doing_ajax() && ! empty( $_GET['lang'] ) && ! is_numeric( sanitize_key( $_GET['lang'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$user_id = get_current_user_id();
|
||||
if ( current_user_can( 'edit_user', $user_id ) ) {
|
||||
$lang = $this->model->get_language( sanitize_key( $_GET['lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
update_user_meta( $user_id, 'pll_filter_content', $lang ? $lang->slug : '' );
|
||||
}
|
||||
}
|
||||
|
||||
$this->filter_lang = $this->model->get_language( get_user_meta( get_current_user_id(), 'pll_filter_content', true ) );
|
||||
|
||||
// Set preferred language for use when saving posts and terms: must not be empty.
|
||||
$this->pref_lang = empty( $this->filter_lang ) ? $this->model->get_default_language() : $this->filter_lang;
|
||||
|
||||
/**
|
||||
* Filters the preferred language on admin side.
|
||||
* The preferred language is used for example to determine the language of a new post.
|
||||
*
|
||||
* @since 1.2.3
|
||||
*
|
||||
* @param PLL_Language $pref_lang Preferred language.
|
||||
*/
|
||||
$this->pref_lang = apply_filters( 'pll_admin_preferred_language', $this->pref_lang );
|
||||
|
||||
$this->set_current_language();
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoids parsing a tax query when all languages are requested
|
||||
* Fixes https://wordpress.org/support/topic/notice-undefined-offset-0-in-wp-includesqueryphp-on-line-3877 introduced in WP 4.1
|
||||
*
|
||||
* @see https://core.trac.wordpress.org/ticket/31246 the suggestion of @boonebgorges.
|
||||
*
|
||||
* @since 1.6.5
|
||||
*
|
||||
* @param array $qvars The array of requested query variables.
|
||||
* @return array
|
||||
*/
|
||||
public function request( $qvars ) {
|
||||
if ( isset( $qvars['lang'] ) && 'all' === $qvars['lang'] ) {
|
||||
unset( $qvars['lang'] );
|
||||
}
|
||||
|
||||
return $qvars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the languages list in admin bar for the admin languages filter.
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar global object.
|
||||
* @return void
|
||||
*/
|
||||
public function admin_bar_menu( $wp_admin_bar ) {
|
||||
$all_item = (object) array(
|
||||
'slug' => 'all',
|
||||
'name' => __( 'Show all languages', 'polylang' ),
|
||||
'flag' => '<span class="ab-icon"></span>',
|
||||
);
|
||||
|
||||
$selected = empty( $this->filter_lang ) ? $all_item : $this->filter_lang;
|
||||
|
||||
$title = sprintf(
|
||||
'<span class="ab-label"%1$s><span class="screen-reader-text">%2$s</span>%3$s</span>',
|
||||
$selected instanceof PLL_Language ? sprintf( ' lang="%s"', esc_attr( $selected->get_locale( 'display' ) ) ) : '',
|
||||
__( 'Filters content by language', 'polylang' ),
|
||||
esc_html( $selected->name )
|
||||
);
|
||||
|
||||
$all_items = array_merge( array( $all_item ), $this->model->get_languages_list() );
|
||||
$items = $all_items;
|
||||
|
||||
if ( $this->should_hide_admin_bar_menu() ) {
|
||||
$items = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the admin bar language filter submenu items.
|
||||
*
|
||||
* @since 2.6
|
||||
* @since 3.8 Added `$all_items` parameter.
|
||||
*
|
||||
* @param array $items The items of the admin languages filter to display (may be empty if menu hidden).
|
||||
* @param array $all_items Complete unfiltered list of all available language items.
|
||||
*/
|
||||
$items = apply_filters( 'pll_admin_languages_filter', $items, $all_items );
|
||||
|
||||
if ( empty( $items ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wp_admin_bar->add_menu(
|
||||
array(
|
||||
'id' => 'languages',
|
||||
'title' => $selected->flag . $title,
|
||||
'href' => esc_url( add_query_arg( 'lang', $selected->slug, remove_query_arg( 'paged' ) ) ),
|
||||
'meta' => array(
|
||||
'title' => __( 'Filters content by language', 'polylang' ),
|
||||
'class' => 'all' === $selected->slug ? '' : 'pll-filtered-languages',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( $items as $lang ) {
|
||||
if ( $selected->slug === $lang->slug ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wp_admin_bar->add_menu(
|
||||
array(
|
||||
'parent' => 'languages',
|
||||
'id' => $lang->slug,
|
||||
'title' => $lang->flag . esc_html( $lang->name ),
|
||||
'href' => esc_url( add_query_arg( 'lang', $lang->slug, remove_query_arg( 'paged' ) ) ),
|
||||
'meta' => 'all' === $lang->slug ? array() : array( 'lang' => esc_attr( $lang->get_locale( 'display' ) ) ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the customize submenu when using a block theme.
|
||||
*
|
||||
* WordPress removes the Customizer menu if a block theme is activated and no other plugins interact with it.
|
||||
* As Polylang interacts with the Customizer, we have to delete this menu ourselves in the case of a block theme,
|
||||
* unless another plugin than Polylang interacts with the Customizer.
|
||||
*
|
||||
* @since 3.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove_customize_submenu() {
|
||||
if ( ! $this->should_customize_menu_be_removed() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $submenu;
|
||||
|
||||
if ( ! empty( $submenu['themes.php'] ) ) {
|
||||
foreach ( $submenu['themes.php'] as $submenu_item ) {
|
||||
if ( 'customize' === $submenu_item[1] ) {
|
||||
remove_submenu_page( 'themes.php', $submenu_item[2] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the Polylang's admin bar menu should be hidden for the current page.
|
||||
* Conventionally, it should be hidden on edition pages.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_hide_admin_bar_menu(): bool {
|
||||
global $pagenow, $typenow, $taxnow;
|
||||
|
||||
if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ), true ) ) {
|
||||
return ! empty( $typenow );
|
||||
}
|
||||
|
||||
if ( 'term.php' === $pagenow ) {
|
||||
return ! empty( $taxnow );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of a Polylang's settings screen.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $tab The name of the screen (`lang`, `strings`, `settings`).
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return non-empty-string
|
||||
*/
|
||||
public static function get_screen_id( string $tab ): string {
|
||||
return sprintf( '%s_page_%s', self::SCREEN_PREFIX, self::get_screen_slug( $tab ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the slug of a Polylang's settings screen, as seen in the URL.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $tab The name of the screen (`lang`, `strings`, `settings`).
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-return non-empty-string
|
||||
*/
|
||||
public static function get_screen_slug( string $tab ): string {
|
||||
return 'lang' === $tab ? 'mlang' : "mlang_$tab";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of sub-menu items.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string[] List of sub-menu items with page slugs as array keys, and sub-menu titles as array values.
|
||||
*
|
||||
* @phpstan-return array<non-empty-string, string>
|
||||
*/
|
||||
protected function get_menu_items(): array {
|
||||
$tabs = array(
|
||||
'lang' => __( 'Languages', 'polylang' ),
|
||||
);
|
||||
|
||||
// Only if at least one language has been created.
|
||||
if ( ! empty( $this->model->languages->filter( 'translator' )->get_list() ) ) {
|
||||
$tabs['strings'] = __( 'Translations', 'polylang' );
|
||||
}
|
||||
|
||||
$tabs['settings'] = __( 'Settings', 'polylang' );
|
||||
|
||||
/**
|
||||
* Filter the list of sub-menu items in Polylang settings.
|
||||
*
|
||||
* @since 1.5.1
|
||||
*
|
||||
* @param string[] $tabs List of sub-menu items with page slugs as array keys and titles as array values.
|
||||
*/
|
||||
return (array) apply_filters( 'pll_settings_tabs', $tabs );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user capability required to access the given menu page.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $menu Menu slug.
|
||||
* @return string
|
||||
*/
|
||||
protected function get_menu_capability( string $menu ): string {
|
||||
switch ( $menu ) {
|
||||
case 'lang':
|
||||
return Capabilities::LANGUAGES;
|
||||
|
||||
case 'strings':
|
||||
return Capabilities::TRANSLATIONS;
|
||||
}
|
||||
|
||||
return 'manage_options';
|
||||
}
|
||||
}
|
||||
88
wp-content/plugins/polylang/src/admin/admin-block-editor.php
Normal file
88
wp-content/plugins/polylang/src/admin/admin-block-editor.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages filters and actions related to the block editor
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
class PLL_Admin_Block_Editor {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @var PLL_Filter_REST_Routes
|
||||
*/
|
||||
public $filter_rest_routes;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param PLL_Admin $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->model = &$polylang->model;
|
||||
$this->filter_rest_routes = new PLL_Filter_REST_Routes( $polylang->model );
|
||||
|
||||
add_filter( 'block_editor_rest_api_preload_paths', array( $this, 'filter_preload_paths' ), 50, 2 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'add_block_editor_inline_script' ), 15 ); // After `PLL_Admin_Base::admin_enqueue_scripts()` to ensure `pll_block-editor`script is enqueued.
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters preload paths based on the context (block editor for posts, site editor or widget editor for instance).
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param array $preload_paths Preload paths.
|
||||
* @param WP_Block_Editor_Context $context Editor context.
|
||||
* @return array Filtered preload paths.
|
||||
*/
|
||||
public function filter_preload_paths( $preload_paths, $context ) {
|
||||
if ( ! $context instanceof WP_Block_Editor_Context ) {
|
||||
return $preload_paths;
|
||||
}
|
||||
|
||||
if ( 'core/edit-post' !== $context->name || ! $context->post instanceof WP_Post ) {
|
||||
// Do nothing if not post editor.
|
||||
return $preload_paths;
|
||||
}
|
||||
|
||||
if ( ! $this->model->is_translated_post_type( $context->post->post_type ) ) {
|
||||
return $preload_paths;
|
||||
}
|
||||
|
||||
$language = $this->model->post->get_language( $context->post->ID );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return $preload_paths;
|
||||
}
|
||||
|
||||
return $this->filter_rest_routes->add_query_parameters(
|
||||
$preload_paths,
|
||||
array(
|
||||
'lang' => $language->slug,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inline block editor script for filterable REST routes.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_block_editor_inline_script() {
|
||||
$handle = 'pll_block-editor';
|
||||
|
||||
if ( wp_script_is( $handle, 'enqueued' ) ) {
|
||||
$this->filter_rest_routes->add_inline_script( $handle );
|
||||
}
|
||||
}
|
||||
}
|
||||
385
wp-content/plugins/polylang/src/admin/admin-classic-editor.php
Normal file
385
wp-content/plugins/polylang/src/admin/admin-classic-editor.php
Normal file
@@ -0,0 +1,385 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Manages filters and actions related to the classic editor
|
||||
*
|
||||
* @since 2.4
|
||||
*/
|
||||
class PLL_Admin_Classic_Editor {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Links
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* Current language (used to filter the content).
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Preferred language to assign to new contents.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $pref_lang;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->model = &$polylang->model;
|
||||
$this->links = &$polylang->links;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
$this->pref_lang = &$polylang->pref_lang;
|
||||
|
||||
// Adds the Languages box in the 'Edit Post' and 'Edit Page' panels
|
||||
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
|
||||
|
||||
// Ajax response for changing the language in the post metabox
|
||||
add_action( 'wp_ajax_post_lang_choice', array( $this, 'post_lang_choice' ) );
|
||||
add_action( 'wp_ajax_pll_posts_not_translated', array( $this, 'ajax_posts_not_translated' ) );
|
||||
|
||||
// Filters the pages by language in the parent dropdown list in the page attributes metabox
|
||||
add_filter( 'page_attributes_dropdown_pages_args', array( $this, 'page_attributes_dropdown_pages_args' ), 10, 2 );
|
||||
|
||||
// Notice
|
||||
add_action( 'edit_form_top', array( $this, 'edit_form_top' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Language box in the 'Edit Post' and 'Edit Page' panels ( as well as in custom post types panels )
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string $post_type Current post type
|
||||
* @return void
|
||||
*/
|
||||
public function add_meta_boxes( $post_type ) {
|
||||
if ( $this->model->is_translated_post_type( $post_type ) ) {
|
||||
add_meta_box(
|
||||
'ml_box',
|
||||
__( 'Languages', 'polylang' ),
|
||||
array( $this, 'post_language' ),
|
||||
$post_type,
|
||||
'side',
|
||||
'high',
|
||||
array(
|
||||
'__back_compat_meta_box' => pll_use_block_editor_plugin(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the Languages metabox in the 'Edit Post' and 'Edit Page' panels
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param WP_Post $post Current post object.
|
||||
* @return void
|
||||
*/
|
||||
public function post_language( WP_Post $post ): void {
|
||||
$post_type = $post->post_type;
|
||||
$from_post_id = 0;
|
||||
$lang = $this->model->post->get_language( $post->ID );
|
||||
$new_post_data = $this->links->get_data_from_new_post_translation_request();
|
||||
|
||||
if ( ! empty( $new_post_data ) ) {
|
||||
$from_post_id = $new_post_data['from_post']->ID;
|
||||
$lang = $new_post_data['new_lang'];
|
||||
}
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
$lang = $this->pref_lang;
|
||||
}
|
||||
|
||||
$dropdown = new PLL_Walker_Dropdown();
|
||||
|
||||
$id = ( 'attachment' === $post_type ) ? sprintf( 'attachments[%d][language]', (int) $post->ID ) : 'post_lang_choice';
|
||||
|
||||
$dropdown_html = $dropdown->walk(
|
||||
$this->model->languages->filter( 'translator' )->get_list(),
|
||||
-1,
|
||||
array(
|
||||
'name' => $id,
|
||||
'class' => 'post_lang_choice tags-input',
|
||||
'selected' => $lang ? $lang->slug : '',
|
||||
'flag' => true,
|
||||
)
|
||||
);
|
||||
|
||||
wp_nonce_field( 'pll_language', '_pll_nonce' );
|
||||
|
||||
// NOTE: the class "tags-input" allows to include the field in the autosave $_POST ( see autosave.js )
|
||||
printf(
|
||||
'<p><strong>%1$s</strong></p>
|
||||
<label class="screen-reader-text" for="%2$s">%1$s</label>
|
||||
<div id="select-%3$s-language">%4$s</div>',
|
||||
esc_html__( 'Language', 'polylang' ),
|
||||
esc_attr( $id ),
|
||||
( 'attachment' === $post_type ? 'media' : 'post' ),
|
||||
$dropdown_html // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
);
|
||||
|
||||
/**
|
||||
* Fires before displaying the list of translations in the Languages metabox for posts.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string $post_type The post type.
|
||||
*/
|
||||
do_action( 'pll_before_post_translations', $post_type );
|
||||
|
||||
echo '<div id="post-translations" class="translations">';
|
||||
if ( $lang ) {
|
||||
if ( 'attachment' === $post_type ) {
|
||||
include __DIR__ . '/view-translations-media.php';
|
||||
} else {
|
||||
include __DIR__ . '/view-translations-post.php';
|
||||
}
|
||||
}
|
||||
echo '</div>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax response for changing the language in the post metabox
|
||||
*
|
||||
* @since 0.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function post_lang_choice() {
|
||||
check_ajax_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
if ( ! isset( $_POST['post_id'], $_POST['lang'], $_POST['post_type'] ) ) {
|
||||
wp_die( 'The request is missing the parameter "post_type", "lang" and/or "post_id".' );
|
||||
}
|
||||
|
||||
global $post_ID; // Obliged to use the global variable for wp_popular_terms_checklist().
|
||||
$post_ID = (int) $_POST['post_id'];
|
||||
$post = get_post( $post_ID );
|
||||
|
||||
if ( ! $post instanceof WP_Post ) {
|
||||
wp_die( esc_html( "Invalid post ID {$post_ID}." ) );
|
||||
}
|
||||
|
||||
$lang_slug = sanitize_key( $_POST['lang'] );
|
||||
$lang = $this->model->get_language( $lang_slug );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
wp_die( esc_html( "{$lang_slug} is not a valid language code." ) );
|
||||
}
|
||||
|
||||
Capabilities::get_user()->can_translate_or_die( $lang );
|
||||
|
||||
$post_type_object = get_post_type_object( $post->post_type );
|
||||
|
||||
if ( empty( $post_type_object ) ) {
|
||||
wp_die( esc_html( "{$post->post_type} is not a valid post type." ) );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( $post_type_object->cap->edit_post, $post->ID ) ) {
|
||||
wp_die( 'You are not allowed to edit this post.' );
|
||||
}
|
||||
|
||||
$this->model->post->set_language( $post->ID, $lang );
|
||||
|
||||
ob_start();
|
||||
if ( 'attachment' === $post->post_type ) {
|
||||
include __DIR__ . '/view-translations-media.php';
|
||||
} else {
|
||||
include __DIR__ . '/view-translations-post.php';
|
||||
}
|
||||
$x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
|
||||
ob_end_clean();
|
||||
|
||||
// Categories
|
||||
if ( isset( $_POST['taxonomies'] ) ) { // Not set for pages
|
||||
$supplemental = array();
|
||||
|
||||
foreach ( array_map( 'sanitize_key', $_POST['taxonomies'] ) as $taxname ) {
|
||||
$taxonomy = get_taxonomy( $taxname );
|
||||
|
||||
if ( ! empty( $taxonomy ) ) {
|
||||
ob_start();
|
||||
$popular_ids = wp_popular_terms_checklist( $taxonomy->name );
|
||||
$supplemental['populars'] = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
ob_start();
|
||||
// Use $post->ID to remember checked terms in case we come back to the original language
|
||||
wp_terms_checklist( $post->ID, array( 'taxonomy' => $taxonomy->name, 'popular_cats' => $popular_ids ) );
|
||||
$supplemental['all'] = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
$supplemental['dropdown'] = wp_dropdown_categories(
|
||||
array(
|
||||
'taxonomy' => $taxonomy->name,
|
||||
'hide_empty' => 0,
|
||||
'name' => 'new' . $taxonomy->name . '_parent',
|
||||
'orderby' => 'name',
|
||||
'hierarchical' => 1,
|
||||
'show_option_none' => '— ' . $taxonomy->labels->parent_item . ' —',
|
||||
'echo' => 0,
|
||||
)
|
||||
);
|
||||
|
||||
$x->Add( array( 'what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parent dropdown list ( only for hierarchical post types )
|
||||
if ( in_array( $post->post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
|
||||
// Args and filter from 'page_attributes_meta_box' in wp-admin/includes/meta-boxes.php of WP 4.2.1
|
||||
$dropdown_args = array(
|
||||
'post_type' => $post->post_type,
|
||||
'exclude_tree' => $post->ID,
|
||||
'selected' => $post->post_parent,
|
||||
'name' => 'parent_id',
|
||||
'show_option_none' => __( '(no parent)', 'polylang' ),
|
||||
'sort_column' => 'menu_order, post_title',
|
||||
'echo' => 0,
|
||||
);
|
||||
|
||||
/** This filter is documented in wp-admin/includes/meta-boxes.php */
|
||||
$dropdown_args = (array) apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); // Since WP 3.3.
|
||||
$dropdown_args['echo'] = 0; // Make sure to not print it.
|
||||
|
||||
/** @var string $data */
|
||||
$data = wp_dropdown_pages( $dropdown_args ); // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
|
||||
$x->Add( array( 'what' => 'pages', 'data' => $data ) );
|
||||
}
|
||||
|
||||
// Flag
|
||||
$x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
|
||||
|
||||
// Sample permalink
|
||||
$x->Add( array( 'what' => 'permalink', 'data' => get_sample_permalink_html( $post->ID ) ) );
|
||||
|
||||
$x->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax response for input in translation autocomplete input box
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_posts_not_translated() {
|
||||
check_ajax_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
if ( ! isset( $_GET['post_type'], $_GET['post_language'], $_GET['translation_language'], $_GET['term'], $_GET['pll_post_id'] ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
$post_type = sanitize_key( $_GET['post_type'] );
|
||||
|
||||
if ( ! post_type_exists( $post_type ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
$term = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
|
||||
$post_language = $this->model->get_language( sanitize_key( $_GET['post_language'] ) );
|
||||
$translation_language = $this->model->get_language( sanitize_key( $_GET['translation_language'] ) );
|
||||
|
||||
$return = array();
|
||||
|
||||
$untranslated_posts = $this->model->post->get_untranslated( $post_type, $post_language, $translation_language, $term );
|
||||
|
||||
// format output
|
||||
foreach ( $untranslated_posts as $post ) {
|
||||
$return[] = array(
|
||||
'id' => $post->ID,
|
||||
'value' => $post->post_title,
|
||||
'link' => $this->links->get_edit_post_link_html( $post ),
|
||||
);
|
||||
}
|
||||
|
||||
// Add current translation in list
|
||||
if ( $post_id = $this->model->post->get_translation( (int) $_GET['pll_post_id'], $translation_language ) ) {
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( ! empty( $post ) ) {
|
||||
array_unshift(
|
||||
$return,
|
||||
array(
|
||||
'id' => $post_id,
|
||||
'value' => $post->post_title,
|
||||
'link' => $this->links->get_edit_post_link_html( $post ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
wp_die( wp_json_encode( $return ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the pages by language in the parent dropdown list in the page attributes metabox.
|
||||
*
|
||||
* @since 0.6
|
||||
*
|
||||
* @param array $dropdown_args Arguments passed to wp_dropdown_pages().
|
||||
* @param WP_Post $post The page being edited.
|
||||
* @return array Modified arguments.
|
||||
*/
|
||||
public function page_attributes_dropdown_pages_args( $dropdown_args, $post ) {
|
||||
$language = isset( $_POST['lang'] ) ? $this->model->get_language( sanitize_key( $_POST['lang'] ) ) : $this->model->post->get_language( $post->ID ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
$language = $this->pref_lang;
|
||||
}
|
||||
|
||||
if ( ! empty( $language ) ) {
|
||||
$dropdown_args['lang'] = $language->slug;
|
||||
}
|
||||
|
||||
return $dropdown_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notice if the user has not sufficient rights to overwrite synchronized taxonomies and metas.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param WP_Post $post the post currently being edited.
|
||||
* @return void
|
||||
*/
|
||||
public function edit_form_top( $post ) {
|
||||
if ( ! $this->model->post->current_user_can_synchronize( $post->ID ) ) {
|
||||
?>
|
||||
<div class="pll-notice notice notice-warning">
|
||||
<p>
|
||||
<?php
|
||||
esc_html_e( 'Some taxonomies or metadata may be synchronized with existing translations that you are not allowed to modify.', 'polylang' );
|
||||
echo ' ';
|
||||
esc_html_e( 'If you attempt to modify them anyway, your changes will not be saved.', 'polylang' );
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
48
wp-content/plugins/polylang/src/admin/admin-default-term.php
Normal file
48
wp-content/plugins/polylang/src/admin/admin-default-term.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages filters and actions related to default terms.
|
||||
*
|
||||
* @since 3.1
|
||||
* @since 3.7 Extends `PLL_Default_Term`, most of the code is moved to it.
|
||||
*/
|
||||
class PLL_Admin_Default_Term extends PLL_Default_Term {
|
||||
|
||||
/**
|
||||
* Setups filters and actions needed.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_hooks() {
|
||||
parent::add_hooks();
|
||||
|
||||
foreach ( $this->taxonomies as $taxonomy ) {
|
||||
if ( 'category' === $taxonomy ) {
|
||||
// Adds the language column in the 'Terms' table.
|
||||
add_filter( 'pll_first_language_term_column', array( $this, 'first_language_column' ), 10, 2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the default term in the terms list table to disable the language dropdown in JS.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $out The output.
|
||||
* @param int $term_id The term id.
|
||||
* @return string The HTML string.
|
||||
*/
|
||||
public function first_language_column( $out, $term_id ) {
|
||||
if ( $this->is_default_term( $term_id ) ) {
|
||||
$out .= sprintf( '<div class="hidden" id="default_cat_%1$d">%1$d</div>', intval( $term_id ) );
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
418
wp-content/plugins/polylang/src/admin/admin-filters-columns.php
Normal file
418
wp-content/plugins/polylang/src/admin/admin-filters-columns.php
Normal file
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds the language column in posts and terms list tables.
|
||||
* Manages quick edit and bulk edit as well.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin_Filters_Columns {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* This class is instantiated on `wp_loaded` (prio 5), see `PLL_Admin::add_filters()`.
|
||||
* `$polylang->links` is instantiated on `wp_loaded` (prio 1), see `PLL_Admin_Base::init()`.
|
||||
* This means `$polylang->links` cannot be `null`.
|
||||
*
|
||||
* @var PLL_Admin_Links
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* Language selected in the admin language filter.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $filter_lang;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->links = &$polylang->links;
|
||||
$this->model = &$polylang->model;
|
||||
$this->filter_lang = &$polylang->filter_lang;
|
||||
|
||||
// Hide the column of the filtered language.
|
||||
add_filter( 'hidden_columns', array( $this, 'hidden_columns' ) ); // Since WP 4.4.
|
||||
|
||||
// Add the language and translations columns in 'All Posts', 'All Pages' and 'Media library' panels.
|
||||
foreach ( $this->model->get_translated_post_types() as $type ) {
|
||||
// Use the latest filter late as some plugins purely overwrite what's done by others :(
|
||||
// Specific case for media.
|
||||
add_filter( 'manage_' . ( 'attachment' == $type ? 'upload' : 'edit-' . $type ) . '_columns', array( $this, 'add_post_column' ), 100 );
|
||||
add_action( 'manage_' . ( 'attachment' == $type ? 'media' : $type . '_posts' ) . '_custom_column', array( $this, 'post_column' ), 10, 2 );
|
||||
}
|
||||
|
||||
// Quick edit and bulk edit.
|
||||
add_filter( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
|
||||
add_filter( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ) );
|
||||
|
||||
// Adds the language column in the 'Categories' and 'Post Tags' tables.
|
||||
foreach ( $this->model->get_translated_taxonomies() as $tax ) {
|
||||
add_filter( 'manage_edit-' . $tax . '_columns', array( $this, 'add_term_column' ) );
|
||||
add_filter( 'manage_' . $tax . '_custom_column', array( $this, 'term_column' ), 10, 3 );
|
||||
}
|
||||
|
||||
// Ajax responses to update list table rows.
|
||||
add_action( 'wp_ajax_pll_update_post_rows', array( $this, 'ajax_update_post_rows' ) );
|
||||
add_action( 'wp_ajax_pll_update_term_rows', array( $this, 'ajax_update_term_rows' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds languages and translations columns in posts, pages, media, categories and tags tables.
|
||||
*
|
||||
* @since 0.8.2
|
||||
*
|
||||
* @param string[] $columns List of table columns.
|
||||
* @param string $before The column before which we want to add our languages.
|
||||
* @return string[] Modified list of columns.
|
||||
*/
|
||||
protected function add_column( array $columns, string $before ): array {
|
||||
if ( $n = array_search( $before, array_keys( $columns ) ) ) {
|
||||
$end = array_slice( $columns, $n );
|
||||
$columns = array_slice( $columns, 0, $n );
|
||||
}
|
||||
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
$columns[ 'language_' . $language->slug ] = $this->links->get_flag_html( $language ) . '<span class="screen-reader-text">' . esc_html( $language->name ) . '</span>';
|
||||
}
|
||||
|
||||
return isset( $end ) ? array_merge( $columns, $end ) : $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first language column in posts, pages, media, categories and tags tables.
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @return string First language column name.
|
||||
*/
|
||||
protected function get_first_language_column(): string {
|
||||
$languages = $this->model->get_languages_list();
|
||||
$language = reset( $languages );
|
||||
return ! empty( $language ) ? "language_{$language->slug}" : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the column for the filtered language.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @param string[] $hidden Array of hidden columns.
|
||||
* @return string[]
|
||||
*/
|
||||
public function hidden_columns( $hidden ) {
|
||||
if ( ! empty( $this->filter_lang ) ) {
|
||||
$hidden[] = 'language_' . $this->filter_lang->slug;
|
||||
}
|
||||
return $hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language and translations columns ( before the comments column ) in the posts, pages and media library tables.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string[] $columns List of posts table columns.
|
||||
* @return string[] Modified list of columns.
|
||||
*/
|
||||
public function add_post_column( $columns ) {
|
||||
return $this->add_column( (array) $columns, 'comments' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the language and translations columns in the posts, pages and media library tables
|
||||
* take care that when doing ajax inline edit, the post may not be updated in database yet.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string $column Column name.
|
||||
* @param int $post_id Post ID.
|
||||
* @return void
|
||||
*/
|
||||
public function post_column( $column, $post_id ): void {
|
||||
if ( ! str_starts_with( $column, 'language_' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post = get_post( (int) $post_id );
|
||||
|
||||
if ( ! $post instanceof WP_Post ) {
|
||||
// Should not happen.
|
||||
return;
|
||||
}
|
||||
|
||||
$inline = isset( $_POST['inline_lang_choice'], $_REQUEST['_inline_edit'] ) && is_string( $_REQUEST['_inline_edit'] ) && wp_verify_nonce( $_REQUEST['_inline_edit'], 'inlineeditnonce' );
|
||||
$lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->post->get_language( $post->ID );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$language = $this->model->get_language( substr( $column, 9 ) );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hidden field containing the post language for quick edit.
|
||||
if ( $column === $this->get_first_language_column() ) {
|
||||
printf( '<div class="hidden" id="lang_%d">%s</div>', (int) $post->ID, esc_html( $lang->slug ) );
|
||||
}
|
||||
|
||||
$tr_id = $this->model->post->get( $post->ID, $language );
|
||||
$tr_post = $tr_id ? get_post( $tr_id ) : null;
|
||||
|
||||
if ( ! $tr_post instanceof WP_Post ) {
|
||||
// Link to add a new translation: no translation for this language yet, or it doesn't exist anymore.
|
||||
echo $this->links->get_new_post_link_html( $post, $language ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
return;
|
||||
}
|
||||
|
||||
// Link to edit (or not) the post or a translation.
|
||||
echo $this->links->get_edit_post_link_html( $tr_post, $tr_post->ID === $post->ID ? 'list_current' : 'list_translation' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick edit & bulk edit.
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @param string $column Column name.
|
||||
* @return string Unmodified column.
|
||||
*/
|
||||
public function quick_edit_custom_box( $column ) {
|
||||
if ( $column !== $this->get_first_language_column() ) {
|
||||
return $column;
|
||||
}
|
||||
|
||||
$elements = $this->model->languages->filter( 'translator' )->get_list();
|
||||
if ( current_filter() == 'bulk_edit_custom_box' ) {
|
||||
array_unshift( $elements, (object) array( 'slug' => -1, 'name' => __( '— No Change —', 'polylang' ) ) );
|
||||
}
|
||||
|
||||
$dropdown = new PLL_Walker_Dropdown();
|
||||
printf(
|
||||
'<fieldset class="inline-edit-col-left">
|
||||
<div class="inline-edit-col">
|
||||
<label class="alignleft">
|
||||
<span class="title">%s</span>
|
||||
%s
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>',
|
||||
esc_html__( 'Language', 'polylang' ),
|
||||
$dropdown->walk( $elements, -1, array( 'name' => 'inline_lang_choice', 'id' => '' ) ) // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
);
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language column (before the posts column) in the 'Categories' or 'Post Tags' table.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string[] $columns List of terms table columns.
|
||||
* @return string[] modified List of columns.
|
||||
*/
|
||||
public function add_term_column( $columns ) {
|
||||
$screen = get_current_screen();
|
||||
|
||||
// Avoid displaying languages in screen options when editing a term.
|
||||
if ( $screen instanceof WP_Screen && 'term' === $screen->base ) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
return $this->add_column( (array) $columns, 'posts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the language column in the taxonomy terms list table.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string $out Column output.
|
||||
* @param string $column Column name.
|
||||
* @param int $term_id Term ID.
|
||||
* @return string
|
||||
*/
|
||||
public function term_column( $out, $column, $term_id ) {
|
||||
if ( ! str_starts_with( $column, 'language_' ) ) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
$term_id = (int) $term_id;
|
||||
$inline = isset( $_POST['inline_lang_choice'], $_REQUEST['_inline_edit'] ) && is_string( $_REQUEST['_inline_edit'] ) && wp_verify_nonce( $_REQUEST['_inline_edit'], 'taxinlineeditnonce' );
|
||||
$lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->term->get_language( $term_id );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
if ( isset( $GLOBALS['post_type'] ) ) {
|
||||
$post_type = $GLOBALS['post_type'];
|
||||
} elseif ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$post_type = sanitize_key( $_REQUEST['post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
if ( isset( $GLOBALS['taxonomy'] ) ) {
|
||||
$taxonomy = $GLOBALS['taxonomy'];
|
||||
} elseif ( isset( $_REQUEST['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$taxonomy = sanitize_key( $_REQUEST['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
if ( ! isset( $taxonomy, $post_type ) || ! is_string( $post_type ) || ! is_string( $taxonomy ) || ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
$language = $this->model->get_language( substr( $column, 9 ) );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return $out;
|
||||
}
|
||||
|
||||
$term = get_term( $term_id, $taxonomy );
|
||||
|
||||
if ( ! $term instanceof WP_Term ) {
|
||||
// Should not happen.
|
||||
return $out;
|
||||
}
|
||||
|
||||
$tr_id = $this->model->term->get( $term->term_id, $language );
|
||||
$tr_term = $tr_id ? get_term( $tr_id, $taxonomy ) : null;
|
||||
|
||||
if ( ! $tr_term instanceof WP_Term ) {
|
||||
// Link to add a new translation: no translation for this language yet, or it doesn't exist anymore.
|
||||
$out .= $this->links->get_new_term_link_html( $term, $post_type, $language );
|
||||
} else {
|
||||
// Link to edit (or not) the term or a translation.
|
||||
$out .= $this->links->get_edit_term_link_html( $tr_term, $post_type, $tr_term->term_id === $term->term_id ? 'list_current' : 'list_translation' );
|
||||
}
|
||||
|
||||
if ( $this->get_first_language_column() === $column ) {
|
||||
// Hidden field containing the term language for quick edit.
|
||||
$out .= sprintf( '<div class="hidden" id="lang_%d">%s</div>', $term->term_id, esc_html( $lang->slug ) );
|
||||
|
||||
/**
|
||||
* Filters the output of the first language column in the terms list table.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $output First language column output.
|
||||
* @param int $term_id Term ID.
|
||||
* @param string $lang Language code.
|
||||
*/
|
||||
$out = apply_filters( 'pll_first_language_term_column', $out, $term->term_id, $lang->slug );
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rows of translated posts when the language is modified in quick edit.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @phpstan-return never
|
||||
*/
|
||||
public function ajax_update_post_rows(): void {
|
||||
check_ajax_referer( 'inlineeditnonce', '_pll_nonce' );
|
||||
|
||||
if ( ! isset( $_POST['post_type'], $_POST['post_id'], $_POST['screen'] ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
$post_type = sanitize_key( $_POST['post_type'] );
|
||||
|
||||
if ( ! post_type_exists( $post_type ) || ! $this->model->is_translated_post_type( $post_type ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
/** @var WP_Posts_List_Table $wp_list_table */
|
||||
$wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
|
||||
|
||||
$x = new WP_Ajax_Response();
|
||||
|
||||
// Collect old translations.
|
||||
$translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
$translations = array_map( 'intval', $translations );
|
||||
$translations = array_merge( $translations, array( (int) $_POST['post_id'] ) ); // Add current post
|
||||
|
||||
foreach ( $translations as $post_id ) {
|
||||
$level = is_post_type_hierarchical( $post_type ) ? count( get_ancestors( $post_id, $post_type ) ) : 0;
|
||||
if ( $post = get_post( $post_id ) ) {
|
||||
ob_start();
|
||||
$wp_list_table->single_row( $post, $level );
|
||||
$data = (string) ob_get_clean();
|
||||
$x->add( array( 'what' => 'row', 'data' => $data, 'supplemental' => array( 'post_id' => $post_id ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
$x->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rows of translated terms when adding / deleting a translation
|
||||
* or when the language is modified in quick edit.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @phpstan-return never
|
||||
*/
|
||||
public function ajax_update_term_rows(): void {
|
||||
check_ajax_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
if ( ! isset( $_POST['taxonomy'], $_POST['term_id'], $_POST['screen'] ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
$taxonomy = sanitize_key( $_POST['taxonomy'] );
|
||||
|
||||
if ( ! taxonomy_exists( $taxonomy ) || ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
/** @var WP_Terms_List_Table $wp_list_table */
|
||||
$wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => sanitize_key( $_POST['screen'] ) ) );
|
||||
|
||||
$x = new WP_Ajax_Response();
|
||||
|
||||
// Collect old translations.
|
||||
$translations = empty( $_POST['translations'] ) ? array() : explode( ',', $_POST['translations'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
$translations = array_map( 'intval', $translations );
|
||||
$translations = array_merge( $translations, $this->model->term->get_translations( (int) $_POST['term_id'] ) ); // Add current translations.
|
||||
$translations = array_unique( $translations ); // Remove duplicates.
|
||||
|
||||
foreach ( $translations as $term_id ) {
|
||||
$level = is_taxonomy_hierarchical( $taxonomy ) ? count( get_ancestors( $term_id, $taxonomy ) ) : 0;
|
||||
$tag = get_term( $term_id, $taxonomy );
|
||||
|
||||
if ( ! $tag instanceof WP_Term ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
$wp_list_table->single_row( $tag, $level );
|
||||
$data = (string) ob_get_clean();
|
||||
$x->add( array( 'what' => 'row', 'data' => $data, 'supplemental' => array( 'term_id' => $term_id ) ) );
|
||||
}
|
||||
|
||||
$x->send();
|
||||
}
|
||||
}
|
||||
151
wp-content/plugins/polylang/src/admin/admin-filters-media.php
Normal file
151
wp-content/plugins/polylang/src/admin/admin-filters-media.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
/**
|
||||
* Manages filters and actions related to media on admin side
|
||||
* Capability to edit / create media is checked before loading this class
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin_Filters_Media extends PLL_Admin_Filters_Post_Base {
|
||||
/**
|
||||
* @var PLL_CRUD_Posts|null
|
||||
*/
|
||||
public $posts;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
$this->posts = &$polylang->posts;
|
||||
|
||||
// Adds the language field and translations tables in the 'Edit Media' panel.
|
||||
add_filter( 'attachment_fields_to_edit', array( $this, 'attachment_fields_to_edit' ), 10, 2 );
|
||||
|
||||
// Adds actions related to languages when creating, saving or deleting media.
|
||||
add_filter( 'attachment_fields_to_save', array( $this, 'save_media' ), 10, 2 );
|
||||
|
||||
// Maybe creates a media translation.
|
||||
add_action( 'admin_init', array( $this, 'translate_media' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language field and translations tables in the 'Edit Media' panel.
|
||||
* Needs WP 3.5+
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @param array $fields List of form fields.
|
||||
* @param WP_Post $post The attachment being edited.
|
||||
* @return array Modified list of form fields.
|
||||
*/
|
||||
public function attachment_fields_to_edit( $fields, $post ) {
|
||||
if ( 'post.php' === $GLOBALS['pagenow'] ) {
|
||||
return $fields; // Don't add anything on edit media panel since we have the metabox.
|
||||
}
|
||||
|
||||
$post_id = $post->ID;
|
||||
$lang = $this->model->post->get_language( $post_id );
|
||||
$user = Capabilities::get_user();
|
||||
$languages = $this->model->languages->filter( 'translator' )->get_list();
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
// The media has no language: prepend an empty option to prevent displaying a wrong information.
|
||||
array_unshift( $languages, (object) array( 'slug' => '', 'name' => '' ) );
|
||||
} elseif ( ! $user->can_translate( $lang ) ) {
|
||||
// The user cannot translate this language, so it isn't in `$languages` (the dropdown will be disabled).
|
||||
if ( ! in_array( $lang, $languages, true ) ) {
|
||||
array_unshift( $languages, $lang );
|
||||
}
|
||||
}
|
||||
|
||||
// Disable the dropdown if:
|
||||
// - the media has no language and the user is a translator,
|
||||
// - or the media has a language but the user is not allowed to translate it.
|
||||
$disabled = empty( $lang ) ? $user->is_translator() : ! $user->can_translate( $lang );
|
||||
|
||||
$dropdown = new PLL_Walker_Dropdown();
|
||||
$fields['language'] = array(
|
||||
'label' => __( 'Language', 'polylang' ),
|
||||
'input' => 'html',
|
||||
'html' => $dropdown->walk(
|
||||
$languages,
|
||||
-1,
|
||||
array(
|
||||
'name' => sprintf( 'attachments[%d][language]', $post_id ),
|
||||
'class' => 'media_lang_choice',
|
||||
'selected' => $lang ? $lang->slug : '',
|
||||
'disabled' => $disabled,
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe creates a media translation.
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function translate_media() {
|
||||
$data = $this->links->get_data_from_new_media_translation_request();
|
||||
if ( empty( $data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = $data['from_post']->ID;
|
||||
|
||||
// Bails if the translations already exists.
|
||||
if ( $this->model->post->get_translation( $post_id, $data['new_lang'] ) ) {
|
||||
wp_safe_redirect( wp_get_referer() );
|
||||
exit;
|
||||
}
|
||||
|
||||
$tr_id = $this->model->post->create_media_translation( $post_id, $data['new_lang'] );
|
||||
wp_safe_redirect( admin_url( sprintf( 'post.php?post=%d&action=edit', $tr_id ) ) ); // WP 3.5+.
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a media is saved
|
||||
* Saves language and translations
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @param array $post An array of post data.
|
||||
* @param array $attachment An array of attachment metadata.
|
||||
* @return array Unmodified $post
|
||||
*/
|
||||
public function save_media( $post, $attachment ) {
|
||||
// Language is filled in attachment by the function applying the filter 'attachment_fields_to_save'
|
||||
// All security checks have been done by functions applying this filter
|
||||
if ( empty( $attachment['language'] ) || ! current_user_can( 'edit_post', $post['ID'] ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
$language = $this->model->get_language( $attachment['language'] );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return $post;
|
||||
}
|
||||
|
||||
Capabilities::get_user()->can_translate_or_die( $language );
|
||||
|
||||
$this->model->post->set_language( $post['ID'], $language );
|
||||
|
||||
return $post;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Some common code for PLL_Admin_Filters_Post and PLL_Admin_Filters_Media
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
abstract class PLL_Admin_Filters_Post_Base {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Links
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* Language selected in the admin language filter.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $filter_lang;
|
||||
|
||||
/**
|
||||
* Preferred language to assign to new contents.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $pref_lang;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->links = &$polylang->links;
|
||||
$this->model = &$polylang->model;
|
||||
$this->pref_lang = &$polylang->pref_lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save translations from the languages metabox.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param int $post_id Post id of the post being saved.
|
||||
* @param int[] $arr An array with language codes as key and post id as value.
|
||||
* @return int[] The array of translated post ids.
|
||||
*/
|
||||
protected function save_translations( $post_id, $arr ) {
|
||||
// Security check as 'wp_insert_post' can be called from outside WP admin.
|
||||
check_admin_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
$translations = $this->model->post->save_translations( $post_id, $arr );
|
||||
return $translations;
|
||||
}
|
||||
}
|
||||
261
wp-content/plugins/polylang/src/admin/admin-filters-post.php
Normal file
261
wp-content/plugins/polylang/src/admin/admin-filters-post.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
/**
|
||||
* Manages filters and actions related to posts on admin side
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin_Filters_Post extends PLL_Admin_Filters_Post_Base {
|
||||
/**
|
||||
* Current language (used to filter the content).
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
$this->curlang = &$polylang->curlang;
|
||||
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
|
||||
|
||||
// Filters posts, pages and media by language
|
||||
add_action( 'parse_query', array( $this, 'parse_query' ) );
|
||||
|
||||
// Adds actions and filters related to languages when creating, saving or deleting posts and pages
|
||||
add_action( 'load-post.php', array( $this, 'edit_post' ) );
|
||||
add_action( 'load-edit.php', array( $this, 'bulk_edit_posts' ) );
|
||||
add_action( 'wp_ajax_inline-save', array( $this, 'inline_edit_post' ), 0 ); // Before WordPress
|
||||
|
||||
// Sets the language in Tiny MCE
|
||||
add_filter( 'tiny_mce_before_init', array( $this, 'tiny_mce_before_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a javascript list of terms ordered by language and hierarchical taxonomies
|
||||
* to filter the category checklist per post language in quick edit
|
||||
* Outputs a javascript list of pages ordered by language
|
||||
* to filter the parent dropdown per post language in quick edit
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function admin_enqueue_scripts() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( empty( $screen ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hierarchical taxonomies
|
||||
if ( 'edit' == $screen->base && $taxonomies = get_object_taxonomies( $screen->post_type, 'objects' ) ) {
|
||||
// Get translated hierarchical taxonomies
|
||||
$hierarchical_taxonomies = array();
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
if ( $taxonomy->hierarchical && $taxonomy->show_in_quick_edit && $this->model->is_translated_taxonomy( $taxonomy->name ) ) {
|
||||
$hierarchical_taxonomies[] = $taxonomy->name;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $hierarchical_taxonomies ) ) {
|
||||
$terms = get_terms( array( 'taxonomy' => $hierarchical_taxonomies, 'get' => 'all' ) );
|
||||
$term_languages = array();
|
||||
|
||||
if ( is_array( $terms ) ) {
|
||||
foreach ( $terms as $term ) {
|
||||
if ( $lang = $this->model->term->get_language( $term->term_id ) ) {
|
||||
$term_languages[ $lang->slug ][ $term->taxonomy ][] = $term->term_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send all these data to javascript
|
||||
if ( ! empty( $term_languages ) ) {
|
||||
wp_localize_script( 'pll_post', 'pll_term_languages', $term_languages );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hierarchical post types
|
||||
if ( 'edit' == $screen->base && is_post_type_hierarchical( $screen->post_type ) ) {
|
||||
$pages = get_pages( array( 'sort_column' => 'menu_order, post_title' ) ); // Same arguments as the parent pages dropdown to avoid an extra query.
|
||||
|
||||
update_post_caches( $pages, $screen->post_type, true, false );
|
||||
|
||||
$page_languages = array();
|
||||
|
||||
foreach ( $pages as $page ) {
|
||||
if ( $lang = $this->model->post->get_language( $page->ID ) ) {
|
||||
$page_languages[ $lang->slug ][] = $page->ID;
|
||||
}
|
||||
}
|
||||
|
||||
// Send all these data to javascript
|
||||
if ( ! empty( $page_languages ) ) {
|
||||
wp_localize_script( 'pll_post', 'pll_page_languages', $page_languages );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters posts, pages and media by language.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param WP_Query $query WP_Query object.
|
||||
* @return void
|
||||
*/
|
||||
public function parse_query( $query ) {
|
||||
$pll_query = new PLL_Query( $query, $this->model );
|
||||
$pll_query->filter_query( $this->curlang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save language and translation when editing a post (post.php).
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function edit_post() {
|
||||
if ( ! isset( $_POST['post_lang_choice'], $_POST['post_ID'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_admin_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
$post_id = (int) $_POST['post_ID'];
|
||||
$post = get_post( $post_id );
|
||||
|
||||
if ( empty( $post ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_type_object = get_post_type_object( $post->post_type );
|
||||
|
||||
if ( empty( $post_type_object ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = Capabilities::get_user();
|
||||
if ( ! $user->has_cap( $post_type_object->cap->edit_post, $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$language = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user->can_translate_or_die( $language );
|
||||
|
||||
$this->model->post->set_language( $post_id, $language );
|
||||
|
||||
if ( ! isset( $_POST['post_tr_lang'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->save_translations( $post_id, array_map( 'absint', $_POST['post_tr_lang'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save language when bulk editing posts.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bulk_edit_posts() {
|
||||
if ( ! isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'], $_REQUEST['post'], $_REQUEST['_wpnonce'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-posts' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( -1 === $_GET['inline_lang_choice'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$language = $this->model->get_language( sanitize_key( $_GET['inline_lang_choice'] ) );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = Capabilities::get_user();
|
||||
$user->can_translate_or_die( $language );
|
||||
|
||||
$post_ids = array_map( 'intval', (array) $_REQUEST['post'] );
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
if ( $user->has_cap( 'edit_post', $post_id ) ) {
|
||||
$this->model->post->set_language( $post_id, $language );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save language when inline editing a post.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function inline_edit_post() {
|
||||
if ( ! isset( $_POST['post_ID'], $_POST['inline_lang_choice'], $_REQUEST['_inline_edit'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! wp_verify_nonce( $_REQUEST['_inline_edit'], 'inlineeditnonce' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$language = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = Capabilities::get_user();
|
||||
$user->can_translate_or_die( $language );
|
||||
|
||||
$post_id = (int) $_POST['post_ID'];
|
||||
|
||||
if ( ! $post_id || ! $user->has_cap( 'edit_post', $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->model->post->set_language( $post_id, $language );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language attribute and text direction for Tiny MCE.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param array $mce_init TinyMCE config.
|
||||
* @return array
|
||||
*/
|
||||
public function tiny_mce_before_init( $mce_init ) {
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
$mce_init['wp_lang_attr'] = $this->curlang->get_locale( 'display' );
|
||||
$mce_init['directionality'] = $this->curlang->is_rtl ? 'rtl' : 'ltr';
|
||||
}
|
||||
return $mce_init;
|
||||
}
|
||||
}
|
||||
734
wp-content/plugins/polylang/src/admin/admin-filters-term.php
Normal file
734
wp-content/plugins/polylang/src/admin/admin-filters-term.php
Normal file
@@ -0,0 +1,734 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Manages filters and actions related to terms on admin side
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin_Filters_Term {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Links
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* Language selected in the admin language filter.
|
||||
*
|
||||
* @var PLL_Language
|
||||
*/
|
||||
public $filter_lang;
|
||||
|
||||
/**
|
||||
* Preferred language to assign to the new terms.
|
||||
*
|
||||
* @var PLL_Language
|
||||
*/
|
||||
public $pref_lang;
|
||||
|
||||
/**
|
||||
* Stores the current post_id when bulk editing posts.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $post_id = 0;
|
||||
|
||||
/**
|
||||
* A reference to the PLL_Admin_Default_Term instance.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @var PLL_Admin_Default_Term|null
|
||||
*/
|
||||
protected $default_term;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->links = &$polylang->links;
|
||||
$this->model = &$polylang->model;
|
||||
$this->pref_lang = &$polylang->pref_lang;
|
||||
$this->default_term = &$polylang->default_term;
|
||||
|
||||
foreach ( $this->model->get_translated_taxonomies() as $tax ) {
|
||||
// Adds the language field in the 'Categories' and 'Post Tags' panels
|
||||
add_action( $tax . '_add_form_fields', array( $this, 'add_term_form' ) );
|
||||
|
||||
// Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels
|
||||
add_action( $tax . '_edit_form_fields', array( $this, 'edit_term_form' ) );
|
||||
}
|
||||
|
||||
// Adds actions related to languages when creating or saving categories and post tags
|
||||
add_filter( 'wp_dropdown_cats', array( $this, 'wp_dropdown_cats' ) );
|
||||
add_action( 'create_term', array( $this, 'save_term' ), 900, 3 );
|
||||
add_action( 'edit_term', array( $this, 'save_term' ), 900, 3 ); // Late as it may conflict with other plugins, see http://wordpress.org/support/topic/polylang-and-wordpress-seo-by-yoast
|
||||
add_action( 'pre_post_update', array( $this, 'pre_post_update' ) );
|
||||
add_filter( 'pll_inserted_term_language', array( $this, 'get_inserted_term_language' ) );
|
||||
add_filter( 'pll_inserted_term_parent', array( $this, 'get_inserted_term_parent' ), 10, 2 );
|
||||
|
||||
// Ajax response for edit term form
|
||||
add_action( 'wp_ajax_term_lang_choice', array( $this, 'term_lang_choice' ) );
|
||||
add_action( 'wp_ajax_pll_terms_not_translated', array( $this, 'ajax_terms_not_translated' ) );
|
||||
|
||||
// Updates the translations term ids when splitting a shared term
|
||||
add_action( 'split_shared_term', array( $this, 'split_shared_term' ), 10, 4 ); // WP 4.2
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language field in the 'Categories' and 'Post Tags' panels
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_term_form() {
|
||||
if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$taxonomy = sanitize_key( $_GET['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$post_type = sanitize_key( $_REQUEST['post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
if ( isset( $GLOBALS['post_type'] ) ) {
|
||||
$post_type = $GLOBALS['post_type'];
|
||||
}
|
||||
|
||||
if ( ! isset( $taxonomy, $post_type ) || ! taxonomy_exists( $taxonomy ) || ! post_type_exists( $post_type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$from_term_id = isset( $_GET['from_tag'] ) ? (int) $_GET['from_tag'] : 0; // phpcs:ignore WordPress.Security.NonceVerification
|
||||
|
||||
$lang = isset( $_GET['new_lang'] ) ? $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) : $this->pref_lang; // phpcs:ignore WordPress.Security.NonceVerification
|
||||
|
||||
$dropdown = new PLL_Walker_Dropdown();
|
||||
|
||||
$dropdown_html = $dropdown->walk(
|
||||
$this->model->languages->filter( 'translator' )->get_list(),
|
||||
-1,
|
||||
array(
|
||||
'name' => 'term_lang_choice',
|
||||
'value' => 'term_id',
|
||||
'selected' => $lang ? $lang->term_id : '',
|
||||
'flag' => true,
|
||||
)
|
||||
);
|
||||
|
||||
wp_nonce_field( 'pll_language', '_pll_nonce' );
|
||||
|
||||
printf(
|
||||
'<div class="form-field">
|
||||
<label for="term_lang_choice">%s</label>
|
||||
<div id="select-add-term-language">%s</div>
|
||||
<p>%s</p>
|
||||
</div>',
|
||||
esc_html__( 'Language', 'polylang' ),
|
||||
$dropdown_html, // phpcs:ignore
|
||||
esc_html__( 'Sets the language', 'polylang' )
|
||||
);
|
||||
|
||||
if ( ! empty( $from_term_id ) ) {
|
||||
printf( '<input type="hidden" name="from_tag" value="%d" />', (int) $from_term_id );
|
||||
}
|
||||
|
||||
// Adds translation fields
|
||||
echo '<div id="term-translations" class="form-field">';
|
||||
if ( $lang ) {
|
||||
include __DIR__ . '/view-translations-term.php';
|
||||
}
|
||||
echo '</div>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language field and translations tables in the 'Edit Category' and 'Edit Tag' panels.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param WP_Term $term The term being edited.
|
||||
* @return void
|
||||
*/
|
||||
public function edit_term_form( $term ) {
|
||||
if ( isset( $_REQUEST['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$post_type = sanitize_key( $_REQUEST['post_type'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
if ( isset( $GLOBALS['post_type'] ) ) {
|
||||
$post_type = $GLOBALS['post_type'];
|
||||
}
|
||||
|
||||
if ( ! isset( $post_type ) || ! post_type_exists( $post_type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$taxonomy = $term->taxonomy; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
|
||||
$lang = $this->model->term->get_language( $term->term_id );
|
||||
$lang = empty( $lang ) ? $this->pref_lang : $lang;
|
||||
|
||||
// Disable the language dropdown and the translations input fields for default terms to prevent removal
|
||||
$disabled = $this->default_term->is_default_term( $term->term_id );
|
||||
|
||||
$dropdown = new PLL_Walker_Dropdown();
|
||||
|
||||
$dropdown_html = $dropdown->walk(
|
||||
$this->model->languages->filter( 'translator' )->get_list(),
|
||||
-1,
|
||||
array(
|
||||
'name' => 'term_lang_choice',
|
||||
'value' => 'term_id',
|
||||
'selected' => $lang->term_id,
|
||||
'disabled' => $disabled,
|
||||
'flag' => true,
|
||||
)
|
||||
);
|
||||
|
||||
wp_nonce_field( 'pll_language', '_pll_nonce' );
|
||||
|
||||
printf(
|
||||
'<tr class="form-field">
|
||||
<th scope="row">
|
||||
<label for="term_lang_choice">%s</label>
|
||||
</th>
|
||||
<td id="select-edit-term-language">
|
||||
%s<br />
|
||||
<p class="description">%s</p>
|
||||
</td>
|
||||
</tr>',
|
||||
esc_html__( 'Language', 'polylang' ),
|
||||
$dropdown_html, // phpcs:ignore
|
||||
esc_html__( 'Sets the language', 'polylang' )
|
||||
);
|
||||
|
||||
echo '<tr id="term-translations" class="form-field">';
|
||||
include __DIR__ . '/view-translations-term.php';
|
||||
echo '</tr>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates term parent if exists when using "Add new" ( translation )
|
||||
*
|
||||
* @since 0.7
|
||||
*
|
||||
* @param string $output html markup for dropdown list of categories
|
||||
* @return string modified html
|
||||
*/
|
||||
public function wp_dropdown_cats( $output ) {
|
||||
if ( isset( $_GET['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$taxonomy = sanitize_key( $_GET['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
if ( isset( $taxonomy, $_GET['from_tag'], $_GET['new_lang'] ) && taxonomy_exists( $taxonomy ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$term = get_term( (int) $_GET['from_tag'], $taxonomy ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
|
||||
if ( $term instanceof WP_Term && $id = $term->parent ) {
|
||||
$lang = $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
if ( $parent = $this->model->term->get_translation( $id, $lang ) ) {
|
||||
return str_replace( '"' . $parent . '"', '"' . $parent . '" selected="selected"', $output );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the current post_id when bulk editing posts for use in save_language and get_inserted_term_language.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return void
|
||||
*/
|
||||
public function pre_post_update( $post_id ) {
|
||||
if ( isset( $_GET['bulk_edit'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->post_id = $post_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the language of a term.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param int $term_id The term ID.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return void|never
|
||||
*/
|
||||
protected function save_language( $term_id, $taxonomy ) {
|
||||
$term_id = (int) $term_id;
|
||||
|
||||
/*
|
||||
* Category metabox.
|
||||
*/
|
||||
if ( isset( $_POST['term_lang_choice'], $_REQUEST[ '_ajax_nonce-add-' . $taxonomy ] ) && wp_verify_nonce( $_REQUEST[ '_ajax_nonce-add-' . $taxonomy ], 'add-' . $taxonomy ) ) {
|
||||
$this->maybe_set_language( $term_id, sanitize_key( $_POST['term_lang_choice'] ) );
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Edit tags or tags metabox.
|
||||
*/
|
||||
if ( isset( $_POST['term_lang_choice'] ) ) {
|
||||
check_admin_referer( 'pll_language', '_pll_nonce' );
|
||||
$this->maybe_set_language( $term_id, sanitize_key( $_POST['term_lang_choice'] ) );
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* *Post* bulk edit, in case a new term is created.
|
||||
*/
|
||||
if ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'], $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-posts' ) ) {
|
||||
/*
|
||||
* Bulk edit does not modify the language, so we possibly create a tag in several languages.
|
||||
*/
|
||||
if ( -1 === (int) $_GET['inline_lang_choice'] ) {
|
||||
// The language of the current term is set a according to the language of the current post.
|
||||
$language = $this->model->post->get_language( $this->post_id );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Capabilities::get_user()->can_translate_or_die( $language );
|
||||
|
||||
$this->model->term->set_language( $term_id, $language );
|
||||
$term = get_term( $term_id, $taxonomy );
|
||||
|
||||
// Get all terms with the same name.
|
||||
if ( $term instanceof WP_Term ) {
|
||||
$term_ids = get_terms( array( 'taxonomy' => $taxonomy, 'name' => $term->name, 'hide_empty' => false, 'fields' => 'ids' ) );
|
||||
|
||||
// If we have several terms with the same name, they are translations of each other.
|
||||
if ( is_array( $term_ids ) && count( $term_ids ) > 1 ) {
|
||||
$translations = array();
|
||||
|
||||
foreach ( $term_ids as $_id ) {
|
||||
$translations[ $this->model->term->get_language( $_id )->slug ] = $_id;
|
||||
}
|
||||
|
||||
$this->model->term->save_translations( $term_id, $translations );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ( current_user_can( 'edit_term', $term_id ) ) {
|
||||
$this->maybe_set_language( $term_id, sanitize_key( $_GET['inline_lang_choice'] ) );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quick edit.
|
||||
*/
|
||||
if ( isset( $_POST['inline_lang_choice'], $_REQUEST['_inline_edit'] ) ) {
|
||||
if ( ! wp_verify_nonce( $_REQUEST['_inline_edit'], 'inlineeditnonce' ) && ! wp_verify_nonce( $_REQUEST['_inline_edit'], 'taxinlineeditnonce' ) ) { // Post quick edit or tag quick edit?
|
||||
return;
|
||||
}
|
||||
|
||||
$this->maybe_set_language( $term_id, sanitize_key( $_POST['inline_lang_choice'] ) );
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Edit post.
|
||||
*/
|
||||
if ( isset( $_POST['post_lang_choice'] ) ) { // FIXME should be useless now.
|
||||
check_admin_referer( 'pll_language', '_pll_nonce' );
|
||||
$this->maybe_set_language( $term_id, sanitize_key( $_POST['post_lang_choice'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language of a term if the user is allowed to.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param int $term_id The term ID.
|
||||
* @param string $language Language slug.
|
||||
* @return void|never
|
||||
*/
|
||||
private function maybe_set_language( int $term_id, string $language ): void {
|
||||
$language = $this->model->get_language( $language );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Capabilities::get_user()->can_translate_or_die( $language );
|
||||
|
||||
$this->model->term->set_language( $term_id, $language );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save translations from our form.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param int $term_id The term id of the term being saved.
|
||||
* @return int[] The array of translated term ids.
|
||||
*/
|
||||
protected function save_translations( $term_id ) {
|
||||
// Security check as 'wp_update_term' can be called from outside WP admin.
|
||||
check_admin_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
$translations = array();
|
||||
|
||||
// Save translations after checking the translated term is in the right language ( as well as cast id to int ).
|
||||
if ( isset( $_POST['term_tr_lang'] ) ) {
|
||||
foreach ( array_map( 'absint', $_POST['term_tr_lang'] ) as $lang => $tr_id ) {
|
||||
$tr_lang = $this->model->term->get_language( $tr_id );
|
||||
$translations[ $lang ] = $tr_lang && $tr_lang->slug == $lang ? $tr_id : 0;
|
||||
}
|
||||
}
|
||||
|
||||
$this->model->term->save_translations( $term_id, $translations );
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a category or post tag is created or edited
|
||||
* Saves language and translations
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @param int $tt_id Term taxonomy ID.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return void
|
||||
*/
|
||||
public function save_term( $term_id, $tt_id, $taxonomy ) {
|
||||
// Does nothing except on taxonomies which are filterable
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tax = get_taxonomy( $taxonomy );
|
||||
|
||||
if ( empty( $tax ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Capability check
|
||||
// As 'wp_update_term' can be called from outside WP admin
|
||||
// 2nd test for creating tags when creating / editing a post
|
||||
if ( current_user_can( $tax->cap->edit_terms ) || ( isset( $_POST['tax_input'][ $taxonomy ] ) && current_user_can( $tax->cap->assign_terms ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->save_language( $term_id, $taxonomy );
|
||||
|
||||
if ( isset( $_POST['term_tr_lang'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->save_translations( $term_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax response for edit term form
|
||||
*
|
||||
* @since 0.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function term_lang_choice() {
|
||||
check_ajax_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
if ( ! isset( $_POST['taxonomy'], $_POST['post_type'], $_POST['lang'] ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
$lang = $this->model->get_language( sanitize_key( $_POST['lang'] ) );
|
||||
$taxonomy = sanitize_key( $_POST['taxonomy'] );
|
||||
$post_type = sanitize_key( $_POST['post_type'] );
|
||||
|
||||
if ( empty( $lang ) || ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
if ( ! empty( $_POST['term_id'] ) ) {
|
||||
$term = get_term( (int) $_POST['term_id'], $taxonomy ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
||||
$term = $term instanceof WP_Term ? $term : null;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include __DIR__ . '/view-translations-term.php';
|
||||
$x = new WP_Ajax_Response( array( 'what' => 'translations', 'data' => ob_get_contents() ) );
|
||||
ob_end_clean();
|
||||
|
||||
// Parent dropdown list ( only for hierarchical taxonomies )
|
||||
// $args copied from edit_tags.php except echo
|
||||
if ( is_taxonomy_hierarchical( $taxonomy ) ) {
|
||||
$args = array(
|
||||
'hide_empty' => 0,
|
||||
'hide_if_empty' => false,
|
||||
'taxonomy' => $taxonomy,
|
||||
'name' => 'parent',
|
||||
'orderby' => 'name',
|
||||
'hierarchical' => true,
|
||||
'show_option_none' => __( 'None', 'polylang' ),
|
||||
'echo' => 0,
|
||||
);
|
||||
$x->Add( array( 'what' => 'parent', 'data' => wp_dropdown_categories( $args ) ) );
|
||||
}
|
||||
|
||||
// Tag cloud
|
||||
// Tests copied from edit_tags.php
|
||||
else {
|
||||
$tax = get_taxonomy( $taxonomy );
|
||||
if ( ! empty( $tax ) && ! is_null( $tax->labels->popular_items ) ) {
|
||||
$args = array( 'taxonomy' => $taxonomy, 'echo' => false );
|
||||
if ( current_user_can( $tax->cap->edit_terms ) ) {
|
||||
$args = array_merge( $args, array( 'link' => 'edit' ) );
|
||||
}
|
||||
|
||||
$tag_cloud = wp_tag_cloud( $args );
|
||||
|
||||
if ( ! empty( $tag_cloud ) ) {
|
||||
/** @phpstan-var non-falsy-string $tag_cloud */
|
||||
$html = sprintf( '<div class="tagcloud"><h2>%1$s</h2>%2$s</div>', esc_html( $tax->labels->popular_items ), $tag_cloud );
|
||||
$x->Add( array( 'what' => 'tag_cloud', 'data' => $html ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flag
|
||||
$x->Add( array( 'what' => 'flag', 'data' => empty( $lang->flag ) ? esc_html( $lang->slug ) : $lang->flag ) );
|
||||
|
||||
$x->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax response for input in translation autocomplete input box.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_terms_not_translated() {
|
||||
check_ajax_referer( 'pll_language', '_pll_nonce' );
|
||||
|
||||
if ( ! isset( $_GET['term'], $_GET['post_type'], $_GET['taxonomy'], $_GET['term_language'], $_GET['translation_language'] ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
$s = wp_unslash( $_GET['term'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
$post_type = sanitize_key( $_GET['post_type'] );
|
||||
$taxonomy = sanitize_key( $_GET['taxonomy'] );
|
||||
|
||||
if ( ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
|
||||
wp_die( 0 );
|
||||
}
|
||||
|
||||
$term_language = $this->model->get_language( sanitize_key( $_GET['term_language'] ) );
|
||||
$translation_language = $this->model->get_language( sanitize_key( $_GET['translation_language'] ) );
|
||||
|
||||
$terms = array();
|
||||
$return = array();
|
||||
|
||||
// Add current translation in list.
|
||||
// Not in add term as term_id is not set.
|
||||
if ( isset( $_GET['term_id'] ) && 'undefined' !== $_GET['term_id'] && $term_id = $this->model->term->get_translation( (int) $_GET['term_id'], $translation_language ) ) {
|
||||
$terms = array( get_term( $term_id, $taxonomy ) );
|
||||
}
|
||||
|
||||
// It is more efficient to use one common query for all languages as soon as there are more than 2.
|
||||
$all_terms = get_terms( array( 'taxonomy' => $taxonomy, 'hide_empty' => false, 'lang' => '', 'name__like' => $s ) );
|
||||
if ( is_array( $all_terms ) ) {
|
||||
foreach ( $all_terms as $term ) {
|
||||
$lang = $this->model->term->get_language( $term->term_id );
|
||||
|
||||
if ( $lang && $lang->slug == $translation_language->slug && ! $this->model->term->get_translation( $term->term_id, $term_language ) ) {
|
||||
$terms[] = $term;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format the ajax response.
|
||||
foreach ( $terms as $term ) {
|
||||
if ( ! $term instanceof WP_Term ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parents_list = get_term_parents_list(
|
||||
$term->term_id,
|
||||
$term->taxonomy,
|
||||
array(
|
||||
'separator' => ' > ',
|
||||
'link' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_string( $parents_list ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$return[] = array(
|
||||
'id' => $term->term_id,
|
||||
'value' => rtrim( $parents_list, ' >' ), // Trim the separator added at the end by WP.
|
||||
'link' => $this->links->get_edit_term_link_html( $term, $post_type ),
|
||||
);
|
||||
}
|
||||
|
||||
wp_die( wp_json_encode( $return ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the translations term ids when splitting a shared term
|
||||
* Splits translations if these are shared terms too
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param int $term_id ID of the formerly shared term.
|
||||
* @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
|
||||
* @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return void
|
||||
*/
|
||||
public function split_shared_term( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid recursion
|
||||
static $avoid_recursion = false;
|
||||
if ( $avoid_recursion ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lang = $this->model->term->get_language( $term_id );
|
||||
if ( empty( $lang ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$avoid_recursion = true;
|
||||
$translations = array();
|
||||
|
||||
foreach ( $this->model->term->get_translations( $term_id ) as $key => $tr_id ) {
|
||||
if ( $lang->slug == $key ) {
|
||||
$translations[ $key ] = $new_term_id;
|
||||
}
|
||||
else {
|
||||
$tr_term = get_term( $tr_id, $taxonomy );
|
||||
|
||||
if ( ! $tr_term instanceof WP_Term ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$split_term_id = _split_shared_term( $tr_id, $tr_term->term_taxonomy_id );
|
||||
|
||||
if ( is_int( $split_term_id ) ) {
|
||||
$translations[ $key ] = $split_term_id;
|
||||
} else {
|
||||
$translations[ $key ] = $tr_id;
|
||||
}
|
||||
|
||||
// Hack translation ids sent by the form to avoid overwrite in PLL_Admin_Filters_Term::save_translations
|
||||
if ( isset( $_POST['term_tr_lang'][ $key ] ) && $_POST['term_tr_lang'][ $key ] == $tr_id ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$_POST['term_tr_lang'][ $key ] = $translations[ $key ];
|
||||
}
|
||||
}
|
||||
|
||||
$this->model->term->set_language( $translations[ $key ], $key );
|
||||
}
|
||||
|
||||
$this->model->term->save_translations( $new_term_id, $translations );
|
||||
$avoid_recursion = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language for subsequently inserted term in admin.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param PLL_Language|null $lang Term language object if found, null otherwise.
|
||||
* @return PLL_Language|null Language object, null if none found.
|
||||
*/
|
||||
public function get_inserted_term_language( $lang ) {
|
||||
if ( $lang instanceof PLL_Language ) {
|
||||
return $lang;
|
||||
}
|
||||
|
||||
if ( ! empty( $_POST['term_lang_choice'] ) && is_string( $_POST['term_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang_slug = sanitize_key( $_POST['term_lang_choice'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang = $this->model->get_language( $lang_slug );
|
||||
return $lang instanceof PLL_Language ? $lang : null;
|
||||
}
|
||||
|
||||
if ( ! empty( $_POST['inline_lang_choice'] ) && is_string( $_POST['inline_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang_slug = sanitize_key( $_POST['inline_lang_choice'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang = $this->model->get_language( $lang_slug );
|
||||
return $lang instanceof PLL_Language ? $lang : null;
|
||||
}
|
||||
|
||||
// *Post* bulk edit, in case a new term is created
|
||||
if ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
// Bulk edit does not modify the language
|
||||
if ( -1 === (int) $_GET['inline_lang_choice'] ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang = $this->model->post->get_language( $this->post_id );
|
||||
return $lang instanceof PLL_Language ? $lang : null;
|
||||
} elseif ( is_string( $_GET['inline_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang_slug = sanitize_key( $_GET['inline_lang_choice'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang = $this->model->get_language( $lang_slug );
|
||||
return $lang instanceof PLL_Language ? $lang : null;
|
||||
}
|
||||
}
|
||||
|
||||
// Special cases for default categories as the select is disabled.
|
||||
$default_term = get_option( 'default_category' );
|
||||
|
||||
if ( ! is_numeric( $default_term ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! empty( $_POST['tag_ID'] ) && in_array( (int) $default_term, $this->model->term->get_translations( (int) $_POST['tag_ID'] ), true ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang = $this->model->term->get_language( (int) $_POST['tag_ID'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return $lang instanceof PLL_Language ? $lang : null;
|
||||
}
|
||||
|
||||
if ( ! empty( $_POST['tax_ID'] ) && in_array( (int) $default_term, $this->model->term->get_translations( (int) $_POST['tax_ID'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$lang = $this->model->term->get_language( (int) $_POST['tax_ID'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return $lang instanceof PLL_Language ? $lang : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the subsequently inserted term parent in admin.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param int $parent Parent term ID, 0 if none found.
|
||||
* @param string $taxonomy Term taxonomy.
|
||||
* @return int Parent term ID if found, 0 otherwise.
|
||||
*/
|
||||
public function get_inserted_term_parent( $parent, $taxonomy ) {
|
||||
if ( $parent ) {
|
||||
return $parent;
|
||||
}
|
||||
|
||||
if ( isset( $_POST['parent'], $_POST['term_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$parent = intval( $_POST['parent'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
} elseif ( isset( $_POST[ "new{$taxonomy}_parent" ], $_POST['term_lang_choice'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$parent = intval( $_POST[ "new{$taxonomy}_parent" ] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
return $parent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class PLL_Widgets_Filters
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* Adds new options to {@see https://developer.wordpress.org/reference/classes/wp_widget/ WP_Widget} and saves them.
|
||||
*/
|
||||
class PLL_Admin_Filters_Widgets_Options extends PLL_Filters_Widgets_Options {
|
||||
/**
|
||||
* Modifies the widgets forms to add our language dropdown list.
|
||||
*
|
||||
* @since 0.3
|
||||
* @since 3.0 Moved from PLL_Admin_Filters
|
||||
*
|
||||
* @param WP_Widget $widget Widget instance.
|
||||
* @param null $return Not used.
|
||||
* @param array $instance Widget settings.
|
||||
* @return void
|
||||
*
|
||||
* @phpstan-param WP_Widget<array<string, mixed>> $widget
|
||||
*/
|
||||
public function in_widget_form( $widget, $return, $instance ) {
|
||||
$screen = get_current_screen();
|
||||
|
||||
// Test the Widgets screen and the Customizer to avoid displaying the option in page builders
|
||||
// Saving the widget reloads the form. And curiously the action is in $_REQUEST but neither in $_POST, nor in $_GET.
|
||||
if ( ( isset( $screen ) && 'widgets' === $screen->base ) || ( isset( $_REQUEST['action'] ) && 'save-widget' === $_REQUEST['action'] ) || isset( $GLOBALS['wp_customize'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
parent::in_widget_form( $widget, $return, $instance );
|
||||
}
|
||||
}
|
||||
}
|
||||
148
wp-content/plugins/polylang/src/admin/admin-filters.php
Normal file
148
wp-content/plugins/polylang/src/admin/admin-filters.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Setup miscellaneous admin filters as well as filters common to admin and frontend
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin_Filters extends PLL_Filters {
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Admin $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
// Language management for users
|
||||
add_action( 'personal_options_update', array( $this, 'personal_options_update' ) );
|
||||
add_action( 'edit_user_profile_update', array( $this, 'personal_options_update' ) );
|
||||
add_action( 'personal_options', array( $this, 'user_profile_enqueue_scripts' ) );
|
||||
|
||||
// Upgrades plugins and themes translations files
|
||||
add_filter( 'themes_update_check_locales', array( $this, 'update_check_locales' ) );
|
||||
add_filter( 'plugins_update_check_locales', array( $this, 'update_check_locales' ) );
|
||||
|
||||
add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
|
||||
|
||||
// Add post state for translations of the privacy policy page
|
||||
add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user biographies.
|
||||
*
|
||||
* @since 0.4
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @return void
|
||||
*/
|
||||
public function personal_options_update( $user_id ) {
|
||||
// Biography translations
|
||||
foreach ( $this->model->get_languages_list() as $lang ) {
|
||||
$meta = $lang->is_default ? 'description' : 'description_' . $lang->slug;
|
||||
$description = empty( $_POST[ 'description_' . $lang->slug ] ) ? '' : trim( $_POST[ 'description_' . $lang->slug ] ); // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
|
||||
|
||||
/** This filter is documented in wp-includes/user.php */
|
||||
$description = apply_filters( 'pre_user_description', $description ); // Applies WP default filter wp_filter_kses
|
||||
update_user_meta( $user_id, $meta, $description );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues scripts for the multilingual biography on the user's profile admin page.
|
||||
*
|
||||
* @since 3.8.4
|
||||
*
|
||||
* @param WP_User $profileuser The current WP_User object.
|
||||
* @return void
|
||||
*/
|
||||
public function user_profile_enqueue_scripts( $profileuser ): void {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( empty( $screen ) || ! in_array( $screen->base, array( 'profile', 'user-edit' ), true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->model->has_languages() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
|
||||
$data = array();
|
||||
|
||||
wp_enqueue_script( 'pll_user', plugins_url( "/js/build/user{$suffix}.js", POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION, array( 'in_footer' => true ) );
|
||||
|
||||
foreach ( $this->model->languages->get_list() as $lang ) {
|
||||
$meta = $lang->is_default ? 'description' : "description_{$lang->slug}";
|
||||
$description = get_user_meta( $profileuser->ID, $meta, true );
|
||||
$description = is_string( $description ) ? $description : '';
|
||||
|
||||
$data[] = array(
|
||||
'slug' => esc_attr( $lang->slug ),
|
||||
'name' => esc_html( $lang->name ),
|
||||
'lang' => esc_attr( $lang->get_locale( 'display' ) ),
|
||||
'direction' => $lang->is_rtl ? 'rtl' : 'ltr',
|
||||
'flag' => PLL_Language::get_flag_information( $lang->flag_code ),
|
||||
'description' => sanitize_user_field( 'description', $description, $profileuser->ID, 'edit' ),
|
||||
);
|
||||
}
|
||||
|
||||
$script = sprintf( 'const pllDescriptionData = %s;', wp_json_encode( $data ) );
|
||||
wp_add_inline_script( 'pll_user', $script, 'before' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to update translations files for plugins and themes.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string[] $locales List of locales to update for plugins and themes.
|
||||
* @return string[]
|
||||
*/
|
||||
public function update_check_locales( $locales ) {
|
||||
return array_merge( $locales, $this->model->get_languages_list( array( 'fields' => 'locale' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds custom classes to the body
|
||||
*
|
||||
* @since 2.2 Adds a text direction dependent class to the body.
|
||||
* @since 3.4 Adds a language dependent class to the body.
|
||||
*
|
||||
* @param string $classes Space-separated list of CSS classes.
|
||||
* @return string
|
||||
*/
|
||||
public function admin_body_class( $classes ) {
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
$classes .= ' pll-dir-' . ( $this->curlang->is_rtl ? 'rtl' : 'ltr' );
|
||||
$classes .= ' pll-lang-' . $this->curlang->slug;
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds post state for translations of the privacy policy page.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @param string[] $post_states An array of post display states.
|
||||
* @param WP_Post $post The current post object.
|
||||
* @return string[]
|
||||
*/
|
||||
public function display_post_states( $post_states, $post ) {
|
||||
$page_for_privacy_policy = get_option( 'wp_page_for_privacy_policy' );
|
||||
|
||||
if ( $page_for_privacy_policy && in_array( $post->ID, $this->model->post->get_translations( $page_for_privacy_policy ) ) ) {
|
||||
$post_states['page_for_privacy_policy'] = __( 'Privacy Policy Page', 'polylang' );
|
||||
}
|
||||
|
||||
return $post_states;
|
||||
}
|
||||
}
|
||||
438
wp-content/plugins/polylang/src/admin/admin-links.php
Normal file
438
wp-content/plugins/polylang/src/admin/admin-links.php
Normal file
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
/**
|
||||
* Manages links related functions.
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
class PLL_Admin_Links extends PLL_Links {
|
||||
/**
|
||||
* Current user.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Capabilities\User\User_Interface
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Base $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( PLL_Base &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
$this->user = Capabilities::get_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the html markup for a new post translation link.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_Post $post The source post.
|
||||
* @param PLL_Language $language The language of the new translation.
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_post_link_html( WP_Post $post, PLL_Language $language ): string {
|
||||
$link = $this->get_new_post_translation_link( $post, $language );
|
||||
return $this->new_translation_link( $link, $language );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to create a new post translation.
|
||||
* Returns an empty string if the current user is not allowed to create posts in the given language.
|
||||
*
|
||||
* @since 1.5
|
||||
* @since 3.8 Changed first parameter type from `int` to `WP_Post`.
|
||||
*
|
||||
* @param WP_Post $post The source post.
|
||||
* @param PLL_Language $language The language of the new translation.
|
||||
* @param string $context Optional. Defaults to 'display' which encodes '&' to '&'.
|
||||
* Otherwise, preserves '&'.
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_post_translation_link( WP_Post $post, PLL_Language $language, string $context = 'display' ): string {
|
||||
if ( ! $this->user->can_translate( $language ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$post_type_object = get_post_type_object( $post->post_type );
|
||||
|
||||
if ( empty( $post_type_object ) || ! $this->user->has_cap( $post_type_object->cap->create_posts ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Special case for the privacy policy page which is associated to a specific capability
|
||||
if ( 'page' === $post_type_object->name && ! $this->user->has_cap( 'manage_privacy_options' ) ) {
|
||||
$privacy_page = get_option( 'wp_page_for_privacy_policy' );
|
||||
$privacy_page = is_numeric( $privacy_page ) ? (int) $privacy_page : 0;
|
||||
|
||||
if ( $privacy_page && in_array( $post->ID, $this->model->post->get_translations( $privacy_page ) ) ) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'attachment' === $post->post_type ) {
|
||||
$args = array(
|
||||
'action' => 'translate_media',
|
||||
'from_media' => $post->ID,
|
||||
'new_lang' => $language->slug,
|
||||
);
|
||||
|
||||
$link = add_query_arg( $args, admin_url( 'admin.php' ) );
|
||||
|
||||
// Add nonce for media as we will directly publish a new attachment from a click on this link
|
||||
if ( 'display' === $context ) {
|
||||
$link = wp_nonce_url( $link, 'translate_media' );
|
||||
} else {
|
||||
$link = add_query_arg( '_wpnonce', wp_create_nonce( 'translate_media' ), $link );
|
||||
}
|
||||
} else {
|
||||
$args = array(
|
||||
'post_type' => $post->post_type,
|
||||
'from_post' => $post->ID,
|
||||
'new_lang' => $language->slug,
|
||||
);
|
||||
|
||||
$link = add_query_arg( $args, admin_url( 'post-new.php' ) );
|
||||
|
||||
if ( 'display' === $context ) {
|
||||
$link = wp_nonce_url( $link, 'new-post-translation' );
|
||||
} else {
|
||||
$link = add_query_arg( '_wpnonce', wp_create_nonce( 'new-post-translation' ), $link );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the new post translation link.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string $link The new post translation link.
|
||||
* @param PLL_Language $language The language of the new translation.
|
||||
* @param int $post_id The source post id.
|
||||
*/
|
||||
return apply_filters( 'pll_get_new_post_translation_link', $link, $language, $post->ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the html markup for a new translation link.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param string $link The new translation link.
|
||||
* @param PLL_Language $language The language of the new translation.
|
||||
* @return string
|
||||
*/
|
||||
protected function new_translation_link( string $link, PLL_Language $language ): string {
|
||||
if ( empty( $link ) ) {
|
||||
return sprintf(
|
||||
'<span title="%s" class="pll_icon_add wp-ui-text-icon"></span>',
|
||||
/* translators: accessibility text, %s is a native language name */
|
||||
esc_attr( sprintf( __( 'You are not allowed to add a translation in %s', 'polylang' ), $language->name ) )
|
||||
);
|
||||
}
|
||||
|
||||
/* translators: accessibility text, %s is a native language name */
|
||||
$hint = sprintf( __( 'Add a translation in %s', 'polylang' ), $language->name );
|
||||
return sprintf(
|
||||
'<a href="%1$s" title="%2$s" class="pll_icon_add"><span class="screen-reader-text">%3$s</span></a>',
|
||||
esc_url( $link ),
|
||||
esc_attr( $hint ),
|
||||
esc_html( $hint )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the html markup for a post translation link.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_Post $post The post.
|
||||
* @param string $mode Optional. How the link should be displayed: with a pen icon or a language's flag.
|
||||
* Possible values are:
|
||||
* - `metabox_translation` (pen icon in metabox),
|
||||
* - `list_translation` (pen icon in items list),
|
||||
* - `list_current` (flag in items list).
|
||||
* Default is `metabox_translation`.
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-param 'metabox_translation'|'list_translation'|'list_current' $mode
|
||||
*/
|
||||
public function get_edit_post_link_html( WP_Post $post, string $mode = 'metabox_translation' ): string {
|
||||
$language = $this->model->post->get_language( $post->ID );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
// Should not happen.
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = (string) get_edit_post_link( $post->ID );
|
||||
return $this->get_edit_item_link_html( $url, $language, $post->ID, $post->post_title, $mode );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the html markup for a translation link.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string $url URL of the edition link.
|
||||
* @param PLL_Language $language Language of the item.
|
||||
* @param int $item_id ID of the item. Used only in `list_translation` mode.
|
||||
* @param string $item_name Name of the item. Not used in `metabox_translation` mode.
|
||||
* @param string $mode How the link should be displayed: with a pen icon or a language's flag.
|
||||
* Possible values are:
|
||||
* - `metabox_translation` (pen icon in metabox),
|
||||
* - `list_translation` (pen icon in items list),
|
||||
* - `list_current` (flag in items list).
|
||||
* Default is `metabox_translation`.
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-param 'metabox_translation'|'list_translation'|'list_current' $mode
|
||||
*/
|
||||
private function get_edit_item_link_html( string $url, PLL_Language $language, int $item_id, string $item_name, string $mode ): string {
|
||||
if ( 'list_current' === $mode ) {
|
||||
$flag = $this->get_flag_html( $language );
|
||||
$class = 'pll_column_flag';
|
||||
} else {
|
||||
$flag = '';
|
||||
$class = 'pll_icon_edit';
|
||||
}
|
||||
|
||||
if ( empty( $url ) ) {
|
||||
// The current user is not allowed to edit the item.
|
||||
if ( 'list_current' === $mode ) {
|
||||
/* translators: accessibility text, %s is a native language name */
|
||||
$hint = sprintf( __( 'You are not allowed to edit this item in %s', 'polylang' ), $language->name );
|
||||
} else {
|
||||
/* translators: accessibility text, %s is a native language name */
|
||||
$hint = sprintf( __( 'You are not allowed to edit a translation in %s', 'polylang' ), $language->name );
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<span title="%s" class="%s wp-ui-text-icon">%s</span>',
|
||||
esc_attr( $hint ),
|
||||
esc_attr( $class ),
|
||||
$flag // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
|
||||
// The current user is allowed to edit the item.
|
||||
if ( 'list_current' === $mode ) {
|
||||
/* translators: accessibility text, %s is a native language name */
|
||||
$hint = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
|
||||
} elseif ( 'list_translation' === $mode ) {
|
||||
/* translators: accessibility text, %s is a native language name */
|
||||
$hint = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
|
||||
$class .= " translation_{$item_id}";
|
||||
} else {
|
||||
/* translators: accessibility text, %s is a native language name */
|
||||
$hint = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
|
||||
$item_name = $hint;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'<a href="%s" class="%s" title="%s"><span class="screen-reader-text">%s</span>%s</a>',
|
||||
esc_url( $url ),
|
||||
esc_attr( $class ),
|
||||
esc_attr( $item_name ),
|
||||
esc_html( $hint ),
|
||||
$flag // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the html markup for a new term translation.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_Term $term The source term.
|
||||
* @param string $post_type Post type name.
|
||||
* @param PLL_Language $language The language of the new translation.
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_term_link_html( WP_Term $term, string $post_type, PLL_Language $language ): string {
|
||||
$link = $this->get_new_term_translation_link( $term, $post_type, $language );
|
||||
return $this->new_translation_link( $link, $language );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL to create a new term translation.
|
||||
* Returns an empty string if the current user is not allowed to create terms in the given language.
|
||||
*
|
||||
* @since 1.5
|
||||
* @since 3.8 Changed first parameter type from `int` to `WP_Term`.
|
||||
* Removed 2nd parameter `$taxonomy`.
|
||||
*
|
||||
* @param WP_Term $term The source term.
|
||||
* @param string $post_type Post type name.
|
||||
* @param PLL_Language $language The language of the new translation.
|
||||
* @return string
|
||||
*/
|
||||
public function get_new_term_translation_link( WP_Term $term, string $post_type, PLL_Language $language ): string {
|
||||
if ( ! $this->user->can_translate( $language ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$tax = get_taxonomy( $term->taxonomy );
|
||||
if ( ! $tax || ! $this->user->has_cap( $tax->cap->edit_terms ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'taxonomy' => $term->taxonomy,
|
||||
'post_type' => $post_type,
|
||||
'from_tag' => $term->term_id,
|
||||
'new_lang' => $language->slug,
|
||||
);
|
||||
|
||||
$link = add_query_arg( $args, admin_url( 'edit-tags.php' ) );
|
||||
|
||||
/**
|
||||
* Filters the new term translation link.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string $link The new term translation link.
|
||||
* @param PLL_Language $language The language of the new translation.
|
||||
* @param int $term_id The source term id.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @param string $post_type Post type name.
|
||||
*/
|
||||
return apply_filters( 'pll_get_new_term_translation_link', $link, $language, $term->term_id, $term->taxonomy, $post_type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the html markup for a term translation link.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_Term $term The term.
|
||||
* @param string $post_type Post type name.
|
||||
* @param string $mode Optional. How the link should be displayed: with a pen icon or a language's flag.
|
||||
* Possible values are:
|
||||
* - `metabox_translation` (pen icon in metabox),
|
||||
* - `list_translation` (pen icon in items list),
|
||||
* - `list_current` (flag in items list).
|
||||
* Default is `metabox_translation`.
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-param 'metabox_translation'|'list_translation'|'list_current' $mode
|
||||
*/
|
||||
public function get_edit_term_link_html( WP_Term $term, string $post_type, string $mode = 'metabox_translation' ): string {
|
||||
$language = $this->model->term->get_language( $term->term_id );
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = (string) get_edit_term_link( $term->term_id, $term->taxonomy, $post_type );
|
||||
return $this->get_edit_item_link_html( $url, $language, $term->term_id, $term->name, $mode );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language flag or the language slug if there is no flag.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param PLL_Language $language PLL_Language object.
|
||||
* @return string
|
||||
*/
|
||||
public function get_flag_html( PLL_Language $language ): string {
|
||||
return $language->flag ?: sprintf( '<abbr>%s</abbr>', esc_html( $language->slug ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns some data (`from_post` and `new_lang`) from the current request.
|
||||
*
|
||||
* @since 3.7
|
||||
* @since 3.8 Removed parameter.
|
||||
*
|
||||
* @return array {
|
||||
* @type WP_Post $from_post The source post.
|
||||
* @type PLL_Language $new_lang The target language.
|
||||
* }
|
||||
*
|
||||
* @phpstan-return array{}|array{from_post: WP_Post, new_lang: PLL_Language}|never
|
||||
*/
|
||||
public function get_data_from_new_post_translation_request(): array {
|
||||
if ( isset( $_GET['from_media'] ) ) {
|
||||
return $this->get_data_from_new_media_translation_request();
|
||||
}
|
||||
|
||||
if ( ! isset( $GLOBALS['pagenow'], $GLOBALS['post_type'], $_GET['_wpnonce'], $_GET['from_post'], $_GET['new_lang'], $_GET['post_type'] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( 'post-new.php' !== $GLOBALS['pagenow'] || ! $this->model->is_translated_post_type( $GLOBALS['post_type'] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Capability check already done in post-new.php.
|
||||
check_admin_referer( 'new-post-translation' );
|
||||
return $this->get_objects_from_new_post_translation_request( (int) $_GET['from_post'], sanitize_key( $_GET['new_lang'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns some data (`from_post` and `new_lang`) from the current request.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return array {
|
||||
* @type WP_Post $from_post The source media.
|
||||
* @type PLL_Language $new_lang The target language.
|
||||
* }
|
||||
*
|
||||
* @phpstan-return array{}|array{from_post: WP_Post, new_lang: PLL_Language}|never
|
||||
*/
|
||||
public function get_data_from_new_media_translation_request(): array {
|
||||
if ( ! $this->options['media_support'] ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( ! isset( $_GET['action'], $_GET['_wpnonce'], $_GET['from_media'], $_GET['new_lang'] ) || 'translate_media' !== $_GET['action'] ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
check_admin_referer( 'translate_media' );
|
||||
return $this->get_objects_from_new_post_translation_request( (int) $_GET['from_media'], sanitize_key( $_GET['new_lang'] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the objects given the post ID and language slug provided in the new post translation request.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param int $post_id The original Post ID provided.
|
||||
* @param string $lang_slug The new translation language provided
|
||||
* @return array {
|
||||
* @type WP_Post $from_post The source post.
|
||||
* @type PLL_Language $new_lang The target language.
|
||||
* }
|
||||
*
|
||||
* @phpstan-return array{}|array{from_post: WP_Post, new_lang: PLL_Language}|never
|
||||
*/
|
||||
private function get_objects_from_new_post_translation_request( int $post_id, string $lang_slug ): array {
|
||||
if ( $post_id <= 0 || empty( $lang_slug ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
$lang = $this->model->get_language( $lang_slug );
|
||||
|
||||
if ( empty( $post ) || empty( $lang ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'from_post' => $post,
|
||||
'new_lang' => $lang,
|
||||
);
|
||||
}
|
||||
}
|
||||
11
wp-content/plugins/polylang/src/admin/admin-model.php
Normal file
11
wp-content/plugins/polylang/src/admin/admin-model.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extends the PLL_Model class with methods needed only in Polylang settings pages.
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin_Model extends PLL_Model {}
|
||||
315
wp-content/plugins/polylang/src/admin/admin-nav-menu.php
Normal file
315
wp-content/plugins/polylang/src/admin/admin-nav-menu.php
Normal file
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages custom menus translations as well as the language switcher menu item on admin side
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Admin $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
// Populates nav menus locations
|
||||
// Since WP 4.4, must be done before customize_register is fired
|
||||
add_filter( 'theme_mod_nav_menu_locations', array( $this, 'theme_mod_nav_menu_locations' ), 20 );
|
||||
|
||||
// Integration in the WP menu interface.
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) ); // after Polylang upgrade.
|
||||
add_action( 'load-nav-menus.php', array( $this, 'add_meta_box' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups filters and terms and create new nav menu locations.
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function admin_init() {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
|
||||
add_action( 'wp_update_nav_menu_item', array( $this, 'wp_update_nav_menu_item' ), 10, 2 );
|
||||
|
||||
// Translation of menus based on chosen locations.
|
||||
add_filter( 'pre_update_option_theme_mods_' . $this->theme, array( $this, 'pre_update_option_theme_mods' ) );
|
||||
add_action( 'delete_nav_menu', array( $this, 'delete_nav_menu' ) );
|
||||
|
||||
$this->create_nav_menu_locations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language switcher metabox.
|
||||
*
|
||||
* @since 3.7.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_meta_box() {
|
||||
// FIXME not displayed if Polylang is activated before the first time the user goes to nav menus http://core.trac.wordpress.org/ticket/16828
|
||||
add_meta_box( 'pll_lang_switch_box', __( 'Language switcher', 'polylang' ), array( $this, 'lang_switch' ), 'nav-menus', 'side', 'high' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Language switcher metabox
|
||||
* The checkbox and all hidden fields are important
|
||||
* Thanks to John Morris for his very interesting post http://www.johnmorrisonline.com/how-to-add-a-fully-functional-custom-meta-box-to-wordpress-navigation-menus/
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function lang_switch() {
|
||||
global $_nav_menu_placeholder, $nav_menu_selected_id;
|
||||
$_nav_menu_placeholder = 0 > $_nav_menu_placeholder ? $_nav_menu_placeholder - 1 : -1;
|
||||
?>
|
||||
<div id="posttype-lang-switch" class="posttypediv">
|
||||
<div id="tabs-panel-lang-switch" class="tabs-panel tabs-panel-active">
|
||||
<ul id="lang-switch-checklist" class="categorychecklist form-no-clear">
|
||||
<li>
|
||||
<label class="menu-item-title">
|
||||
<input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-object-id]" value="-1"> <?php esc_html_e( 'Languages', 'polylang' ); ?>
|
||||
</label>
|
||||
<input type="hidden" class="menu-item-type" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-type]" value="custom">
|
||||
<input type="hidden" class="menu-item-title" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-title]" value="<?php esc_attr_e( 'Languages', 'polylang' ); ?>">
|
||||
<input type="hidden" class="menu-item-url" name="menu-item[<?php echo (int) $_nav_menu_placeholder; ?>][menu-item-url]" value="#pll_switcher">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p class="button-controls">
|
||||
<span class="add-to-menu">
|
||||
<input type="submit" <?php disabled( $nav_menu_selected_id, 0 ); ?> class="button-secondary submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu', 'polylang' ); ?>" name="add-post-type-menu-item" id="submit-posttype-lang-switch">
|
||||
<span class="spinner"></span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares javascript to modify the language switcher menu item
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function admin_enqueue_scripts() {
|
||||
$screen = get_current_screen();
|
||||
if ( empty( $screen ) || 'nav-menus' !== $screen->base ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
|
||||
wp_enqueue_script( 'pll_nav_menu', plugins_url( "/js/build/nav-menu{$suffix}.js", POLYLANG_ROOT_FILE ), array(), POLYLANG_VERSION );
|
||||
|
||||
$data = array(
|
||||
'strings' => PLL_Switcher::get_switcher_options( 'menu', 'string' ), // The strings for the options
|
||||
'title' => __( 'Languages', 'polylang' ), // The title
|
||||
'val' => array(),
|
||||
);
|
||||
|
||||
// Get all language switcher menu items
|
||||
$items = get_posts(
|
||||
array(
|
||||
'numberposts' => -1,
|
||||
'nopaging' => true,
|
||||
'post_type' => 'nav_menu_item',
|
||||
'fields' => 'ids',
|
||||
'meta_key' => '_pll_menu_item',
|
||||
)
|
||||
);
|
||||
|
||||
// The options values for the language switcher
|
||||
foreach ( $items as $item ) {
|
||||
$data['val'][ $item ] = get_post_meta( $item, '_pll_menu_item', true );
|
||||
}
|
||||
|
||||
// Send all these data to javascript
|
||||
wp_localize_script( 'pll_nav_menu', 'pll_data', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save our menu item options.
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @param int $menu_id ID of the updated menu.
|
||||
* @param int $menu_item_db_id ID of the updated menu item.
|
||||
* @return void
|
||||
*/
|
||||
public function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0 ) {
|
||||
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_REQUEST['update-nav-menu-nonce'] ) || ! wp_verify_nonce( $_REQUEST['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $_POST['menu-item-url'] ) || ! is_array( $_POST['menu-item-url'] ) || empty( $_POST['menu-item-url'][ $menu_item_db_id ] ) || '#pll_switcher' !== $_POST['menu-item-url'][ $menu_item_db_id ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options = array( 'hide_if_no_translation' => 0, 'hide_current' => 0, 'force_home' => 0, 'show_flags' => 0, 'show_names' => 1, 'dropdown' => 0 ); // Default values.
|
||||
|
||||
// Our jQuery form has not been displayed.
|
||||
if ( empty( $_POST['menu-item-pll-detect'][ $menu_item_db_id ] ) ) {
|
||||
if ( ! get_post_meta( $menu_item_db_id, '_pll_menu_item', true ) ) { // Our options were never saved.
|
||||
update_post_meta( $menu_item_db_id, '_pll_menu_item', $options );
|
||||
}
|
||||
} else {
|
||||
foreach ( array_keys( $options ) as $opt ) {
|
||||
$options[ $opt ] = empty( $_POST[ 'menu-item-' . $opt ][ $menu_item_db_id ] ) ? 0 : 1;
|
||||
}
|
||||
update_post_meta( $menu_item_db_id, '_pll_menu_item', $options ); // Allow us to easily identify our nav menu item.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns menu languages and translations based on (temporary) locations.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param array $locations Nav menu locations.
|
||||
* @return array
|
||||
*/
|
||||
public function update_nav_menu_locations( $locations ) {
|
||||
// Extract language and menu from locations.
|
||||
$nav_menus = $this->options->get( 'nav_menus' );
|
||||
|
||||
foreach ( $locations as $loc => $menu ) {
|
||||
$infos = $this->explode_location( $loc );
|
||||
$nav_menus[ $this->theme ][ $infos['location'] ][ $infos['lang'] ] = $menu ?: 0;
|
||||
|
||||
if ( $this->options->get( 'default_lang' ) !== $infos['lang'] ) {
|
||||
unset( $locations[ $loc ] ); // Remove temporary locations before database update.
|
||||
}
|
||||
}
|
||||
|
||||
$this->options->set( 'nav_menus', $nav_menus );
|
||||
|
||||
return $locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns menu languages and translations based on (temporary) locations.
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @param mixed $mods Theme mods.
|
||||
* @return mixed
|
||||
*/
|
||||
public function pre_update_option_theme_mods( $mods ) {
|
||||
global $wp_customize;
|
||||
|
||||
if ( ! current_user_can( 'edit_theme_options' ) || ! is_array( $mods ) || ! isset( $mods['nav_menu_locations'] ) || ! is_array( $mods['nav_menu_locations'] ) ) {
|
||||
return $mods;
|
||||
}
|
||||
|
||||
/*
|
||||
* Manage Locations tab in Appearance -> Menus.
|
||||
*/
|
||||
if ( isset( $_GET['action'], $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'save-menu-locations' ) && 'locations' === $_GET['action'] ) {
|
||||
$nav_menus = $this->options->get( 'nav_menus' );
|
||||
|
||||
$nav_menus[ $this->theme ] = array();
|
||||
$this->options->set( 'nav_menus', $nav_menus );
|
||||
|
||||
$mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
|
||||
return $mods;
|
||||
}
|
||||
|
||||
/*
|
||||
* Edit Menus tab in Appearance -> Menus.
|
||||
*/
|
||||
if ( isset( $_POST['action'], $_REQUEST['update-nav-menu-nonce'] ) && wp_verify_nonce( $_REQUEST['update-nav-menu-nonce'], 'update-nav_menu' ) && 'update' === $_POST['action'] ) {
|
||||
$nav_menus = $this->options->get( 'nav_menus' );
|
||||
|
||||
$nav_menus[ $this->theme ] = array();
|
||||
$this->options->set( 'nav_menus', $nav_menus );
|
||||
|
||||
$mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
|
||||
return $mods;
|
||||
}
|
||||
|
||||
if ( ! $wp_customize instanceof WP_Customize_Manager ) {
|
||||
return $mods;
|
||||
}
|
||||
|
||||
/*
|
||||
* Customizer. Don't reset locations in this case.
|
||||
*/
|
||||
$action = 'save-customize_' . $wp_customize->get_stylesheet();
|
||||
if ( isset( $_POST['action'], $_REQUEST['nonce'] ) && wp_verify_nonce( $_REQUEST['nonce'], $action ) && 'customize_save' == $_POST['action'] ) {
|
||||
$mods['nav_menu_locations'] = $this->update_nav_menu_locations( $mods['nav_menu_locations'] );
|
||||
}
|
||||
|
||||
return $mods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills temporary menu locations based on menus translations
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param bool|array $menus Associative array of registered navigation menu IDs keyed by their location name.
|
||||
* @return bool|array
|
||||
*/
|
||||
public function theme_mod_nav_menu_locations( $menus ) {
|
||||
// Prefill locations with 0 value in case a location does not exist in $menus
|
||||
$locations = get_registered_nav_menus();
|
||||
if ( is_array( $locations ) ) {
|
||||
$locations = array_fill_keys( array_keys( $locations ), 0 );
|
||||
$menus = is_array( $menus ) ? array_merge( $locations, $menus ) : $locations;
|
||||
}
|
||||
|
||||
if ( is_array( $menus ) ) {
|
||||
foreach ( array_keys( $menus ) as $loc ) {
|
||||
foreach ( $this->model->get_languages_list() as $lang ) {
|
||||
if ( ! empty( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ] ) && term_exists( $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ], 'nav_menu' ) ) {
|
||||
$menus[ $this->combine_location( $loc, $lang ) ] = $this->options['nav_menus'][ $this->theme ][ $loc ][ $lang->slug ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the nav menu term_id from the locations stored in Polylang options when a nav menu is deleted
|
||||
*
|
||||
* @since 1.7.3
|
||||
*
|
||||
* @param int $term_id nav menu id
|
||||
* @return void
|
||||
*/
|
||||
public function delete_nav_menu( $term_id ) {
|
||||
$nav_menus = $this->options->get( 'nav_menus' );
|
||||
|
||||
if ( empty( $nav_menus ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $nav_menus as $theme => $locations ) {
|
||||
foreach ( $locations as $loc => $languages ) {
|
||||
foreach ( $languages as $lang => $menu_id ) {
|
||||
if ( $menu_id === $term_id ) {
|
||||
unset( $nav_menus[ $theme ][ $loc ][ $lang ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->options->set( 'nav_menus', $nav_menus );
|
||||
}
|
||||
}
|
||||
295
wp-content/plugins/polylang/src/admin/admin-notices.php
Normal file
295
wp-content/plugins/polylang/src/admin/admin-notices.php
Normal file
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class to manage admin notices
|
||||
* displayed only to admin, based on 'manage_options' capability
|
||||
* and only on dashboard, plugins and Polylang admin pages
|
||||
*
|
||||
* @since 2.3.9
|
||||
* @since 2.7 Dismissed notices are stored in an option instead of a user meta
|
||||
*/
|
||||
class PLL_Admin_Notices {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Stores custom notices.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private static $notices = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Setup actions
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @param PLL_Admin_Base $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( PLL_Admin_Base $polylang ) {
|
||||
$this->options = $polylang->options;
|
||||
|
||||
add_action( 'admin_init', array( $this, 'hide_notice' ) );
|
||||
add_action( 'admin_notices', array( $this, 'display_notices' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom notice
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @param string $name Notice name
|
||||
* @param string $html Content of the notice
|
||||
* @return void
|
||||
*/
|
||||
public static function add_notice( $name, $html ) {
|
||||
self::$notices[ $name ] = $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom notices.
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function get_notices() {
|
||||
return self::$notices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has a notice been dismissed?
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @param string $notice Notice name
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_dismissed( $notice ) {
|
||||
$dismissed = get_option( 'pll_dismissed_notices', array() );
|
||||
|
||||
// Handle legacy user meta
|
||||
$dismissed_meta = get_user_meta( get_current_user_id(), 'pll_dismissed_notices', true );
|
||||
if ( is_array( $dismissed_meta ) ) {
|
||||
if ( array_diff( $dismissed_meta, $dismissed ) ) {
|
||||
$dismissed = array_merge( $dismissed, $dismissed_meta );
|
||||
update_option( 'pll_dismissed_notices', $dismissed );
|
||||
}
|
||||
if ( ! is_multisite() ) {
|
||||
// Don't delete on multisite to avoid the notices to appear in other sites.
|
||||
delete_user_meta( get_current_user_id(), 'pll_dismissed_notices' );
|
||||
}
|
||||
}
|
||||
|
||||
return in_array( $notice, $dismissed );
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we display notices on this screen?
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @param string $notice The notice name.
|
||||
* @param array $allowed_screens The screens allowed to display the notice.
|
||||
* If empty, default screens are used, i.e. dashboard, plugins, languages, strings and settings.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function can_display_notice( string $notice, array $allowed_screens = array() ) {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( empty( $screen ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( empty( $allowed_screens ) ) {
|
||||
$allowed_screens = array(
|
||||
'dashboard',
|
||||
'plugins',
|
||||
PLL_Admin_Base::get_screen_id( 'lang' ),
|
||||
PLL_Admin_Base::get_screen_id( 'strings' ),
|
||||
PLL_Admin_Base::get_screen_id( 'settings' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters admin notices which can be displayed.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @param bool $display Whether the notice should be displayed or not.
|
||||
* @param string $notice The notice name.
|
||||
*/
|
||||
return apply_filters( 'pll_can_display_notice', in_array( $screen->id, $allowed_screens, true ), $notice );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a dismissed notice in the database.
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @param string $notice Notice name.
|
||||
* @return void
|
||||
*/
|
||||
public static function dismiss( $notice ) {
|
||||
$dismissed = get_option( 'pll_dismissed_notices', array() );
|
||||
|
||||
if ( ! in_array( $notice, $dismissed ) ) {
|
||||
$dismissed[] = $notice;
|
||||
update_option( 'pll_dismissed_notices', array_unique( $dismissed ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a click on the dismiss button
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function hide_notice() {
|
||||
if ( isset( $_GET['pll-hide-notice'], $_GET['_pll_notice_nonce'] ) ) {
|
||||
$notice = sanitize_key( $_GET['pll-hide-notice'] );
|
||||
check_admin_referer( $notice, '_pll_notice_nonce' );
|
||||
self::dismiss( $notice );
|
||||
wp_safe_redirect( remove_query_arg( array( 'pll-hide-notice', '_pll_notice_nonce' ), wp_get_referer() ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays notices
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function display_notices() {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
// Core notices
|
||||
if ( defined( 'WOOCOMMERCE_VERSION' ) && ! defined( 'PLLWC_VERSION' ) && $this->can_display_notice( 'pllwc' ) && ! static::is_dismissed( 'pllwc' ) ) {
|
||||
$this->pllwc_notice();
|
||||
}
|
||||
|
||||
if ( ! defined( 'POLYLANG_PRO' ) && $this->can_display_notice( 'review' ) && ! static::is_dismissed( 'review' ) && ! empty( $this->options['first_activation'] ) && time() > $this->options['first_activation'] + 15 * DAY_IN_SECONDS ) {
|
||||
$this->review_notice();
|
||||
}
|
||||
|
||||
$allowed_screen = PLL_Admin_Base::get_screen_id( 'strings' );
|
||||
if (
|
||||
( ! empty( $this->options['previous_version'] ) && version_compare( $this->options['previous_version'], '3.7.0', '<' ) )
|
||||
&& $this->can_display_notice( 'empty-strings-translations', (array) $allowed_screen )
|
||||
&& ! static::is_dismissed( 'empty-strings-translations' )
|
||||
) {
|
||||
$this->empty_strings_translations_notice();
|
||||
}
|
||||
|
||||
// Custom notices
|
||||
foreach ( static::get_notices() as $notice => $html ) {
|
||||
if ( $this->can_display_notice( $notice ) && ! static::is_dismissed( $notice ) ) {
|
||||
?>
|
||||
<div class="pll-notice notice notice-info">
|
||||
<?php
|
||||
$this->dismiss_button( $notice );
|
||||
echo wp_kses_post( $html );
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a dismiss button
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @param string $name Notice name
|
||||
* @return void
|
||||
*/
|
||||
public function dismiss_button( $name ) {
|
||||
printf(
|
||||
'<a class="notice-dismiss" href="%s"><span class="screen-reader-text">%s</span></a>',
|
||||
esc_url( wp_nonce_url( add_query_arg( 'pll-hide-notice', $name ), $name, '_pll_notice_nonce' ) ),
|
||||
/* translators: accessibility text */
|
||||
esc_html__( 'Dismiss this notice.', 'polylang' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notice if WooCommerce is activated without Polylang for WooCommerce
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function pllwc_notice() {
|
||||
?>
|
||||
<div class="pll-notice notice notice-warning">
|
||||
<?php $this->dismiss_button( 'pllwc' ); ?>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %1$s is link start tag, %2$s is link end tag. */
|
||||
esc_html__( 'We have noticed that you are using Polylang with WooCommerce. To ensure compatibility, we recommend you use %1$sPolylang for WooCommerce%2$s.', 'polylang' ),
|
||||
'<a href="https://polylang.pro/pricing/polylang-for-woocommerce/">',
|
||||
'</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notice asking for a review
|
||||
*
|
||||
* @since 2.3.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function review_notice() {
|
||||
?>
|
||||
<div class="pll-notice notice notice-info">
|
||||
<?php $this->dismiss_button( 'review' ); ?>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %1$s is link start tag, %2$s is link end tag. */
|
||||
esc_html__( 'We have noticed that you have been using Polylang for some time. We hope you love it, and we would really appreciate it if you would %1$sgive us a 5 stars rating%2$s.', 'polylang' ),
|
||||
'<a href="https://wordpress.org/support/plugin/polylang/reviews/?rate=5#new-post">',
|
||||
'</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notice about the empty strings translations.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function empty_strings_translations_notice() {
|
||||
?>
|
||||
<div class="pll-notice notice notice-info">
|
||||
<?php $this->dismiss_button( 'empty-strings-translations' ); ?>
|
||||
<p>
|
||||
<?php esc_html_e( 'Translations matching the original string are shown as empty in the table. Untranslated content remains unchanged.', 'polylang' ); ?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
158
wp-content/plugins/polylang/src/admin/admin-static-pages.php
Normal file
158
wp-content/plugins/polylang/src/admin/admin-static-pages.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages the static front page and the page for posts on admin side
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
class PLL_Admin_Static_Pages extends PLL_Static_Pages {
|
||||
/**
|
||||
* @var PLL_Admin_Links|null
|
||||
*/
|
||||
protected $links;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param object $polylang An array of attachment metadata.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
$this->links = &$polylang->links;
|
||||
|
||||
// Add post state for translations of the front page and posts page
|
||||
add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
|
||||
|
||||
// Refreshes the language cache when a static front page or page for for posts has been translated.
|
||||
add_action( 'pll_save_post', array( $this, 'pll_save_post' ), 10, 3 );
|
||||
|
||||
// Prevents WP resetting the option
|
||||
add_filter( 'pre_update_option_show_on_front', array( $this, 'update_show_on_front' ), 10, 2 );
|
||||
|
||||
add_action( 'admin_notices', array( $this, 'notice_must_translate' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds post state for translations of the front page and posts page.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string[] $post_states An array of post display states.
|
||||
* @param WP_Post $post The current post object.
|
||||
* @return string[]
|
||||
*/
|
||||
public function display_post_states( $post_states, $post ) {
|
||||
if ( in_array( $post->ID, $this->model->get_languages_list( array( 'fields' => 'page_on_front' ) ) ) ) {
|
||||
$post_states['page_on_front'] = __( 'Front Page', 'polylang' );
|
||||
}
|
||||
|
||||
if ( in_array( $post->ID, $this->model->get_languages_list( array( 'fields' => 'page_for_posts' ) ) ) ) {
|
||||
$post_states['page_for_posts'] = __( 'Posts Page', 'polylang' );
|
||||
}
|
||||
|
||||
return $post_states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the language cache when a static front page or page for for posts has been translated.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param int $post_id Not used.
|
||||
* @param WP_Post $post Not used.
|
||||
* @param int[] $translations Translations of the post being saved.
|
||||
* @return void
|
||||
*/
|
||||
public function pll_save_post( $post_id, $post, $translations ) {
|
||||
if ( in_array( $this->page_on_front, $translations ) || in_array( $this->page_for_posts, $translations ) ) {
|
||||
$this->model->clean_languages_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents WP resetting the option if the admin language filter is active for a language with no pages.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param string $value The new, unserialized option value.
|
||||
* @param string $old_value The old option value.
|
||||
* @return string
|
||||
*/
|
||||
public function update_show_on_front( $value, $old_value ) {
|
||||
if ( ! empty( $GLOBALS['pagenow'] ) && 'options-reading.php' === $GLOBALS['pagenow'] && 'posts' === $value && ! get_pages() && get_pages( array( 'lang' => '' ) ) ) {
|
||||
$value = $old_value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notice to translate the static front page if it is not translated in all languages
|
||||
* This is especially useful after a new language is created.
|
||||
* The notice is not dismissible and displayed on the Languages pages and the list of pages.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function notice_must_translate() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if ( ! empty( $screen ) && ( PLL_Admin_Base::get_screen_id( 'lang' ) === $screen->id || 'edit-page' === $screen->id ) ) {
|
||||
$message = $this->get_must_translate_message();
|
||||
|
||||
if ( ! empty( $message ) ) {
|
||||
printf(
|
||||
'<div class="error"><p>%s</p></div>',
|
||||
$message // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message asking to translate the static front page in all languages.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_must_translate_message() {
|
||||
if ( ! $this->page_on_front ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$page_on_front = get_post( $this->page_on_front );
|
||||
|
||||
if ( ! $page_on_front instanceof WP_Post ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$untranslated = array();
|
||||
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
if ( ! $this->model->post->get( $page_on_front->ID, $language ) ) {
|
||||
$untranslated[] = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
esc_url( $this->links->get_new_post_translation_link( $page_on_front, $language ) ),
|
||||
esc_html( $language->name )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $untranslated ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
/* translators: %s is a comma separated list of native language names */
|
||||
esc_html__( 'You must translate your static front page in %s.', 'polylang' ),
|
||||
implode( ', ', $untranslated ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
);
|
||||
}
|
||||
}
|
||||
152
wp-content/plugins/polylang/src/admin/admin-strings.php
Normal file
152
wp-content/plugins/polylang/src/admin/admin-strings.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* A fully static class to manage strings translations on admin side
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
class PLL_Admin_Strings {
|
||||
/**
|
||||
* Stores the strings to translate.
|
||||
*
|
||||
* @var array {
|
||||
* @type string $name A unique name for the string.
|
||||
* @type string $string The actual string to translate.
|
||||
* @type string $context The group in which the string is registered.
|
||||
* @type bool $multiline Whether the string table should display a multiline textarea or a single line input.
|
||||
* }
|
||||
*/
|
||||
protected static $strings = array();
|
||||
|
||||
/**
|
||||
* The strings to register by default.
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
protected static $default_strings;
|
||||
|
||||
/**
|
||||
* Add filters
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init() {
|
||||
// default strings translations sanitization
|
||||
add_filter( 'pll_sanitize_string_translation', array( self::class, 'sanitize_string_translation' ), 10, 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register strings for translation making sure it is not duplicate or empty
|
||||
*
|
||||
* @since 0.6
|
||||
*
|
||||
* @param string $name A unique name for the string
|
||||
* @param string $string The string to register
|
||||
* @param string $context Optional, the group in which the string is registered, defaults to 'polylang'
|
||||
* @param bool $multiline Optional, whether the string table should display a multiline textarea or a single line input, defaults to single line
|
||||
* @return void
|
||||
*/
|
||||
public static function register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
|
||||
if ( $string && is_scalar( $string ) ) {
|
||||
self::$strings[ md5( $string ) ] = compact( 'name', 'string', 'context', 'multiline' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered strings
|
||||
*
|
||||
* @since 0.6.1
|
||||
*
|
||||
* @return array list of all registered strings
|
||||
*/
|
||||
public static function &get_strings() {
|
||||
self::$default_strings = array(
|
||||
'widget_title' => __( 'Widget title', 'polylang' ),
|
||||
'widget_text' => __( 'Widget text', 'polylang' ),
|
||||
);
|
||||
|
||||
global $wp_registered_widgets;
|
||||
$sidebars = wp_get_sidebars_widgets();
|
||||
foreach ( $sidebars as $sidebar => $widgets ) {
|
||||
if ( 'wp_inactive_widgets' == $sidebar || empty( $widgets ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $widgets as $widget ) {
|
||||
// Nothing can be done if the widget is created using pre WP2.8 API. There is no object, so we can't access it to get the widget options.
|
||||
if ( ! isset( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! $wp_registered_widgets[ $widget ]['callback'][0] instanceof WP_Widget ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$widget_instance = $wp_registered_widgets[ $widget ]['callback'][0];
|
||||
$widget_settings = $widget_instance->get_settings();
|
||||
$number = $wp_registered_widgets[ $widget ]['params'][0]['number'];
|
||||
|
||||
// Don't enable widget translation if the widget is visible in only one language or if there is no title.
|
||||
if ( ! empty( $widget_settings[ $number ]['pll_lang'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Widget title.
|
||||
if ( ! empty( $widget_settings[ $number ]['title'] ) ) {
|
||||
self::register_string( self::$default_strings['widget_title'], $widget_settings[ $number ]['title'], 'Widget' );
|
||||
}
|
||||
|
||||
// Text of the Widget text.
|
||||
if ( ! empty( $widget_settings[ $number ]['text'] ) ) {
|
||||
self::register_string( self::$default_strings['widget_text'], $widget_settings[ $number ]['text'], 'Widget', true );
|
||||
}
|
||||
|
||||
// Content of the widget custom html.
|
||||
if ( $widget_instance instanceof WP_Widget_Custom_HTML && ! empty( $widget_settings[ $number ]['content'] ) ) {
|
||||
self::register_string( self::$default_strings['widget_text'], $widget_settings[ $number ]['content'], 'Widget', true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the list of strings registered for translation
|
||||
* Mainly for use by our PLL_WPML_Compat class
|
||||
*
|
||||
* @since 1.0.2
|
||||
*
|
||||
* @param array $strings list of strings
|
||||
*/
|
||||
self::$strings = apply_filters( 'pll_get_strings', self::$strings );
|
||||
return self::$strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the sanitization ( before saving in DB ) of default strings translations
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $translation The string translation.
|
||||
* @param string $name The name as defined in pll_register_string. Unused.
|
||||
* @param string $context The context as defined in pll_register_string. Unused.
|
||||
* @param string $original The original string to translate. Unused.
|
||||
* @param string $previous The previous string translation.
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize_string_translation( $translation, $name, $context, $original, $previous ) {
|
||||
if ( trim( $previous ) === trim( $translation ) ) {
|
||||
// Don't overwrite the translation to prevent breaking the string.
|
||||
return $translation;
|
||||
}
|
||||
|
||||
if ( $name === self::$default_strings['widget_title'] ) {
|
||||
return sanitize_text_field( $translation );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'unfiltered_html' ) ) {
|
||||
$translation = wp_kses_post( $translation );
|
||||
}
|
||||
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
180
wp-content/plugins/polylang/src/admin/admin.php
Normal file
180
wp-content/plugins/polylang/src/admin/admin.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Polylang class for admin (except Polylang pages), accessible from @see PLL().
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Admin extends PLL_Admin_Base {
|
||||
/**
|
||||
* @var PLL_Admin_Filters|null
|
||||
*/
|
||||
public $filters;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Filters_Columns|null
|
||||
*/
|
||||
public $filters_columns;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Filters_Post|null
|
||||
*/
|
||||
public $filters_post;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Filters_Term|null
|
||||
*/
|
||||
public $filters_term;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Filters_Media|null
|
||||
*/
|
||||
public $filters_media;
|
||||
|
||||
/**
|
||||
* @since 2.9
|
||||
*
|
||||
* @var PLL_Filters_Sanitization|null
|
||||
*/
|
||||
public $filters_sanitization;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Block_Editor|null
|
||||
*/
|
||||
public $block_editor;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Classic_Editor|null
|
||||
*/
|
||||
public $classic_editor;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Nav_Menu|null
|
||||
*/
|
||||
public $nav_menu;
|
||||
|
||||
/**
|
||||
* @var PLL_Admin_Filters_Widgets_Options|null
|
||||
*/
|
||||
public $filters_widgets_options;
|
||||
|
||||
/**
|
||||
* Setups filters and action needed on all admin pages and on plugins page.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Links_Model $links_model Reference to the links model.
|
||||
*/
|
||||
public function __construct( &$links_model ) {
|
||||
parent::__construct( $links_model );
|
||||
|
||||
// Adds a 'settings' link in the plugins table
|
||||
add_filter( 'plugin_action_links_' . POLYLANG_BASENAME, array( $this, 'plugin_action_links' ) );
|
||||
add_action( 'in_plugin_update_message-' . POLYLANG_BASENAME, array( $this, 'plugin_update_message' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups filters and action needed on all admin pages and on plugins page
|
||||
* Loads the settings pages or the filters base on the request
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
// Setup filters for admin pages
|
||||
// Priority 5 to make sure filters are there before customize_register is fired
|
||||
if ( $this->model->has_languages() ) {
|
||||
add_action( 'wp_loaded', array( $this, 'add_filters' ), 5 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a 'settings' link for our plugin in the plugins list table.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string[] $links List of links associated to the plugin.
|
||||
* @return string[] Modified list of links.
|
||||
*/
|
||||
public function plugin_action_links( $links ) {
|
||||
array_unshift( $links, '<a href="admin.php?page=mlang">' . __( 'Settings', 'polylang' ) . '</a>' );
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the upgrade notice in plugins table
|
||||
*
|
||||
* @since 1.1.6
|
||||
*
|
||||
* @param array $plugin_data Not used
|
||||
* @param object $r Plugin update data
|
||||
* @return void
|
||||
*/
|
||||
public function plugin_update_message( $plugin_data, $r ) {
|
||||
if ( ! empty( $r->upgrade_notice ) ) {
|
||||
printf( '<p style="margin: 3px 0 0 0; border-top: 1px solid #ddd; padding-top: 3px">%s</p>', esc_html( $r->upgrade_notice ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup filters for admin pages
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 2.7 instantiate a PLL_Bulk_Translate instance.
|
||||
* @return void
|
||||
*/
|
||||
public function add_filters() {
|
||||
$this->filters_sanitization = new PLL_Filters_Sanitization( $this->get_locale_for_sanitization() );
|
||||
$this->filters_widgets_options = new PLL_Admin_Filters_Widgets_Options( $this );
|
||||
|
||||
// All these are separated just for convenience and maintainability
|
||||
$classes = array( 'Filters', 'Filters_Columns', 'Filters_Post', 'Filters_Term', 'Nav_Menu', 'Classic_Editor', 'Block_Editor' );
|
||||
|
||||
// Don't load media filters if option is disabled or if user has no right
|
||||
if ( $this->options['media_support'] && ( $obj = get_post_type_object( 'attachment' ) ) && ( current_user_can( $obj->cap->edit_posts ) || current_user_can( $obj->cap->create_posts ) ) ) {
|
||||
$classes[] = 'Filters_Media';
|
||||
}
|
||||
|
||||
foreach ( $classes as $class ) {
|
||||
$obj = strtolower( $class );
|
||||
|
||||
/**
|
||||
* Filter the class to instantiate when loading admin filters
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param string $class class name
|
||||
*/
|
||||
$class = apply_filters( 'pll_' . $obj, 'PLL_Admin_' . $class );
|
||||
$this->$obj = new $class( $this );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the locale according to the current language instead of the language
|
||||
* of the admin interface.
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_locale_for_sanitization() {
|
||||
$locale = get_locale();
|
||||
|
||||
if ( isset( $_POST['post_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$locale = $lang->locale;
|
||||
} elseif ( isset( $_POST['term_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['term_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$locale = $lang->locale;
|
||||
} elseif ( isset( $_POST['inline_lang_choice'] ) && $lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$locale = $lang->locale;
|
||||
} elseif ( ! empty( $this->curlang ) ) {
|
||||
$locale = $this->curlang->locale;
|
||||
}
|
||||
|
||||
return $locale;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* Displays the translations fields for media
|
||||
*
|
||||
* @package Polylang
|
||||
*
|
||||
* @var PLL_Admin_Classic_Editor $this PLL_Admin_Classic_Editor object.
|
||||
* @var PLL_Language $lang The media language. Default language if no language assigned yet.
|
||||
* @var WP_Post $post The media object.
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
?>
|
||||
<p><strong><?php esc_html_e( 'Translations', 'polylang' ); ?></strong></p>
|
||||
<table>
|
||||
<?php
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
if ( $language->term_id === $lang->term_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translation_id = $this->model->post->get_translation( $post->ID, $language );
|
||||
$translation = null;
|
||||
|
||||
if ( ! empty( $translation_id ) && $translation_id !== $post->ID ) {
|
||||
$translation = get_post( $translation_id );
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td class = "pll-media-language-column"><span class = "pll-translation-flag"><?php echo $language->flag; // phpcs:ignore WordPress.Security.EscapeOutput ?></span><?php echo esc_html( $language->name ); ?></td>
|
||||
<td class = "pll-media-edit-column">
|
||||
<?php
|
||||
if ( $translation instanceof WP_Post ) {
|
||||
// The translation exists.
|
||||
printf(
|
||||
'<input type="hidden" name="media_tr_lang[%s]" value="%d" />',
|
||||
esc_attr( $language->slug ),
|
||||
(int) $translation->ID
|
||||
);
|
||||
echo $this->links->get_edit_post_link_html( $translation ); // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
} else {
|
||||
// The translation doesn't exist.
|
||||
echo $this->links->get_new_post_link_html( $post, $language ); // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
} // End foreach
|
||||
?>
|
||||
</table>
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Displays the translations fields for posts
|
||||
*
|
||||
* @package Polylang
|
||||
*
|
||||
* @var PLL_Admin_Classic_Editor $this PLL_Admin_Classic_Editor object.
|
||||
* @var PLL_Language $lang The post language. Default language if no language assigned yet.
|
||||
* @var WP_Post $post The post object.
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
?>
|
||||
<p><strong><?php esc_html_e( 'Translations', 'polylang' ); ?></strong></p>
|
||||
<table>
|
||||
<?php
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
if ( $language->term_id === $lang->term_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$translation_id = $this->model->post->get_translation( $post->ID, $language );
|
||||
if ( ! $translation_id || $translation_id === $post->ID ) { // $translation_id == $post->ID happens if the post has been (auto)saved before changing the language.
|
||||
$translation_id = 0;
|
||||
}
|
||||
|
||||
if ( ! empty( $from_post_id ) ) {
|
||||
$translation_id = $this->model->post->get( $from_post_id, $language );
|
||||
}
|
||||
|
||||
$add_link = $this->links->get_new_post_link_html( $post, $language );
|
||||
$link = $add_link;
|
||||
$translation = null;
|
||||
if ( $translation_id ) {
|
||||
$translation = get_post( $translation_id );
|
||||
|
||||
if ( $translation instanceof WP_Post ) {
|
||||
$link = $this->links->get_edit_post_link_html( $translation );
|
||||
}
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<th class = "pll-language-column"><?php echo $language->flag ?: esc_html( $language->slug ); // phpcs:ignore WordPress.Security.EscapeOutput ?></th>
|
||||
<td class = "hidden"><?php echo $add_link; // phpcs:ignore WordPress.Security.EscapeOutput ?></td>
|
||||
<td class = "pll-edit-column pll-column-icon"><?php echo $link; // phpcs:ignore WordPress.Security.EscapeOutput ?></td>
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Fires before the translation column is outputted in the language metabox.
|
||||
* The dynamic portion of the hook name, `$lang`, refers to the language code.
|
||||
*
|
||||
* @since 2.1
|
||||
* @since 3.8 New `$post_type` parameter.
|
||||
*
|
||||
* @param string $post_type The post type.
|
||||
*/
|
||||
do_action( 'pll_before_post_translation_' . $language->slug, $post->post_type );
|
||||
?>
|
||||
<td class = "pll-translation-column">
|
||||
<?php
|
||||
printf(
|
||||
'<label class="screen-reader-text" for="tr_lang_%1$s">%2$s</label>
|
||||
<input type="hidden" name="post_tr_lang[%1$s]" id="htr_lang_%1$s" value="%3$s" />
|
||||
<span lang="%5$s" dir="%6$s"><input type="text" class="tr_lang" id="tr_lang_%1$s" value="%4$s" /></span>',
|
||||
esc_attr( $language->slug ),
|
||||
/* translators: accessibility text */
|
||||
esc_html__( 'Translation', 'polylang' ),
|
||||
( empty( $translation ) ? '0' : esc_attr( (string) $translation->ID ) ),
|
||||
( empty( $translation ) ? '' : esc_attr( $translation->post_title ) ),
|
||||
esc_attr( $language->get_locale( 'display' ) ),
|
||||
( $language->is_rtl ? 'rtl' : 'ltr' )
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
102
wp-content/plugins/polylang/src/admin/view-translations-term.php
Normal file
102
wp-content/plugins/polylang/src/admin/view-translations-term.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* Displays the translations fields for terms
|
||||
*
|
||||
* @package Polylang
|
||||
*
|
||||
* @var PLL_Admin_Filters_Term $this PLL_Admin_Filters_Term object.
|
||||
* @var PLL_Language $lang The post language. Default language if no language assigned yet.
|
||||
* @var WP_Term|null $term The term object on edition mode, null on creation mode.
|
||||
* @var string $taxonomy Taxonomy name.
|
||||
* @var string $post_type Post type.
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! empty( $term ) ) {
|
||||
// Edit term form ?>
|
||||
<th scope="row"><?php esc_html_e( 'Translations', 'polylang' ); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
}
|
||||
else {
|
||||
// Add term form
|
||||
?>
|
||||
<p><?php esc_html_e( 'Translations', 'polylang' ); ?></p>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<table class="widefat term-translations" id="<?php echo ! empty( $term ) ? 'edit' : 'add'; ?>-term-translations">
|
||||
<?php
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
if ( $language->term_id == $lang->term_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Look for any existing translation in this language
|
||||
// Take care not to propose a self link
|
||||
$translation = null;
|
||||
if ( ! empty( $term ) && ( $translation_id = $this->model->term->get_translation( $term->term_id, $language ) ) && $translation_id !== $term->term_id ) {
|
||||
$translation = get_term( $translation_id, $taxonomy );
|
||||
}
|
||||
if ( ! empty( $from_term_id ) && ( $translation_id = $this->model->term->get( $from_term_id, $language ) ) && ! $this->model->term->get_translation( $translation_id, $lang ) ) {
|
||||
$translation = get_term( $translation_id, $taxonomy );
|
||||
}
|
||||
$translation_exists = $translation instanceof WP_Term;
|
||||
|
||||
$add_link = ! empty( $term ) ? $this->links->get_new_term_link_html( $term, $post_type, $language ) : ''; // Do not display the add new link in add term form ($term not set).
|
||||
$link = $add_link;
|
||||
|
||||
if ( $translation_exists ) {
|
||||
$link = $this->links->get_edit_term_link_html( $translation, $post_type );
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<th class = "pll-language-column">
|
||||
<span class = "pll-translation-flag"><?php echo $language->flag ?: esc_html( $language->slug ); // phpcs:ignore WordPress.Security.EscapeOutput ?></span>
|
||||
<?php
|
||||
printf(
|
||||
'<span class="pll-language-name%1$s">%2$s</span>',
|
||||
! empty( $term ) ? '' : ' screen-reader-text',
|
||||
esc_html( $language->name )
|
||||
);
|
||||
?>
|
||||
</th>
|
||||
<?php
|
||||
if ( ! empty( $term ) ) {
|
||||
?>
|
||||
<td class = "hidden"><?php echo $add_link; // phpcs:ignore WordPress.Security.EscapeOutput ?></td>
|
||||
<td class = "pll-edit-column"><?php echo $link; // phpcs:ignore WordPress.Security.EscapeOutput ?></td>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<td class = "pll-translation-column">
|
||||
<?php
|
||||
printf(
|
||||
'<label class="screen-reader-text" for="tr_lang_%1$s">%2$s</label>
|
||||
<input type="hidden" class="htr_lang" name="term_tr_lang[%1$s]" id="htr_lang_%1$s" value="%3$d" />
|
||||
<span lang="%6$s" dir="%7$s"><input type="text" class="tr_lang" id="tr_lang_%1$s" value="%4$s"%5$s /></span>',
|
||||
esc_attr( $language->slug ),
|
||||
/* translators: accessibility text */
|
||||
esc_html__( 'Translation', 'polylang' ),
|
||||
$translation_exists ? (int) $translation->term_id : 0,
|
||||
$translation_exists ? esc_attr( $translation->name ) : '',
|
||||
disabled( empty( $disabled ), false, false ),
|
||||
esc_attr( $language->get_locale( 'display' ) ),
|
||||
( $language->is_rtl ? 'rtl' : 'ltr' )
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
} // End foreach
|
||||
?>
|
||||
</table>
|
||||
<?php
|
||||
|
||||
if ( ! empty( $term ) ) {
|
||||
// Edit term form
|
||||
?>
|
||||
</td>
|
||||
<?php
|
||||
}
|
||||
676
wp-content/plugins/polylang/src/api.php
Normal file
676
wp-content/plugins/polylang/src/api.php
Normal file
@@ -0,0 +1,676 @@
|
||||
<?php
|
||||
/**
|
||||
* The Polylang public API.
|
||||
*
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Template tag: displays the language switcher.
|
||||
* The function does nothing if used outside the frontend.
|
||||
*
|
||||
* @api
|
||||
* @since 0.5
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional array of arguments.
|
||||
*
|
||||
* @type int $dropdown The list is displayed as dropdown if set to 1, defaults to 0.
|
||||
* @type int $echo Echoes the list if set to 1, defaults to 1.
|
||||
* @type int $hide_if_empty Hides languages with no posts ( or pages ) if set to 1, defaults to 1.
|
||||
* @type int $show_flags Displays flags if set to 1, defaults to 0.
|
||||
* @type int $show_names Shows language names if set to 1, defaults to 1.
|
||||
* @type string $display_names_as Whether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name.
|
||||
* @type int $force_home Will always link to the homepage in the translated language if set to 1, defaults to 0.
|
||||
* @type int $hide_if_no_translation Hides the link if there is no translation if set to 1, defaults to 0.
|
||||
* @type int $hide_current Hides the current language if set to 1, defaults to 0.
|
||||
* @type int $post_id Returns links to the translations of the post defined by post_id if set, defaults to not set.
|
||||
* @type int $raw Return a raw array instead of html markup if set to 1, defaults to 0.
|
||||
* @type string $item_spacing Whether to preserve or discard whitespace between list items, valid options are 'preserve' and 'discard', defaults to 'preserve'.
|
||||
* }
|
||||
* @return string|array Either the html markup of the switcher or the raw elements to build a custom language switcher.
|
||||
*/
|
||||
function pll_the_languages( $args = array() ) {
|
||||
if ( empty( PLL()->links ) ) {
|
||||
return empty( $args['raw'] ) ? '' : array();
|
||||
}
|
||||
|
||||
$switcher = new PLL_Switcher();
|
||||
return $switcher->the_languages( PLL()->links, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current language on frontend.
|
||||
* Returns the language set in admin language filter on backend (false if set to all languages).
|
||||
*
|
||||
* @api
|
||||
* @since 0.8.1
|
||||
* @since 3.4 Accepts composite values.
|
||||
*
|
||||
* @param string $field Optional, the language field to return (@see PLL_Language), defaults to `'slug'`.
|
||||
* Pass `\OBJECT` constant to get the language object. A composite value can be used for language
|
||||
* term property values, in the form of `{language_taxonomy_name}:{property_name}` (see
|
||||
* {@see PLL_Language::get_tax_prop()} for the possible values). Ex: `term_language:term_taxonomy_id`.
|
||||
* @return string|int|bool|string[]|PLL_Language The requested field or object for the current language, `false` if the field isn't set or if current language doesn't exist yet.
|
||||
*
|
||||
* @phpstan-return (
|
||||
* $field is \OBJECT ? PLL_Language : (
|
||||
* $field is 'slug' ? non-empty-string : string|int|bool|list<non-empty-string>
|
||||
* )
|
||||
* )|false
|
||||
*/
|
||||
function pll_current_language( $field = 'slug' ) {
|
||||
if ( empty( PLL()->curlang ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( \OBJECT === $field ) {
|
||||
return PLL()->curlang;
|
||||
}
|
||||
|
||||
return PLL()->curlang->get_prop( $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default language.
|
||||
*
|
||||
* @api
|
||||
* @since 1.0
|
||||
* @since 3.4 Accepts composite values.
|
||||
*
|
||||
* @param string $field Optional, the language field to return (@see PLL_Language), defaults to `'slug'`.
|
||||
* Pass `\OBJECT` constant to get the language object. A composite value can be used for language
|
||||
* term property values, in the form of `{language_taxonomy_name}:{property_name}` (see
|
||||
* {@see PLL_Language::get_tax_prop()} for the possible values). Ex: `term_language:term_taxonomy_id`.
|
||||
* @return string|int|bool|string[]|PLL_Language The requested field or object for the default language, `false` if the field isn't set or if default language doesn't exist yet.
|
||||
*
|
||||
* @phpstan-return (
|
||||
* $field is \OBJECT ? PLL_Language : (
|
||||
* $field is 'slug' ? non-empty-string : string|int|bool|list<non-empty-string>
|
||||
* )
|
||||
* )|false
|
||||
*/
|
||||
function pll_default_language( $field = 'slug' ) {
|
||||
$lang = PLL()->model->get_default_language();
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( \OBJECT === $field ) {
|
||||
return $lang;
|
||||
}
|
||||
|
||||
return $lang->get_prop( $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Among the post and its translations, returns the ID of the post which is in the language represented by $lang.
|
||||
*
|
||||
* @api
|
||||
* @since 0.5
|
||||
* @since 3.4 Returns `0` instead of `false` if not translated or if the post has no language.
|
||||
* @since 3.4 $lang accepts `PLL_Language` or string.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param PLL_Language|string $lang Optional language (object or slug), defaults to the current language.
|
||||
* @return int The translation post ID if exists. 0 if not translated, the post has no language or if the language doesn't exist.
|
||||
*
|
||||
* @phpstan-return int<0, max>
|
||||
*/
|
||||
function pll_get_post( $post_id, $lang = '' ) {
|
||||
$lang = $lang ?: pll_current_language();
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PLL()->model->post->get( $post_id, $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Among the term and its translations, returns the ID of the term which is in the language represented by $lang.
|
||||
*
|
||||
* @api
|
||||
* @since 0.5
|
||||
* @since 3.4 Returns `0` instead of `false` if not translated or if the term has no language.
|
||||
* @since 3.4 $lang accepts PLL_Language or string.
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @param PLL_Language|string $lang Optional language (object or slug), defaults to the current language.
|
||||
* @return int The translation term ID if exists. 0 if not translated, the term has no language or if the language doesn't exist.
|
||||
*
|
||||
* @phpstan-return int<0, max>
|
||||
*/
|
||||
function pll_get_term( $term_id, $lang = '' ) {
|
||||
$lang = $lang ?: pll_current_language();
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PLL()->model->term->get( $term_id, $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the home url in a language.
|
||||
*
|
||||
* @api
|
||||
* @since 0.8
|
||||
*
|
||||
* @param string $lang Optional language code, defaults to the current language.
|
||||
* @return string
|
||||
*/
|
||||
function pll_home_url( $lang = '' ) {
|
||||
if ( empty( $lang ) ) {
|
||||
$lang = pll_current_language();
|
||||
}
|
||||
|
||||
if ( empty( $lang ) || empty( PLL()->links ) ) {
|
||||
return home_url( '/' );
|
||||
}
|
||||
|
||||
return PLL()->links->get_home_url( $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a string for translation in the "strings translation" panel.
|
||||
*
|
||||
* @api
|
||||
* @since 0.6
|
||||
*
|
||||
* @param string $name A unique name for the string.
|
||||
* @param string $string The string to register.
|
||||
* @param string $context Optional, the group in which the string is registered, defaults to 'polylang'.
|
||||
* @param bool $multiline Optional, true if the string table should display a multiline textarea,
|
||||
* false if should display a single line input, defaults to false.
|
||||
* @return void
|
||||
*/
|
||||
function pll_register_string( $name, $string, $context = 'Polylang', $multiline = false ) {
|
||||
if ( PLL() instanceof PLL_Admin_Base ) {
|
||||
PLL_Admin_Strings::register_string( $name, $string, $context, $multiline );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string ( previously registered with pll_register_string ).
|
||||
*
|
||||
* @api
|
||||
* @since 0.6
|
||||
*
|
||||
* @param string $string The string to translate.
|
||||
* @return string The string translated in the current language.
|
||||
*/
|
||||
function pll__( $string ) {
|
||||
if ( ! is_scalar( $string ) || '' === $string ) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
return __( $string, 'pll_string' ); // PHPCS:ignore WordPress.WP.I18n
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string ( previously registered with pll_register_string ) and escapes it for safe use in HTML output.
|
||||
*
|
||||
* @api
|
||||
* @since 2.1
|
||||
*
|
||||
* @param string $string The string to translate.
|
||||
* @return string The string translated in the current language.
|
||||
*/
|
||||
function pll_esc_html__( $string ) {
|
||||
return esc_html( pll__( $string ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string ( previously registered with pll_register_string ) and escapes it for safe use in HTML attributes.
|
||||
*
|
||||
* @api
|
||||
* @since 2.1
|
||||
*
|
||||
* @param string $string The string to translate.
|
||||
* @return string The string translated in the current language.
|
||||
*/
|
||||
function pll_esc_attr__( $string ) {
|
||||
return esc_attr( pll__( $string ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Echoes a translated string ( previously registered with pll_register_string )
|
||||
* It is an equivalent of _e() and is not escaped.
|
||||
*
|
||||
* @api
|
||||
* @since 0.6
|
||||
*
|
||||
* @param string $string The string to translate.
|
||||
* @return void
|
||||
*/
|
||||
function pll_e( $string ) {
|
||||
echo pll__( $string ); // phpcs:ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* Echoes a translated string ( previously registered with pll_register_string ) and escapes it for safe use in HTML output.
|
||||
*
|
||||
* @api
|
||||
* @since 2.1
|
||||
*
|
||||
* @param string $string The string to translate.
|
||||
* @return void
|
||||
*/
|
||||
function pll_esc_html_e( $string ) {
|
||||
echo pll_esc_html__( $string ); // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Echoes a translated a string ( previously registered with pll_register_string ) and escapes it for safe use in HTML attributes.
|
||||
*
|
||||
* @api
|
||||
* @since 2.1
|
||||
*
|
||||
* @param string $string The string to translate.
|
||||
* @return void
|
||||
*/
|
||||
function pll_esc_attr_e( $string ) {
|
||||
echo pll_esc_attr__( $string ); // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a string ( previously registered with pll_register_string ).
|
||||
*
|
||||
* @api
|
||||
* @since 1.5.4
|
||||
*
|
||||
* @param string $string The string to translate.
|
||||
* @param string $lang Language code.
|
||||
* @return string The string translated in the requested language.
|
||||
*/
|
||||
function pll_translate_string( $string, $lang ) {
|
||||
if ( PLL() instanceof PLL_Frontend && pll_current_language() === $lang ) {
|
||||
return pll__( $string );
|
||||
}
|
||||
|
||||
if ( ! is_scalar( $string ) || '' === $string ) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$lang = PLL()->model->get_language( $lang );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$mo = new PLL_MO();
|
||||
$mo->import_from_db( $lang );
|
||||
|
||||
return $mo->translate( $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Polylang manages languages and translations for this post type.
|
||||
*
|
||||
* @api
|
||||
* @since 1.0.1
|
||||
*
|
||||
* @param string $post_type Post type name.
|
||||
* @return bool
|
||||
*/
|
||||
function pll_is_translated_post_type( $post_type ) {
|
||||
return PLL()->model->is_translated_post_type( $post_type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if Polylang manages languages and translations for this taxonomy.
|
||||
*
|
||||
* @api
|
||||
* @since 1.0.1
|
||||
*
|
||||
* @param string $tax Taxonomy name.
|
||||
* @return bool
|
||||
*/
|
||||
function pll_is_translated_taxonomy( $tax ) {
|
||||
return PLL()->model->is_translated_taxonomy( $tax );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available languages.
|
||||
*
|
||||
* @api
|
||||
* @since 1.5
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional array of arguments.
|
||||
*
|
||||
* @type bool $hide_empty Hides languages with no posts if set to true ( defaults to false ).
|
||||
* @type string $fields Return only that field if set ( @see PLL_Language for a list of fields ), defaults to 'slug'.
|
||||
* }
|
||||
* @return string[]
|
||||
*/
|
||||
function pll_languages_list( $args = array() ) {
|
||||
$args = wp_parse_args( $args, array( 'fields' => 'slug' ) );
|
||||
$hide_empty = ! empty( $args['hide_empty'] ) ? 'hide_empty' : '';
|
||||
$hide_default = ! empty( $args['hide_default'] ) ? 'hide_default' : '';
|
||||
unset( $args['hide_empty'], $args['hide_default'] );
|
||||
|
||||
return PLL()->model->languages
|
||||
->filter( $hide_empty )
|
||||
->filter( $hide_default )
|
||||
->get_list( $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the post language.
|
||||
*
|
||||
* @api
|
||||
* @since 1.5
|
||||
* @since 3.4 $lang accepts PLL_Language or string.
|
||||
* @since 3.4 Returns a boolean.
|
||||
*
|
||||
* @param int $id Post ID.
|
||||
* @param PLL_Language|string $lang Language (object or slug).
|
||||
* @return bool True when successfully assigned. False otherwise (or if the given language is already assigned to
|
||||
* the post).
|
||||
*/
|
||||
function pll_set_post_language( $id, $lang ) {
|
||||
return PLL()->model->post->set_language( $id, $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the term language.
|
||||
*
|
||||
* @api
|
||||
* @since 1.5
|
||||
* @since 3.4 $lang accepts PLL_Language or string.
|
||||
* @since 3.4 Returns a boolean.
|
||||
*
|
||||
* @param int $id Term ID.
|
||||
* @param PLL_Language|string $lang Language (object or slug).
|
||||
* @return bool True when successfully assigned. False otherwise (or if the given language is already assigned to
|
||||
* the term).
|
||||
*/
|
||||
function pll_set_term_language( $id, $lang ) {
|
||||
return PLL()->model->term->set_language( $id, $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Save posts translations.
|
||||
*
|
||||
* @api
|
||||
* @since 1.5
|
||||
* @since 3.4 Returns an associative array of translations.
|
||||
*
|
||||
* @param int[] $arr An associative array of translations with language code as key and post ID as value.
|
||||
* @return int[] An associative array with language codes as key and post IDs as values.
|
||||
*
|
||||
* @phpstan-return array<non-empty-string, positive-int>
|
||||
*/
|
||||
function pll_save_post_translations( $arr ) {
|
||||
$id = reset( $arr );
|
||||
if ( $id ) {
|
||||
return PLL()->model->post->save_translations( $id, $arr );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save terms translations
|
||||
*
|
||||
* @api
|
||||
* @since 1.5
|
||||
* @since 3.4 Returns an associative array of translations.
|
||||
*
|
||||
* @param int[] $arr An associative array of translations with language code as key and term ID as value.
|
||||
* @return int[] An associative array with language codes as key and term IDs as values.
|
||||
*
|
||||
* @phpstan-return array<non-empty-string, positive-int>
|
||||
*/
|
||||
function pll_save_term_translations( $arr ) {
|
||||
$id = reset( $arr );
|
||||
if ( $id ) {
|
||||
return PLL()->model->term->save_translations( $id, $arr );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the post language.
|
||||
*
|
||||
* @api
|
||||
* @since 1.5.4
|
||||
* @since 3.4 Accepts composite values for `$field`.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param string $field Optional, the language field to return (@see PLL_Language), defaults to `'slug'`.
|
||||
* Pass `\OBJECT` constant to get the language object. A composite value can be used for language
|
||||
* term property values, in the form of `{language_taxonomy_name}:{property_name}` (see
|
||||
* {@see PLL_Language::get_tax_prop()} for the possible values). Ex: `term_language:term_taxonomy_id`.
|
||||
* @return string|int|bool|string[]|PLL_Language The requested field or object for the post language, `false` if no language is associated to that post.
|
||||
*
|
||||
* @phpstan-return (
|
||||
* $field is \OBJECT ? PLL_Language : (
|
||||
* $field is 'slug' ? non-empty-string : string|int|bool|list<non-empty-string>
|
||||
* )
|
||||
* )|false
|
||||
*/
|
||||
function pll_get_post_language( $post_id, $field = 'slug' ) {
|
||||
$lang = PLL()->model->post->get_language( $post_id );
|
||||
|
||||
if ( empty( $lang ) || \OBJECT === $field ) {
|
||||
return $lang;
|
||||
}
|
||||
|
||||
return $lang->get_prop( $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the term language.
|
||||
*
|
||||
* @api
|
||||
* @since 1.5.4
|
||||
* @since 3.4 Accepts composite values for `$field`.
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @param string $field Optional, the language field to return (@see PLL_Language), defaults to `'slug'`.
|
||||
* Pass `\OBJECT` constant to get the language object. A composite value can be used for language
|
||||
* term property values, in the form of `{language_taxonomy_name}:{property_name}` (see
|
||||
* {@see PLL_Language::get_tax_prop()} for the possible values). Ex: `term_language:term_taxonomy_id`.
|
||||
* @return string|int|bool|string[]|PLL_Language The requested field or object for the post language, `false` if no language is associated to that term.
|
||||
*
|
||||
* @phpstan-return (
|
||||
* $field is \OBJECT ? PLL_Language : (
|
||||
* $field is 'slug' ? non-empty-string : string|int|bool|list<non-empty-string>
|
||||
* )
|
||||
* )|false
|
||||
*/
|
||||
function pll_get_term_language( $term_id, $field = 'slug' ) {
|
||||
$lang = PLL()->model->term->get_language( $term_id );
|
||||
|
||||
if ( empty( $lang ) || \OBJECT === $field ) {
|
||||
return $lang;
|
||||
}
|
||||
|
||||
return $lang->get_prop( $field );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of translations of a post.
|
||||
*
|
||||
* @api
|
||||
* @since 1.8
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return int[] An associative array of translations with language code as key and translation post ID as value.
|
||||
*
|
||||
* @phpstan-return array<non-empty-string, positive-int>
|
||||
*/
|
||||
function pll_get_post_translations( $post_id ) {
|
||||
return PLL()->model->post->get_translations( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of translations of a term.
|
||||
*
|
||||
* @api
|
||||
* @since 1.8
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @return int[] An associative array of translations with language code as key and translation term ID as value.
|
||||
*
|
||||
* @phpstan-return array<non-empty-string, positive-int>
|
||||
*/
|
||||
function pll_get_term_translations( $term_id ) {
|
||||
return PLL()->model->term->get_translations( $term_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts posts in a language.
|
||||
*
|
||||
* @api
|
||||
* @since 1.5
|
||||
*
|
||||
* @param string $lang Language code.
|
||||
* @param array $args {
|
||||
* Optional array of arguments.
|
||||
*
|
||||
* @type string $post_type Post type.
|
||||
* @type int $m YearMonth ( ex: 201307 ).
|
||||
* @type int $year 4 digit year.
|
||||
* @type int $monthnum Month number (from 1 to 12).
|
||||
* @type int $day Day of the month (from 1 to 31).
|
||||
* @type int $author Author id.
|
||||
* @type string $author_name Author nicename.
|
||||
* @type string $post_format Post format.
|
||||
* @type string $post_status Post status.
|
||||
* }
|
||||
* @return int Posts count.
|
||||
*/
|
||||
function pll_count_posts( $lang, $args = array() ) {
|
||||
$lang = PLL()->model->get_language( $lang );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PLL()->model->count_posts( $lang, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps `wp_insert_post` with language feature.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array $postarr {
|
||||
* An array of elements that make up a post to insert.
|
||||
* @See https://developer.wordpress.org/reference/functions/wp_insert_post/ wp_insert_post() for accepted arguments.
|
||||
*
|
||||
* @type string[] $translations The translation group to assign to the post with language slug as keys and post ID as values.
|
||||
* }
|
||||
* @param PLL_Language|string $language The post language object or slug.
|
||||
* @return int|WP_Error The post ID on success. The value `WP_Error` on failure.
|
||||
*/
|
||||
function pll_insert_post( array $postarr, $language ) {
|
||||
$language = PLL()->model->get_language( $language );
|
||||
|
||||
if ( ! $language instanceof PLL_Language ) {
|
||||
return new WP_Error( 'invalid_language', __( 'Please provide a valid language.', 'polylang' ) );
|
||||
}
|
||||
|
||||
return PLL()->model->post->insert( $postarr, $language );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps `wp_insert_term` with language feature.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $term The term name to add.
|
||||
* @param string $taxonomy The taxonomy to which to add the term.
|
||||
* @param PLL_Language|string $language The term language object or slug.
|
||||
* @param array $args {
|
||||
* Optional. Array of arguments for inserting a term.
|
||||
*
|
||||
* @type string $alias_of Slug of the term to make this term an alias of.
|
||||
* Default empty string. Accepts a term slug.
|
||||
* @type string $description The term description. Default empty string.
|
||||
* @type int $parent The id of the parent term. Default 0.
|
||||
* @type string $slug The term slug to use. Default empty string.
|
||||
* @type string[] $translations The translation group to assign to the term with language slug as keys and `term_id` as values.
|
||||
* }
|
||||
* @return array|WP_Error {
|
||||
* An array of the new term data, `WP_Error` otherwise.
|
||||
*
|
||||
* @type int $term_id The new term ID.
|
||||
* @type int|string $term_taxonomy_id The new term taxonomy ID. Can be a numeric string.
|
||||
* }
|
||||
*/
|
||||
function pll_insert_term( string $term, string $taxonomy, $language, array $args = array() ) {
|
||||
$language = PLL()->model->get_language( $language );
|
||||
|
||||
if ( ! $language instanceof PLL_Language ) {
|
||||
return new WP_Error( 'invalid_language', __( 'Please provide a valid language.', 'polylang' ) );
|
||||
}
|
||||
|
||||
return PLL()->model->term->insert( $term, $taxonomy, $language, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps `wp_update_post` with language feature.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param array $postarr {
|
||||
* Optional. An array of elements that make up a post to update.
|
||||
* @See https://developer.wordpress.org/reference/functions/wp_insert_post/ wp_insert_post() for accepted arguments.
|
||||
*
|
||||
* @type PLL_Language|string $lang The post language object or slug.
|
||||
* @type string[] $translations The translation group to assign to the post with language slug as keys and post ID as values.
|
||||
* }
|
||||
* @return int|WP_Error The post ID on success. The value `WP_Error` on failure.
|
||||
*/
|
||||
function pll_update_post( array $postarr ) {
|
||||
return PLL()->model->post->update( $postarr );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps `wp_update_term` with language feature.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param int $term_id The ID of the term.
|
||||
* @param array $args {
|
||||
* Optional. Array of arguments for updating a term.
|
||||
*
|
||||
* @type string $alias_of Slug of the term to make this term an alias of.
|
||||
* Default empty string. Accepts a term slug.
|
||||
* @type string $description The term description. Default empty string.
|
||||
* @type int $parent The id of the parent term. Default 0.
|
||||
* @type string $slug The term slug to use. Default empty string.
|
||||
* @type string $name The term name.
|
||||
* @type PLL_Language|string $lang The term language object or slug.
|
||||
* @type string[] $translations The translation group to assign to the term with language slug as keys and `term_id` as values.
|
||||
* }
|
||||
* @return array|WP_Error {
|
||||
* An array containing the `term_id` and `term_taxonomy_id`, `WP_Error` otherwise.
|
||||
*
|
||||
* @type int $term_id The new term ID.
|
||||
* @type int|string $term_taxonomy_id The new term taxonomy ID. Can be a numeric string.
|
||||
* }
|
||||
*/
|
||||
function pll_update_term( int $term_id, array $args = array() ) {
|
||||
return PLL()->model->term->update( $term_id, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to access the Polylang instance.
|
||||
* However, it is always preferable to use API functions
|
||||
* as internal methods may be changed without prior notice.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @return PLL_Frontend|PLL_Admin|PLL_Settings|PLL_REST_Request
|
||||
*/
|
||||
function PLL() { // PHPCS:ignore WordPress.NamingConventions.ValidFunctionName
|
||||
return $GLOBALS['polylang'];
|
||||
}
|
||||
237
wp-content/plugins/polylang/src/base.php
Normal file
237
wp-content/plugins/polylang/src/base.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\REST\Request;
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
|
||||
/**
|
||||
* Base class for both admin and frontend
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
abstract class PLL_Base {
|
||||
/**
|
||||
* Capabilities.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @var Capabilities
|
||||
*/
|
||||
public $capabilities;
|
||||
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
public $links_model;
|
||||
|
||||
/**
|
||||
* Registers hooks on insert / update post related actions and filters.
|
||||
*
|
||||
* @var PLL_CRUD_Posts|null
|
||||
*/
|
||||
public $posts;
|
||||
|
||||
/**
|
||||
* Registers hooks on insert / update term related action and filters.
|
||||
*
|
||||
* @var PLL_CRUD_Terms|null
|
||||
*/
|
||||
public $terms;
|
||||
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Links_Model $links_model Links Model.
|
||||
*/
|
||||
public function __construct( PLL_Links_Model &$links_model ) {
|
||||
$this->capabilities = new Capabilities();
|
||||
$this->links_model = &$links_model;
|
||||
$this->model = &$links_model->model;
|
||||
$this->options = $this->model->options;
|
||||
$this->request = new Request( $this->model );
|
||||
|
||||
$GLOBALS['l10n_unloaded']['pll_string'] = true; // Short-circuit _load_textdomain_just_in_time() for 'pll_string' domain in WP 4.6+
|
||||
|
||||
add_action( 'widgets_init', array( $this, 'widgets_init' ) );
|
||||
|
||||
// User defined strings translations
|
||||
add_action( 'pll_language_defined', array( $this, 'load_strings_translations' ), 5 );
|
||||
add_action( 'change_locale', array( $this, 'load_strings_translations' ) ); // Since WP 4.7
|
||||
add_action( 'personal_options_update', array( $this, 'load_strings_translations' ), 1, 0 ); // Before WP, for confirmation request when changing the user email.
|
||||
add_action( 'lostpassword_post', array( $this, 'load_strings_translations' ), 10, 0 ); // Password reset email.
|
||||
// Switch_to_blog
|
||||
add_action( 'switch_blog', array( $this, 'switch_blog' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates classes reacting to CRUD operations on posts and terms,
|
||||
* only when at least one language is defined.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
if ( $this->model->has_languages() ) {
|
||||
$this->posts = new PLL_CRUD_Posts( $this );
|
||||
$this->terms = new PLL_CRUD_Terms( $this );
|
||||
|
||||
// WordPress options.
|
||||
new PLL_Translate_Option( 'blogname', array(), array( 'context' => 'WordPress' ) );
|
||||
new PLL_Translate_Option( 'blogdescription', array(), array( 'context' => 'WordPress' ) );
|
||||
new PLL_Translate_Option( 'date_format', array(), array( 'context' => 'WordPress' ) );
|
||||
new PLL_Translate_Option( 'time_format', array(), array( 'context' => 'WordPress' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers our widgets
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function widgets_init() {
|
||||
register_widget( 'PLL_Widget_Languages' );
|
||||
|
||||
// Overwrites the calendar widget to filter posts by language
|
||||
if ( ! defined( 'PLL_WIDGET_CALENDAR' ) || PLL_WIDGET_CALENDAR ) {
|
||||
unregister_widget( 'WP_Widget_Calendar' );
|
||||
register_widget( 'PLL_Widget_Calendar' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads user defined strings translations
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 2.1.3 $locale parameter added.
|
||||
*
|
||||
* @param string $locale Language locale or slug. Defaults to current locale.
|
||||
* @return void
|
||||
*/
|
||||
public function load_strings_translations( $locale = '' ) {
|
||||
if ( empty( $locale ) ) {
|
||||
$locale = ( is_admin() && ! Polylang::is_ajax_on_front() ) ? get_user_locale() : get_locale();
|
||||
}
|
||||
|
||||
$language = $this->model->get_language( $locale );
|
||||
|
||||
if ( ! empty( $language ) ) {
|
||||
$mo = new PLL_MO();
|
||||
$mo->import_from_db( $language );
|
||||
$GLOBALS['l10n']['pll_string'] = &$mo;
|
||||
} else {
|
||||
unset( $GLOBALS['l10n']['pll_string'] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets some variables when the blog is switched.
|
||||
* Applied only if Polylang is active on the new blog.
|
||||
*
|
||||
* @since 1.5.1
|
||||
*
|
||||
* @param int $new_blog_id New blog ID.
|
||||
* @param int $prev_blog_id Previous blog ID.
|
||||
* @return void
|
||||
*/
|
||||
public function switch_blog( $new_blog_id, $prev_blog_id ) {
|
||||
if ( (int) $new_blog_id === (int) $prev_blog_id ) {
|
||||
// Do nothing if same blog.
|
||||
return;
|
||||
}
|
||||
|
||||
$this->links_model->remove_filters();
|
||||
|
||||
if ( $this->is_active_on_current_site() ) {
|
||||
$this->links_model = $this->model->get_links_model();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if Polylang is active on the current blog (useful when the blog is switched).
|
||||
*
|
||||
* @since 3.5.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_active_on_current_site(): bool {
|
||||
return pll_is_plugin_active( POLYLANG_BASENAME ) && ! empty( $this->options['version'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the customize menu should be removed or not.
|
||||
*
|
||||
* @since 3.2
|
||||
*
|
||||
* @return bool True if it should be removed, false otherwise.
|
||||
*/
|
||||
public function should_customize_menu_be_removed() {
|
||||
// Exit if a block theme isn't activated.
|
||||
if ( ! function_exists( 'wp_is_block_theme' ) || ! wp_is_block_theme() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! $this->is_customize_register_hooked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not Polylang or third party callbacks are hooked to `customize_register`.
|
||||
*
|
||||
* @since 3.4.3
|
||||
*
|
||||
* @global $wp_filter
|
||||
*
|
||||
* @return bool True if Polylang's callbacks are hooked, false otherwise.
|
||||
*/
|
||||
protected function is_customize_register_hooked() {
|
||||
global $wp_filter;
|
||||
|
||||
if ( empty( $wp_filter['customize_register'] ) || ! $wp_filter['customize_register'] instanceof WP_Hook ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* 'customize_register' is hooked by:
|
||||
* @see PLL_Nav_Menu::create_nav_menu_locations()
|
||||
* @see PLL_Frontend_Static_Pages::filter_customizer()
|
||||
*/
|
||||
$floor = 0;
|
||||
if ( ! empty( $this->nav_menu ) && (bool) $wp_filter['customize_register']->has_filter( 'customize_register', array( $this->nav_menu, 'create_nav_menu_locations' ) ) ) {
|
||||
++$floor;
|
||||
}
|
||||
|
||||
if ( ! empty( $this->static_pages ) && (bool) $wp_filter['customize_register']->has_filter( 'customize_register', array( $this->static_pages, 'filter_customizer' ) ) ) {
|
||||
++$floor;
|
||||
}
|
||||
|
||||
$count = array_sum( array_map( 'count', $wp_filter['customize_register']->callbacks ) );
|
||||
|
||||
return $count > $floor;
|
||||
}
|
||||
}
|
||||
121
wp-content/plugins/polylang/src/cache.php
Normal file
121
wp-content/plugins/polylang/src/cache.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* An extremely simple non persistent cache system.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @template TCacheData
|
||||
*/
|
||||
class PLL_Cache {
|
||||
/**
|
||||
* Current site id.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $blog_id;
|
||||
|
||||
/**
|
||||
* The cache container.
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @phpstan-var array<int, array<non-empty-string, TCacheData>>
|
||||
*/
|
||||
protected $cache = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->blog_id = get_current_blog_id();
|
||||
add_action( 'switch_blog', array( $this, 'switch_blog' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when switching blog.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param int $new_blog_id New blog ID.
|
||||
* @return void
|
||||
*/
|
||||
public function switch_blog( $new_blog_id ) {
|
||||
$this->blog_id = $new_blog_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a value in cache.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 3.6 Returns the cached value.
|
||||
*
|
||||
* @param string $key Cache key.
|
||||
* @param mixed $data The value to add to the cache.
|
||||
* @return mixed
|
||||
*
|
||||
* @phpstan-param non-empty-string $key
|
||||
* @phpstan-param TCacheData $data
|
||||
* @phpstan-return TCacheData
|
||||
*/
|
||||
public function set( $key, $data ) {
|
||||
$this->cache[ $this->blog_id ][ $key ] = $data;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value from cache.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string $key Cache key.
|
||||
* @return mixed
|
||||
*
|
||||
* @phpstan-param non-empty-string $key
|
||||
* @phpstan-return TCacheData|false
|
||||
*/
|
||||
public function get( $key ) {
|
||||
return $this->cache[ $this->blog_id ][ $key ] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans the cache (for this blog only).
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string $key Optional. Cache key. An empty string to clean the whole cache for the current blog.
|
||||
* Default is an empty string.
|
||||
* @return void
|
||||
*/
|
||||
public function clean( $key = '' ) {
|
||||
if ( '' === $key ) {
|
||||
unset( $this->cache[ $this->blog_id ] );
|
||||
} else {
|
||||
unset( $this->cache[ $this->blog_id ][ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and returns a "unique" cache key, depending on `$data` and prefixed by `$prefix`.
|
||||
*
|
||||
* @since 3.6
|
||||
*
|
||||
* @param string $prefix String to prefix the cache key.
|
||||
* @param string|array|object $data Data.
|
||||
* @return string
|
||||
*
|
||||
* @phpstan-param non-empty-string $prefix
|
||||
* @phpstan-return non-empty-string
|
||||
*/
|
||||
public function get_unique_key( string $prefix, $data ): string {
|
||||
/** @var scalar */
|
||||
$serialized = maybe_serialize( $data );
|
||||
return $prefix . md5( (string) $serialized );
|
||||
}
|
||||
}
|
||||
293
wp-content/plugins/polylang/src/class-polylang.php
Normal file
293
wp-content/plugins/polylang/src/class-polylang.php
Normal file
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Options\Registry as Options_Registry;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Don't access directly
|
||||
}
|
||||
|
||||
// Default directory to store user data such as custom flags
|
||||
if ( ! defined( 'PLL_LOCAL_DIR' ) ) {
|
||||
define( 'PLL_LOCAL_DIR', WP_CONTENT_DIR . '/polylang' );
|
||||
}
|
||||
|
||||
// Includes local config file if exists
|
||||
if ( is_readable( PLL_LOCAL_DIR . '/pll-config.php' ) ) {
|
||||
include_once PLL_LOCAL_DIR . '/pll-config.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the plugin, as well as activation, and deactivation
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @template TPLLClass of PLL_Base
|
||||
*/
|
||||
class Polylang {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 0.1
|
||||
*/
|
||||
public function __construct() {
|
||||
require_once __DIR__ . '/functions.php'; // VIP functions
|
||||
|
||||
// Plugin initialization
|
||||
// Take no action before all plugins are loaded
|
||||
add_action( 'plugins_loaded', array( $this, 'init' ), 1 );
|
||||
|
||||
// Override load text domain waiting for the language to be defined
|
||||
// Here for plugins which load text domain as soon as loaded :(
|
||||
if ( ! defined( 'PLL_OLT' ) || PLL_OLT ) {
|
||||
PLL_OLT_Manager::instance();
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads the compatibility with some plugins and themes.
|
||||
* Loaded as soon as possible as we may need to act before other plugins are loaded.
|
||||
*/
|
||||
if ( ! defined( 'PLL_PLUGINS_COMPAT' ) || PLL_PLUGINS_COMPAT ) {
|
||||
PLL_Integrations::instance();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the current request is an ajax request on frontend or not
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_ajax_on_front() {
|
||||
// Special test for plupload which does not use jquery ajax and thus does not pass our ajax prefilter
|
||||
// Special test for customize_save done in frontend but for which we want to load the admin
|
||||
$in = isset( $_REQUEST['action'] ) && in_array( sanitize_key( $_REQUEST['action'] ), array( 'upload-attachment', 'customize_save' ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$is_ajax_on_front = wp_doing_ajax() && empty( $_REQUEST['pll_ajax_backend'] ) && ! $in; // phpcs:ignore WordPress.Security.NonceVerification
|
||||
|
||||
/**
|
||||
* Filters whether the current request is an ajax request on front.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @param bool $is_ajax_on_front Whether the current request is an ajax request on front.
|
||||
*/
|
||||
return apply_filters( 'pll_is_ajax_on_front', $is_ajax_on_front );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current request a REST API request?
|
||||
* Inspired by WP::parse_request()
|
||||
* Needed because at this point, the constant REST_REQUEST is not defined yet
|
||||
*
|
||||
* @since 2.4.1
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_rest_request() {
|
||||
// Handle pretty permalinks.
|
||||
$home_path = trim( (string) wp_parse_url( home_url(), PHP_URL_PATH ), '/' );
|
||||
$home_path_regex = sprintf( '|^%s|i', preg_quote( $home_path, '|' ) );
|
||||
|
||||
$req_uri = trim( (string) wp_parse_url( pll_get_requested_url(), PHP_URL_PATH ), '/' );
|
||||
$req_uri = (string) preg_replace( $home_path_regex, '', $req_uri );
|
||||
$req_uri = trim( $req_uri, '/' );
|
||||
$req_uri = str_replace( 'index.php', '', $req_uri );
|
||||
$req_uri = trim( $req_uri, '/' );
|
||||
|
||||
// And also test rest_route query string parameter is not empty for plain permalinks.
|
||||
$query_string = array();
|
||||
wp_parse_str( (string) wp_parse_url( pll_get_requested_url(), PHP_URL_QUERY ), $query_string );
|
||||
$rest_route = isset( $query_string['rest_route'] ) && is_string( $query_string['rest_route'] ) ? trim( $query_string['rest_route'], '/' ) : false;
|
||||
|
||||
return 0 === strpos( $req_uri, rest_get_url_prefix() . '/' ) || ! empty( $rest_route );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if we are in the wizard process.
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_wizard() {
|
||||
return isset( $_GET['page'] ) && ! empty( $_GET['page'] ) && 'mlang_wizard' === sanitize_key( $_GET['page'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines constants
|
||||
* May be overridden by a plugin if set before plugins_loaded, 1
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function define_constants() {
|
||||
// Cookie name. no cookie will be used if set to false
|
||||
if ( ! defined( 'PLL_COOKIE' ) ) {
|
||||
define( 'PLL_COOKIE', 'pll_language' );
|
||||
}
|
||||
|
||||
// Backward compatibility with Polylang < 2.3
|
||||
if ( ! defined( 'PLL_AJAX_ON_FRONT' ) ) {
|
||||
define( 'PLL_AJAX_ON_FRONT', self::is_ajax_on_front() );
|
||||
}
|
||||
|
||||
// Admin
|
||||
if ( ! defined( 'PLL_ADMIN' ) ) {
|
||||
define( 'PLL_ADMIN', wp_doing_cron() || ( defined( 'WP_CLI' ) && WP_CLI ) || ( is_admin() && ! PLL_AJAX_ON_FRONT ) );
|
||||
}
|
||||
|
||||
// Settings page whatever the tab except for the wizard which needs to be an admin process.
|
||||
if ( ! defined( 'PLL_SETTINGS' ) ) {
|
||||
define( 'PLL_SETTINGS', is_admin() && ( ( isset( $_GET['page'] ) && 0 === strpos( sanitize_key( $_GET['page'] ), 'mlang' ) && ! self::is_wizard() ) || ! empty( $_REQUEST['pll_ajax_settings'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Polylang initialization
|
||||
* setups models and separate admin and frontend
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
self::define_constants();
|
||||
|
||||
// Plugin options.
|
||||
add_action( 'pll_init_options_for_blog', array( Options_Registry::class, 'register' ) );
|
||||
$options = new Options();
|
||||
|
||||
// Plugin upgrade
|
||||
if ( ! empty( $options['version'] ) ) {
|
||||
if ( version_compare( $options['version'], POLYLANG_VERSION, '<' ) ) {
|
||||
$upgrade = new PLL_Upgrade( $options );
|
||||
if ( ! $upgrade->upgrade() ) { // If the version is too old
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In some edge cases, it's possible that no options were found in the database.
|
||||
$options['version'] = POLYLANG_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the model class to use
|
||||
* /!\ this filter is fired *before* the $polylang object is available
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param string $class either PLL_Model or PLL_Admin_Model
|
||||
*/
|
||||
$class = apply_filters( 'pll_model', PLL_SETTINGS || self::is_wizard() ? 'PLL_Admin_Model' : 'PLL_Model' );
|
||||
/** @var PLL_Model $model */
|
||||
$model = new $class( $options );
|
||||
|
||||
if ( ! $model->has_languages() ) {
|
||||
/**
|
||||
* Fires when no language has been defined yet
|
||||
* Used to load overridden textdomains
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
do_action( 'pll_no_language_defined' );
|
||||
}
|
||||
|
||||
$class = '';
|
||||
|
||||
if ( PLL_SETTINGS ) {
|
||||
$class = 'PLL_Settings';
|
||||
} elseif ( PLL_ADMIN ) {
|
||||
$class = 'PLL_Admin';
|
||||
} elseif ( self::is_rest_request() ) {
|
||||
$class = 'PLL_REST_Request';
|
||||
} elseif ( $model->has_languages() ) {
|
||||
$class = 'PLL_Frontend';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the class to use to instantiate the $polylang object
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param string $class A class name.
|
||||
*/
|
||||
$class = apply_filters( 'pll_context', $class );
|
||||
|
||||
if ( ! empty( $class ) ) {
|
||||
/** @phpstan-var class-string<TPLLClass> $class */
|
||||
$this->init_context( $class, $model );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Polylang initialization.
|
||||
* Setups the Polylang Context, loads the modules and init Polylang.
|
||||
*
|
||||
* @since 3.6
|
||||
*
|
||||
* @param string $class The class name.
|
||||
* @param PLL_Model $model Instance of PLL_Model.
|
||||
* @return PLL_Base
|
||||
*
|
||||
* @phpstan-param class-string<TPLLClass> $class
|
||||
* @phpstan-return TPLLClass
|
||||
*/
|
||||
public function init_context( string $class, PLL_Model $model ): PLL_Base {
|
||||
global $polylang;
|
||||
|
||||
$links_model = $model->get_links_model();
|
||||
$polylang = new $class( $links_model );
|
||||
|
||||
/**
|
||||
* Fires after Polylang's model init.
|
||||
* This is the best place to register a custom table (see `PLL_Model`'s constructor).
|
||||
* /!\ This hook is fired *before* the $polylang object is available.
|
||||
* /!\ The languages are also not available yet.
|
||||
*
|
||||
* @since 3.4
|
||||
*
|
||||
* @param PLL_Model $model Polylang model.
|
||||
*/
|
||||
do_action( 'pll_model_init', $model );
|
||||
|
||||
$model->maybe_create_language_terms();
|
||||
|
||||
/**
|
||||
* Fires after the $polylang object is created and before the API is loaded
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @param object $polylang
|
||||
*/
|
||||
do_action_ref_array( 'pll_pre_init', array( &$polylang ) );
|
||||
|
||||
// Loads the API
|
||||
require_once POLYLANG_DIR . '/src/api.php';
|
||||
|
||||
// Loads the modules.
|
||||
$load_scripts = require POLYLANG_DIR . '/src/modules/module-build.php';
|
||||
|
||||
foreach ( $load_scripts as $load_script ) {
|
||||
require_once POLYLANG_DIR . "/src/modules/{$load_script}/load.php";
|
||||
}
|
||||
|
||||
$polylang->init();
|
||||
|
||||
/**
|
||||
* Fires after the $polylang object and the API is loaded
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param object $polylang
|
||||
*/
|
||||
do_action_ref_array( 'pll_init', array( &$polylang ) );
|
||||
|
||||
return $polylang;
|
||||
}
|
||||
}
|
||||
66
wp-content/plugins/polylang/src/constant-functions.php
Normal file
66
wp-content/plugins/polylang/src/constant-functions.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*
|
||||
* The aim of these functions is to be stubed in unit tests.
|
||||
*
|
||||
* /!\ THE CODE IN THIS FILE MUST BE COMPATIBLE WITH PHP 5.6.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tells if a constant is defined.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param string $constant_name Name of the constant.
|
||||
* @return bool True if the constant is defined, false otherwise.
|
||||
*
|
||||
* @phpstan-param non-falsy-string $constant_name
|
||||
*/
|
||||
function pll_has_constant( $constant_name ) {
|
||||
return defined( $constant_name ); // phpcs:ignore WordPressVIPMinimum.Constants.ConstantString.NotCheckingConstantName
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a constant if it is defined.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param string $constant_name Name of the constant.
|
||||
* @param mixed $default Optional. Value to return if the constant is not defined. Defaults to `null`.
|
||||
* @return mixed The value of the constant.
|
||||
*
|
||||
* @phpstan-template D of int|float|string|bool|array|null
|
||||
* @phpstan-param non-falsy-string $constant_name
|
||||
* @phpstan-param D $default
|
||||
* @phpstan-return D
|
||||
*/
|
||||
function pll_get_constant( $constant_name, $default = null ) {
|
||||
if ( ! pll_has_constant( $constant_name ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
/** @phpstan-var D $return */
|
||||
$return = constant( $constant_name );
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a constant if it is not already defined.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param string $constant_name Name of the constant.
|
||||
* @param mixed $value Value to set.
|
||||
* @return bool True on success, false on failure or already defined.
|
||||
*
|
||||
* @phpstan-param non-falsy-string $constant_name
|
||||
* @phpstan-param int|float|string|bool|array|null $value
|
||||
*/
|
||||
function pll_set_constant( $constant_name, $value ) {
|
||||
if ( pll_has_constant( $constant_name ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return define( $constant_name, $value ); // phpcs:ignore WordPressVIPMinimum.Constants.ConstantString.NotCheckingConstantName
|
||||
}
|
||||
102
wp-content/plugins/polylang/src/cookie.php
Normal file
102
wp-content/plugins/polylang/src/cookie.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class to manage manage the language cookie
|
||||
*
|
||||
* @since 2.9
|
||||
*/
|
||||
class PLL_Cookie {
|
||||
/**
|
||||
* Parses the cookie parameters.
|
||||
*
|
||||
* @since 2.9
|
||||
*
|
||||
* @param array $args {@see PLL_Cookie::set()}
|
||||
* @return array
|
||||
*/
|
||||
protected static function parse_args( $args ) {
|
||||
/**
|
||||
* Filters the Polylang cookie duration.
|
||||
*
|
||||
* If a cookie duration of 0 is specified, a session cookie will be set.
|
||||
* If a negative cookie duration is specified, the cookie is removed.
|
||||
* /!\ This filter may be fired *before* the theme is loaded.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param int $duration Cookie duration in seconds.
|
||||
*/
|
||||
$expiration = (int) apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS );
|
||||
|
||||
$defaults = array(
|
||||
'expires' => 0 !== $expiration ? time() + $expiration : 0,
|
||||
'path' => COOKIEPATH,
|
||||
'domain' => COOKIE_DOMAIN, // Cookie domain must be set to false for localhost (default value for `COOKIE_DOMAIN`) thanks to Stephen Harris.
|
||||
'secure' => is_ssl(),
|
||||
'httponly' => false,
|
||||
'samesite' => 'Lax',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
/**
|
||||
* Filters the Polylang cookie arguments.
|
||||
* /!\ This filter may be fired *before* the theme is loaded.
|
||||
*
|
||||
* @since 3.6
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional. Array of arguments for setting the cookie.
|
||||
*
|
||||
* @type int $expires Cookie duration.
|
||||
* If a cookie duration of 0 is specified, a session cookie will be set.
|
||||
* If a negative cookie duration is specified, the cookie is removed.
|
||||
* @type string $path Cookie path.
|
||||
* @type string $domain Cookie domain. Must be set to false for localhost (default value for `COOKIE_DOMAIN`).
|
||||
* @type bool $secure Should the cookie be sent only over https?
|
||||
* @type bool $httponly Should the cookie be accessed only over http protocol?.
|
||||
* @type string $samesite Either 'Strict', 'Lax' or 'None'.
|
||||
* }
|
||||
*/
|
||||
return (array) apply_filters( 'pll_cookie_args', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cookie.
|
||||
*
|
||||
* @since 2.9
|
||||
*
|
||||
* @param string $lang Language cookie value.
|
||||
* @param array $args {
|
||||
* Optional. Array of arguments for setting the cookie.
|
||||
*
|
||||
* @type string $path Cookie path, defaults to COOKIEPATH.
|
||||
* @type string $domain Cookie domain, defaults to COOKIE_DOMAIN
|
||||
* @type bool $secure Should the cookie be sent only over https?
|
||||
* @type bool $httponly Should the cookie accessed only over http protocol? Defaults to false.
|
||||
* @type string $samesite Either 'Strict', 'Lax' or 'None', defaults to 'Lax'.
|
||||
* }
|
||||
* @return void
|
||||
*/
|
||||
public static function set( $lang, $args = array() ) {
|
||||
$args = self::parse_args( $args );
|
||||
|
||||
if ( ! headers_sent() && PLL_COOKIE !== false && self::get() !== $lang ) {
|
||||
setcookie( PLL_COOKIE, $lang, $args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language cookie value.
|
||||
*
|
||||
* @since 2.9
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get() {
|
||||
return isset( $_COOKIE[ PLL_COOKIE ] ) ? sanitize_key( $_COOKIE[ PLL_COOKIE ] ) : '';
|
||||
}
|
||||
}
|
||||
445
wp-content/plugins/polylang/src/crud-posts.php
Normal file
445
wp-content/plugins/polylang/src/crud-posts.php
Normal file
@@ -0,0 +1,445 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\REST\Request;
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
use WP_Syntex\Polylang\Capabilities\Create\Post as Create_Post;
|
||||
|
||||
/**
|
||||
* Adds actions and filters related to languages when creating, updating or deleting posts.
|
||||
* Actions and filters triggered when reading posts are handled separately.
|
||||
*
|
||||
* @since 2.4
|
||||
*/
|
||||
class PLL_CRUD_Posts {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* Preferred language to assign to a new post.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
protected $pref_lang;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
protected $curlang;
|
||||
|
||||
/**
|
||||
* Reference to the Polylang options array.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Reference to the Polylang Request object.
|
||||
*
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param PLL_Base $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( PLL_Base &$polylang ) {
|
||||
$this->options = $polylang->options;
|
||||
$this->model = &$polylang->model;
|
||||
$this->pref_lang = &$polylang->pref_lang;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
$this->request = &$polylang->request;
|
||||
|
||||
add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
|
||||
add_action( 'set_object_terms', array( $this, 'set_object_terms' ), 10, 4 );
|
||||
add_filter( 'wp_insert_post_parent', array( $this, 'wp_insert_post_parent' ), 10, 2 );
|
||||
add_action( 'before_delete_post', array( $this, 'delete_post' ) );
|
||||
add_action( 'post_updated', array( $this, 'force_tags_translation' ), 10, 3 );
|
||||
|
||||
// Specific for media
|
||||
if ( $polylang->options['media_support'] ) {
|
||||
add_action( 'add_attachment', array( $this, 'set_default_language' ) );
|
||||
add_action( 'delete_attachment', array( $this, 'delete_post' ) );
|
||||
add_filter( 'wp_delete_file', array( $this, 'wp_delete_file' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set a language by default for posts if it has no language yet.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return void
|
||||
*/
|
||||
public function set_default_language( $post_id ) {
|
||||
if ( is_multisite() && ms_is_switched() && ! $this->model->has_languages() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->model->post->get_language( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_language = new Create_Post(
|
||||
$this->model,
|
||||
$this->request,
|
||||
$this->pref_lang instanceof PLL_Language ? $this->pref_lang : null, // Can be `false` as well...
|
||||
$this->curlang instanceof PLL_Language ? $this->curlang : null // Can be `false` as well...
|
||||
);
|
||||
|
||||
$this->model->post->set_language(
|
||||
$post_id,
|
||||
$post_language->get_language( (int) $post_id )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a post ( or page ) is saved, published or updated.
|
||||
*
|
||||
* @since 0.1
|
||||
* @since 2.3 Does not save the language and translations anymore, unless the post has no language yet.
|
||||
*
|
||||
* @param int $post_id Post id of the post being saved.
|
||||
* @param WP_Post $post The post being saved.
|
||||
* @return void
|
||||
*/
|
||||
public function save_post( $post_id, $post ) {
|
||||
if ( is_multisite() && ms_is_switched() && ! $this->model->has_languages() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->model->is_translated_post_type( $post->post_type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $id = wp_is_post_revision( $post_id ) ) {
|
||||
$post_id = $id;
|
||||
}
|
||||
|
||||
$lang = $this->model->post->get_language( $post_id );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
$this->set_default_language( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after the post language and translations are saved.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param int $post_id Post id.
|
||||
* @param WP_Post $post Post object.
|
||||
* @param int[] $translations The list of translations post ids.
|
||||
*/
|
||||
do_action( 'pll_save_post', $post_id, $post, $this->model->post->get_translations( $post_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that saved terms are in the right language.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @param int $object_id Object ID.
|
||||
* @param int[]|string[] $terms An array of object term IDs or slugs.
|
||||
* @param int[] $tt_ids An array of term taxonomy IDs.
|
||||
* @param string $taxonomy Taxonomy slug.
|
||||
* @return void
|
||||
*/
|
||||
public function set_object_terms( $object_id, $terms, $tt_ids, $taxonomy ) {
|
||||
static $avoid_recursion;
|
||||
|
||||
if ( $avoid_recursion || empty( $terms ) || ! is_array( $terms ) || empty( $tt_ids )
|
||||
|| ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lang = $this->model->post->get_language( $object_id );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the term_taxonomy_ids to get all the requested terms in 1 query.
|
||||
$new_terms = get_terms(
|
||||
array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'term_taxonomy_id' => array_map( 'intval', $tt_ids ),
|
||||
'lang' => '',
|
||||
)
|
||||
);
|
||||
|
||||
if ( empty( $new_terms ) || ! is_array( $new_terms ) ) {
|
||||
// Terms not found.
|
||||
return;
|
||||
}
|
||||
|
||||
$new_term_ids_translated = $this->translate_terms( $new_terms, $taxonomy, $lang );
|
||||
|
||||
// Query the object's term.
|
||||
$orig_terms = get_terms(
|
||||
array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'object_ids' => $object_id,
|
||||
'lang' => '',
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_array( $orig_terms ) ) {
|
||||
$orig_term_ids = wp_list_pluck( $orig_terms, 'term_id' );
|
||||
$orig_term_ids_translated = $this->translate_terms( $orig_terms, $taxonomy, $lang );
|
||||
|
||||
// Terms that are not in the translated list.
|
||||
$remove_term_ids = array_diff( $orig_term_ids, $orig_term_ids_translated );
|
||||
|
||||
if ( ! empty( $remove_term_ids ) ) {
|
||||
wp_remove_object_terms( $object_id, $remove_term_ids, $taxonomy );
|
||||
}
|
||||
} else {
|
||||
$orig_term_ids = array();
|
||||
$orig_term_ids_translated = array();
|
||||
}
|
||||
|
||||
// Terms to add.
|
||||
$add_term_ids = array_unique( array_merge( $orig_term_ids_translated, $new_term_ids_translated ) );
|
||||
$add_term_ids = array_diff( $add_term_ids, $orig_term_ids );
|
||||
|
||||
if ( ! empty( $add_term_ids ) ) {
|
||||
$avoid_recursion = true;
|
||||
wp_set_object_terms( $object_id, $add_term_ids, $taxonomy, true ); // Append.
|
||||
$avoid_recursion = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the post parent is in the correct language.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param int $post_parent Post parent ID.
|
||||
* @param int $post_id Post ID.
|
||||
* @return int
|
||||
*/
|
||||
public function wp_insert_post_parent( $post_parent, $post_id ) {
|
||||
$lang = $this->model->post->get_language( $post_id );
|
||||
$parent_post_type = $post_parent > 0 ? get_post_type( $post_parent ) : null;
|
||||
// Dont break the hierarchy in case the post has no language
|
||||
if ( ! empty( $lang ) && ! empty( $parent_post_type ) && $this->model->is_translated_post_type( $parent_post_type ) ) {
|
||||
$post_parent = $this->model->post->get_translation( $post_parent, $lang );
|
||||
}
|
||||
|
||||
return $post_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a post, page or media is deleted
|
||||
* Don't delete translations if this is a post revision thanks to AndyDeGroo who caught this bug
|
||||
* http://wordpress.org/support/topic/plugin-polylang-quick-edit-still-breaks-translation-linking-of-pages-in-072
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @return void
|
||||
*/
|
||||
public function delete_post( $post_id ) {
|
||||
if ( ! wp_is_post_revision( $post_id ) ) {
|
||||
$this->model->post->delete_translation( $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents WP deleting files when there are still media using them.
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @param string $file Path to the file to delete.
|
||||
* @return string Empty or unmodified path.
|
||||
*/
|
||||
public function wp_delete_file( $file ) {
|
||||
global $wpdb;
|
||||
|
||||
$uploadpath = wp_upload_dir();
|
||||
|
||||
// Get the main attached file.
|
||||
$attached_file = substr_replace( $file, '', 0, strlen( trailingslashit( $uploadpath['basedir'] ) ) );
|
||||
$attached_file = preg_replace( '#-\d+x\d+\.([a-z]+)$#', '.$1', $attached_file );
|
||||
|
||||
$ids = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT post_id FROM $wpdb->postmeta
|
||||
WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
|
||||
$attached_file
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $ids ) ) {
|
||||
return ''; // Prevent deleting the file.
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a media translation
|
||||
*
|
||||
* @since 1.8
|
||||
* @since 3.7 Deprecated in favor of PLL_Translated_Post::create_media_translation().
|
||||
*
|
||||
* @param int $post_id Original attachment id.
|
||||
* @param string|PLL_Language $lang New translation language.
|
||||
* @return int Attachment id of the translated media.
|
||||
*/
|
||||
public function create_media_translation( $post_id, $lang ) {
|
||||
_deprecated_function( __METHOD__, '3.7', 'PLL_Translated_Post::create_media_translation()' );
|
||||
return $this->model->post->create_media_translation( $post_id, $lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that tags are in the correct language when a post is updated, due to `tags_input` parameter being removed in `wp_update_post()`.
|
||||
*
|
||||
* @since 3.4.5
|
||||
*
|
||||
* @param int $post_id Post ID, unused.
|
||||
* @param WP_Post $post_after Post object following the update.
|
||||
* @param WP_Post $post_before Post object before the update.
|
||||
* @return void
|
||||
*/
|
||||
public function force_tags_translation( $post_id, $post_after, $post_before ) {
|
||||
if ( ! is_object_in_taxonomy( $post_before->post_type, 'post_tag' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$terms = get_the_terms( $post_before, 'post_tag' );
|
||||
|
||||
if ( empty( $terms ) || ! is_array( $terms ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$term_ids = wp_list_pluck( $terms, 'term_id' );
|
||||
|
||||
// Let's ensure that `PLL_CRUD_Posts::set_object_terms()` will do its job.
|
||||
wp_set_post_terms( $post_id, $term_ids, 'post_tag' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that all terms in the given list are in the given language.
|
||||
* If not the case, the terms are translated or created (for a hierarchical taxonomy, terms are created recursively).
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param WP_Term[] $terms List of terms to translate.
|
||||
* @param string $taxonomy The terms' taxonomy.
|
||||
* @param PLL_Language $language The language to translate the terms into.
|
||||
* @return int[] List of `term_id`s.
|
||||
*
|
||||
* @phpstan-return array<positive-int>
|
||||
*/
|
||||
private function translate_terms( array $terms, string $taxonomy, PLL_Language $language ): array {
|
||||
$term_ids_translated = array();
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$term_ids_translated[] = $this->translate_term( $term, $taxonomy, $language );
|
||||
}
|
||||
|
||||
return array_filter( $term_ids_translated );
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the given term into the given language.
|
||||
* If the translation doesn't exist, it is created (for a hierarchical taxonomy, terms are created recursively).
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param WP_Term $term The term to translate.
|
||||
* @param string $taxonomy The term's taxonomy.
|
||||
* @param PLL_Language $language The language to translate the term into.
|
||||
* @return int A `term_id` on success, `0` on failure.
|
||||
*
|
||||
* @phpstan-return int<0, max>
|
||||
*/
|
||||
private function translate_term( WP_Term $term, string $taxonomy, PLL_Language $language ): int {
|
||||
// Check if the term is in the correct language or if a translation exists.
|
||||
$tr_term_id = $this->model->term->get( $term->term_id, $language );
|
||||
|
||||
if ( ! empty( $tr_term_id ) ) {
|
||||
// Already in the correct language.
|
||||
return $tr_term_id;
|
||||
}
|
||||
|
||||
// Or choose the correct language for tags (initially defined by name).
|
||||
$tr_term_id = $this->model->term_exists( $term->name, $taxonomy, $term->parent, $language );
|
||||
|
||||
if ( ! empty( $tr_term_id ) ) {
|
||||
return $tr_term_id;
|
||||
}
|
||||
|
||||
// Or create the term in the correct language.
|
||||
$tr_parent_term_id = 0;
|
||||
|
||||
if ( $term->parent > 0 && is_taxonomy_hierarchical( $taxonomy ) ) {
|
||||
$parent = get_term( $term->parent, $taxonomy );
|
||||
|
||||
if ( $parent instanceof WP_Term ) {
|
||||
// Translate the parent recursively.
|
||||
$tr_parent_term_id = $this->translate_term( $parent, $taxonomy, $language );
|
||||
}
|
||||
}
|
||||
|
||||
$lang_callback = function ( $lang, $tax, $slug ) use ( $language, $term, $taxonomy ) {
|
||||
if ( ! $lang instanceof PLL_Language && $tax === $taxonomy && $slug === $term->slug ) {
|
||||
return $language;
|
||||
}
|
||||
return $lang;
|
||||
};
|
||||
$parent_callback = function ( $parent_id, $tax, $slug ) use ( $tr_parent_term_id, $term, $taxonomy ) {
|
||||
if ( empty( $parent_id ) && $tax === $taxonomy && $slug === $term->slug ) {
|
||||
return $tr_parent_term_id;
|
||||
}
|
||||
return $parent_id;
|
||||
};
|
||||
add_filter( 'pll_inserted_term_language', $lang_callback, 10, 3 );
|
||||
add_filter( 'pll_inserted_term_parent', $parent_callback, 10, 3 );
|
||||
$new_term_info = wp_insert_term(
|
||||
$term->name,
|
||||
$taxonomy,
|
||||
array(
|
||||
'parent' => $tr_parent_term_id,
|
||||
'slug' => $term->slug, // Useless but prevents the use of `sanitize_title()` and for consistency with `$lang_callback`.
|
||||
)
|
||||
);
|
||||
remove_filter( 'pll_inserted_term_language', $lang_callback );
|
||||
remove_filter( 'pll_inserted_term_parent', $parent_callback );
|
||||
|
||||
if ( is_wp_error( $new_term_info ) ) {
|
||||
// Term creation failed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
$tr_term_id = max( 0, (int) $new_term_info['term_id'] );
|
||||
|
||||
if ( empty( $tr_term_id ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->model->term->set_language( $tr_term_id, $language );
|
||||
|
||||
$trs = $this->model->term->get_translations( $term->term_id );
|
||||
|
||||
$trs[ $language->slug ] = $tr_term_id;
|
||||
|
||||
$this->model->term->save_translations( $term->term_id, $trs );
|
||||
|
||||
return $tr_term_id;
|
||||
}
|
||||
}
|
||||
333
wp-content/plugins/polylang/src/crud-terms.php
Normal file
333
wp-content/plugins/polylang/src/crud-terms.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\REST\Request;
|
||||
use WP_Syntex\Polylang\Capabilities\Capabilities;
|
||||
use WP_Syntex\Polylang\Capabilities\Create\Term as Create_Term;
|
||||
|
||||
/**
|
||||
* Adds actions and filters related to languages when creating, reading, updating or deleting posts
|
||||
* Acts both on frontend and backend
|
||||
*
|
||||
* @since 2.4
|
||||
*/
|
||||
class PLL_CRUD_Terms {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Current language (used to filter the content).
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Language selected in the admin language filter.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $filter_lang;
|
||||
|
||||
/**
|
||||
* Preferred language to assign to new contents.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $pref_lang;
|
||||
|
||||
/**
|
||||
* Stores the 'lang' query var from WP_Query.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $tax_query_lang;
|
||||
|
||||
/**
|
||||
* Stores the term name before creating a slug if needed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $pre_term_name = '';
|
||||
|
||||
/**
|
||||
* Stores the term ID before creating a slug if needed.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $pre_term_id = 0;
|
||||
|
||||
/**
|
||||
* Reference to the Polylang options array.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Reference to the Polylang Request object.
|
||||
*
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param PLL_Base $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( PLL_Base &$polylang ) {
|
||||
$this->options = $polylang->options;
|
||||
$this->model = &$polylang->model;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
$this->filter_lang = &$polylang->filter_lang;
|
||||
$this->pref_lang = &$polylang->pref_lang;
|
||||
$this->request = &$polylang->request;
|
||||
|
||||
// Saving terms.
|
||||
add_action( 'create_term', array( $this, 'save_term' ), 999, 3 );
|
||||
add_action( 'edit_term', array( $this, 'save_term' ), 999, 3 ); // After PLL_Admin_Filters_Term
|
||||
add_filter( 'pre_term_name', array( $this, 'set_pre_term_name' ) );
|
||||
add_filter( 'pre_term_slug', array( $this, 'set_pre_term_slug' ), 10, 2 );
|
||||
add_filter( 'pre_term_term_id', array( $this, 'set_pre_term_id' ) );
|
||||
|
||||
// Filters terms query by language.
|
||||
add_filter( 'get_terms_args', array( $this, 'adjust_query_lang' ) );
|
||||
add_filter( 'terms_clauses', array( $this, 'terms_clauses' ), 10, 3 );
|
||||
add_action( 'pre_get_posts', array( $this, 'set_tax_query_lang' ), 999 );
|
||||
add_action( 'posts_selection', array( $this, 'unset_tax_query_lang' ), 0 );
|
||||
|
||||
// Deleting terms.
|
||||
add_action( 'pre_delete_term', array( $this, 'delete_term' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set a language by default for terms if it has no language yet.
|
||||
*
|
||||
* @since 1.5.4
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return void
|
||||
*/
|
||||
protected function set_default_language( $term_id, $taxonomy ) {
|
||||
$term_language = new Create_Term(
|
||||
$this->model,
|
||||
$this->request,
|
||||
$this->pref_lang instanceof PLL_Language ? $this->pref_lang : null, // Can be `false` as well...
|
||||
$this->curlang instanceof PLL_Language ? $this->curlang : null // Can be `false` as well...
|
||||
);
|
||||
|
||||
$this->model->term->set_language(
|
||||
$term_id,
|
||||
$term_language->get_language( (int) $term_id, (string) $taxonomy )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a category or post tag is created or edited.
|
||||
* Does nothing except on taxonomies which are filterable.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param int $term_id Term id of the term being saved.
|
||||
* @param int $tt_id Term taxonomy id.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return void
|
||||
*/
|
||||
public function save_term( $term_id, $tt_id, $taxonomy ) {
|
||||
if ( is_multisite() && ms_is_switched() && ! $this->model->has_languages() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lang = $this->model->term->get_language( $term_id );
|
||||
|
||||
if ( empty( $lang ) ) {
|
||||
$this->set_default_language( $term_id, $taxonomy );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after the term language and translations are saved.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param int $term_id Term id.
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @param int[] $translations The list of translations term ids.
|
||||
*/
|
||||
do_action( 'pll_save_term', $term_id, $taxonomy, $this->model->term->get_translations( $term_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the languages to filter `WP_Term_Query`.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param string[] $taxonomies Queried taxonomies.
|
||||
* @param array $args WP_Term_Query arguments.
|
||||
* @return PLL_Language[] The languages to filter `WP_Term_Query`.
|
||||
*/
|
||||
protected function get_queried_languages( $taxonomies, $args ): array {
|
||||
global $pagenow;
|
||||
|
||||
/*
|
||||
* Do nothing except on taxonomies which are filterable.
|
||||
* Since WP 4.7, make sure not to filter wp_get_object_terms().
|
||||
*/
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomies ) || ! empty( $args['object_ids'] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// If get_terms() is queried with a 'lang' parameter.
|
||||
if ( isset( $args['lang'] ) ) {
|
||||
$languages = is_string( $args['lang'] ) ? explode( ',', $args['lang'] ) : $args['lang'];
|
||||
return array_filter( array_map( array( $this->model, 'get_language' ), (array) $languages ) );
|
||||
}
|
||||
|
||||
// On the tags page, everything should be filtered according to the admin language filter except the parent dropdown.
|
||||
if ( 'edit-tags.php' === $pagenow && empty( $args['class'] ) ) {
|
||||
return ! empty( $this->filter_lang ) ? array( $this->filter_lang ) : array();
|
||||
}
|
||||
|
||||
return ! empty( $this->curlang ) ? array( $this->curlang ) : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts query language for `_get_term_hierarchy()` and `WP_Query`.
|
||||
*
|
||||
* @since 1.3
|
||||
* @since 3.8 Renamed from `get_terms_args()`.
|
||||
*
|
||||
* @param array $args WP_Term_Query arguments.
|
||||
* @return array Modified arguments.
|
||||
*/
|
||||
public function adjust_query_lang( $args ) {
|
||||
// Don't break _get_term_hierarchy().
|
||||
if ( 'all' === $args['get'] && 'id' === $args['orderby'] && 'id=>parent' === $args['fields'] ) {
|
||||
$args['lang'] = '';
|
||||
}
|
||||
|
||||
if ( isset( $this->tax_query_lang ) ) {
|
||||
$args['lang'] = empty( $this->tax_query_lang ) && ! empty( $this->curlang ) && ! empty( $args['slug'] ) ? $this->curlang->slug : $this->tax_query_lang;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters categories and post tags by language(s) when needed on admin side
|
||||
*
|
||||
* @since 0.2
|
||||
*
|
||||
* @param string[] $clauses List of sql clauses.
|
||||
* @param string[] $taxonomies List of taxonomies.
|
||||
* @param array $args WP_Term_Query arguments.
|
||||
* @return string[] Modified sql clauses.
|
||||
*/
|
||||
public function terms_clauses( $clauses, $taxonomies, $args ) {
|
||||
$languages = $this->get_queried_languages( $taxonomies, $args );
|
||||
return $this->model->terms_clauses( $clauses, $languages );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the WP_Term_Query language when doing a WP_Query.
|
||||
* Needed since WP 4.9.
|
||||
*
|
||||
* @since 2.3.2
|
||||
*
|
||||
* @param WP_Query $query WP_Query object.
|
||||
* @return void
|
||||
*/
|
||||
public function set_tax_query_lang( $query ) {
|
||||
$this->tax_query_lang = $query->query_vars['lang'] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the WP_Term_Query language filter for WP_Query.
|
||||
* Needed since WP 4.9.
|
||||
*
|
||||
* @since 2.3.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unset_tax_query_lang() {
|
||||
unset( $this->tax_query_lang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a category or post tag is deleted
|
||||
* Deletes language and translations
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param int $term_id Id of the term to delete.
|
||||
* @param string $taxonomy Name of the taxonomy.
|
||||
* @return void
|
||||
*/
|
||||
public function delete_term( $term_id, $taxonomy ) {
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete translation and relationships only if the term is translatable.
|
||||
$this->model->term->delete_translation( $term_id );
|
||||
$this->model->term->delete_language( $term_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the term name for use in pre_term_slug
|
||||
*
|
||||
* @since 0.9.5
|
||||
*
|
||||
* @param string $name term name
|
||||
* @return string unmodified term name
|
||||
*/
|
||||
public function set_pre_term_name( $name ) {
|
||||
$this->pre_term_name = is_string( $name ) ? $name : '';
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends language slug to the term slug if needed.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param string $slug Term slug.
|
||||
* @param string $taxonomy Term taxonomy.
|
||||
* @return string Slug with a language suffix if found.
|
||||
*/
|
||||
public function set_pre_term_slug( $slug, $taxonomy ) {
|
||||
if ( ! $this->model->is_translated_taxonomy( $taxonomy ) || ! is_string( $slug ) ) {
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$term_slug = new PLL_Term_Slug( $this->model, $slug, $taxonomy, $this->pre_term_name, $this->pre_term_id );
|
||||
|
||||
return $term_slug->get_suffixed_slug( '-' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the term ID to use in `pre_term_slug`.
|
||||
*
|
||||
* @since 3.7.7
|
||||
*
|
||||
* @param int $term_id Term ID.
|
||||
* @return int Unmodified term ID.
|
||||
*/
|
||||
public function set_pre_term_id( $term_id ) {
|
||||
$this->pre_term_id = is_numeric( $term_id ) ? (int) $term_id : 0;
|
||||
|
||||
return $term_id;
|
||||
}
|
||||
}
|
||||
231
wp-content/plugins/polylang/src/default-term.php
Normal file
231
wp-content/plugins/polylang/src/default-term.php
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages filters and actions related to default terms.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
class PLL_Default_Term {
|
||||
|
||||
/**
|
||||
* A reference to the PLL_Model instance.
|
||||
*
|
||||
* @var PLL_Model
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* Preferred language to assign to new contents.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
protected $curlang;
|
||||
|
||||
/**
|
||||
* Array of registered taxonomy names for which Polylang manages languages and translations.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $taxonomies;
|
||||
|
||||
/**
|
||||
* Constructor: setups properties.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->model = &$polylang->model;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
$this->taxonomies = $this->model->get_translated_taxonomies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups filters and actions needed.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_hooks() {
|
||||
foreach ( $this->taxonomies as $taxonomy ) {
|
||||
if ( 'category' === $taxonomy ) {
|
||||
// Allows to get the default terms in all languages.
|
||||
add_filter( 'option_default_' . $taxonomy, array( $this, 'option_default_term' ) );
|
||||
add_action( 'update_option_default_' . $taxonomy, array( $this, 'update_option_default_term' ), 10, 2 );
|
||||
}
|
||||
}
|
||||
add_action( 'pll_add_language', array( $this, 'handle_default_term_on_create_language' ) );
|
||||
|
||||
// The default term should be in the default language.
|
||||
add_action( 'pll_update_default_lang', array( $this, 'update_default_term_language' ) );
|
||||
|
||||
// Prevents deleting all the translations of the default term.
|
||||
add_filter( 'map_meta_cap', array( $this, 'fix_delete_default_term' ), 10, 4 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the default term to return the one in the right language.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param int $taxonomy_term_id The taxonomy term id.
|
||||
* @return int A taxonomy term id.
|
||||
*/
|
||||
public function option_default_term( $taxonomy_term_id ) {
|
||||
if ( isset( $this->curlang ) && $tr = $this->model->term->get( $taxonomy_term_id, $this->curlang ) ) {
|
||||
$taxonomy_term_id = $tr;
|
||||
}
|
||||
return $taxonomy_term_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the new default term is translated in all languages.
|
||||
* If not, create the translations.
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param int $old_value The old option value.
|
||||
* @param int $value The new option value.
|
||||
* @return void
|
||||
*/
|
||||
public function update_option_default_term( $old_value, $value ) {
|
||||
$default_cat_lang = $this->model->term->get_language( $value );
|
||||
|
||||
// Assign a default language to default term.
|
||||
if ( ! $default_cat_lang ) {
|
||||
$default_cat_lang = $this->model->get_default_language();
|
||||
$this->model->term->set_language( (int) $value, $default_cat_lang );
|
||||
}
|
||||
|
||||
if ( empty( $default_cat_lang ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$current_filter = current_filter();
|
||||
if ( ! $current_filter ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$taxonomy = substr( $current_filter, 22 );
|
||||
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
if ( $language->slug != $default_cat_lang->slug && ! $this->model->term->get_translation( $value, $language ) ) {
|
||||
$this->create_default_term( $language, $taxonomy );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default term for a language.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Language|string|int $lang Language.
|
||||
* @param string $taxonomy The current taxonomy.
|
||||
* @return void
|
||||
*/
|
||||
public function create_default_term( $lang, $taxonomy ) {
|
||||
$lang = $this->model->get_language( $lang );
|
||||
|
||||
// Create a new term.
|
||||
// FIXME this is translated in admin language when we would like it in $lang
|
||||
$cat_name = __( 'Uncategorized', 'polylang' );
|
||||
$cat_slug = sanitize_title( $cat_name . '-' . $lang->slug );
|
||||
$cat = wp_insert_term( $cat_name, $taxonomy, array( 'slug' => $cat_slug ) );
|
||||
|
||||
// Check that the term was not previously created (in case the language was deleted and recreated).
|
||||
$cat = $cat->error_data['term_exists'] ?? $cat['term_id'];
|
||||
|
||||
// Set language.
|
||||
$this->model->term->set_language( (int) $cat, $lang );
|
||||
|
||||
// This is a translation of the default term.
|
||||
$default = (int) get_option( 'default_' . $taxonomy );
|
||||
$translations = $this->model->term->get_translations( $default );
|
||||
|
||||
$this->model->term->save_translations( (int) $cat, $translations );
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the default term when new languages are created.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param array $args Argument used to create the language. @see `Model\Languages::add()`.
|
||||
* @return void
|
||||
*/
|
||||
public function handle_default_term_on_create_language( $args ) {
|
||||
foreach ( $this->taxonomies as $taxonomy ) {
|
||||
if ( 'category' === $taxonomy ) {
|
||||
$default = (int) get_option( 'default_' . $taxonomy );
|
||||
|
||||
// Assign default language to default term.
|
||||
if ( ! $this->model->term->get_language( $default ) ) {
|
||||
$this->model->term->set_language( $default, $args['slug'] );
|
||||
} elseif ( empty( $args['no_default_cat'] ) && ! $this->model->term->get( $default, $args['slug'] ) ) {
|
||||
$this->create_default_term( $args['slug'], $taxonomy );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents deleting all the translations of the default term.
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @param array $caps The user's actual capabilities.
|
||||
* @param string $cap Capability name.
|
||||
* @param int $user_id The user ID.
|
||||
* @param array $args Adds the context to the cap. The term id.
|
||||
* @return array
|
||||
*/
|
||||
public function fix_delete_default_term( $caps, $cap, $user_id, $args ) {
|
||||
if ( 'delete_term' === $cap && $this->is_default_term( reset( $args ) ) ) {
|
||||
$caps[] = 'do_not_allow';
|
||||
}
|
||||
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the term is the default term.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param int $term_id The term ID.
|
||||
* @return bool True if the term is the default term, false otherwise.
|
||||
*/
|
||||
public function is_default_term( $term_id ) {
|
||||
$term = get_term( $term_id );
|
||||
if ( $term instanceof WP_Term ) {
|
||||
$default_term_id = get_option( 'default_' . $term->taxonomy );
|
||||
return $default_term_id && in_array( $default_term_id, $this->model->term->get_translations( $term_id ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the default term language.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param string $slug Language slug.
|
||||
* @return void
|
||||
*/
|
||||
public function update_default_term_language( $slug ) {
|
||||
foreach ( $this->taxonomies as $taxonomy ) {
|
||||
if ( 'category' === $taxonomy ) {
|
||||
$default_cats = $this->model->term->get_translations( get_option( 'default_' . $taxonomy ) );
|
||||
if ( isset( $default_cats[ $slug ] ) ) {
|
||||
update_option( 'default_' . $taxonomy, $default_cats[ $slug ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
185
wp-content/plugins/polylang/src/filter-rest-routes.php
Normal file
185
wp-content/plugins/polylang/src/filter-rest-routes.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class to manage REST routes filterable by language.
|
||||
*
|
||||
* @since 3.5
|
||||
*/
|
||||
class PLL_Filter_REST_Routes {
|
||||
/**
|
||||
* Cached REST routes filterable by language ordered by types.
|
||||
*
|
||||
* @var string[]
|
||||
* @phpstan-var array<string, string>
|
||||
*/
|
||||
private $filtered_routes = array();
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
private $model;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param PLL_Model $model Shared instance of the current PLL_Model.
|
||||
*/
|
||||
public function __construct( PLL_Model $model ) {
|
||||
$this->model = $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds query parameters to preload paths.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param (string|string[])[] $preload_paths Array of paths to preload.
|
||||
* @param array $args Array of query strings to add paired by key/value.
|
||||
* @return (string|string[])[]
|
||||
*/
|
||||
public function add_query_parameters( array $preload_paths, array $args ): array {
|
||||
foreach ( $preload_paths as $k => $path ) {
|
||||
if ( empty( $path ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$query_params = array();
|
||||
// If the method request is OPTIONS, $path is an array and the first element is the path
|
||||
if ( is_array( $path ) ) {
|
||||
$temp_path = $path[0];
|
||||
} else {
|
||||
$temp_path = $path;
|
||||
}
|
||||
|
||||
$path_parts = wp_parse_url( $temp_path );
|
||||
|
||||
if ( ! isset( $path_parts['path'] ) || ! $this->is_filtered( $path_parts['path'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! empty( $path_parts['query'] ) ) {
|
||||
parse_str( $path_parts['query'], $query_params );
|
||||
}
|
||||
|
||||
// Add params in query params
|
||||
foreach ( $args as $key => $value ) {
|
||||
$query_params[ $key ] = $value;
|
||||
}
|
||||
|
||||
// Sort query params to put it in the same order as the preloading middleware does
|
||||
ksort( $query_params );
|
||||
|
||||
// Replace the key by the correct path with query params reordered
|
||||
$sorted_path = add_query_arg( urlencode_deep( $query_params ), $path_parts['path'] );
|
||||
|
||||
if ( is_array( $path ) ) {
|
||||
$preload_paths[ $k ][0] = $sorted_path;
|
||||
} else {
|
||||
$preload_paths[ $k ] = $sorted_path;
|
||||
}
|
||||
}
|
||||
|
||||
return $preload_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds inline script to declare filtered REST route on client side.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param string $script_handle Name of the script to add the inline script to.
|
||||
* @return void
|
||||
*/
|
||||
public function add_inline_script( string $script_handle ) {
|
||||
$script_var = 'window.pllFilteredRoutes = ' . (string) wp_json_encode( $this->get() );
|
||||
|
||||
wp_add_inline_script( $script_handle, $script_var, 'before' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns filtered REST routes by entity type (e.g. post type or taxonomy).
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @return string[] REST routes.
|
||||
* @phpstan-return array<string, string>
|
||||
*/
|
||||
private function get(): array {
|
||||
if ( ! empty( $this->filtered_routes ) ) {
|
||||
return $this->filtered_routes;
|
||||
}
|
||||
|
||||
$translatable_post_types = $this->model->get_translated_post_types();
|
||||
$translatable_taxonomies = $this->model->get_translated_taxonomies();
|
||||
|
||||
$post_types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
|
||||
$taxonomies = get_taxonomies( array( 'show_in_rest' => true ), 'objects' );
|
||||
|
||||
$filtered_entities = $this->extract_filtered_rest_entities(
|
||||
array_merge( $post_types, $taxonomies ),
|
||||
array_merge( $translatable_post_types, $translatable_taxonomies )
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters REST routes filterable by languages.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string[] $routes Array of filterable REST routes, with types as key.
|
||||
*/
|
||||
$this->filtered_routes = apply_filters(
|
||||
'pll_filtered_rest_routes',
|
||||
array_merge(
|
||||
$filtered_entities,
|
||||
array( 'search' => 'wp/v2/search' )
|
||||
)
|
||||
);
|
||||
|
||||
return $this->filtered_routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if a given route is fileterable by language.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param string $rest_route Route to test.
|
||||
* @return bool Whether the route is filterable or not.
|
||||
*/
|
||||
private function is_filtered( string $rest_route ): bool {
|
||||
$rest_route = trim( $rest_route );
|
||||
|
||||
return ! preg_match( '/\d+$/', $rest_route ) && in_array( trim( $rest_route, '/' ), $this->get(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts filterable REST route from an array of entity objects
|
||||
* from a list of translatable entities (e.g. post types or taxonomies).
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param object[] $rest_entities Array of post type or taxonomy objects.
|
||||
* @param string[] $translatable_entities Array of translatable entity names.
|
||||
* @return string[] Filtered routes.
|
||||
* @phpstan-param array<WP_Post_Type|WP_Taxonomy> $rest_entities
|
||||
* @phpstan-return array<string, string>
|
||||
*/
|
||||
private function extract_filtered_rest_entities( array $rest_entities, array $translatable_entities ) {
|
||||
$filtered_entities = array();
|
||||
foreach ( $rest_entities as $rest_entity ) {
|
||||
if ( in_array( $rest_entity->name, $translatable_entities, true ) ) {
|
||||
$rest_base = empty( $rest_entity->rest_base ) ? $rest_entity->name : $rest_entity->rest_base;
|
||||
$rest_namespace = empty( $rest_entity->rest_namespace ) ? 'wp/v2' : $rest_entity->rest_namespace;
|
||||
|
||||
$filtered_entities[ $rest_entity->name ] = "{$rest_namespace}/{$rest_base}";
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered_entities;
|
||||
}
|
||||
}
|
||||
203
wp-content/plugins/polylang/src/filters-links.php
Normal file
203
wp-content/plugins/polylang/src/filters-links.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages links filters needed on both frontend and admin
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
class PLL_Filters_Links {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
public $links_model;
|
||||
|
||||
/**
|
||||
* @var PLL_Links|null
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param PLL_Base $polylang The Polylang object.
|
||||
* @param-out PLL_Base $polylang
|
||||
*/
|
||||
public function __construct( PLL_Base &$polylang ) {
|
||||
$this->links = &$polylang->links;
|
||||
$this->links_model = &$polylang->links_model;
|
||||
$this->model = &$polylang->model;
|
||||
$this->options = $polylang->options;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
|
||||
// Low priority on links filters to come after any other modifications.
|
||||
if ( $this->options['force_lang'] ) {
|
||||
add_filter( 'post_link', array( $this, 'post_type_link' ), 20, 2 );
|
||||
add_filter( '_get_page_link', array( $this, '_get_page_link' ), 20, 2 );
|
||||
}
|
||||
|
||||
add_filter( 'post_type_link', array( $this, 'post_type_link' ), 20, 2 );
|
||||
add_filter( 'term_link', array( $this, 'term_link' ), 20, 3 );
|
||||
|
||||
if ( $this->options['force_lang'] > 0 ) {
|
||||
add_filter( 'attachment_link', array( $this, 'attachment_link' ), 20, 2 );
|
||||
}
|
||||
|
||||
// Keeps the preview post link on default domain when using multiple domains and SSO is not available.
|
||||
if ( 3 === $this->options['force_lang'] && ! class_exists( 'PLL_Xdata_Domain' ) ) {
|
||||
add_filter( 'preview_post_link', array( $this, 'preview_post_link' ), 20 );
|
||||
}
|
||||
|
||||
// Rewrites post types archives links to filter them by language.
|
||||
add_filter( 'post_type_archive_link', array( $this, 'post_type_archive_link' ), 20, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies page links
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string $link post link
|
||||
* @param int $post_id post ID
|
||||
* @return string modified post link
|
||||
*/
|
||||
public function _get_page_link( $link, $post_id ) {
|
||||
// /!\ WP does not use pretty permalinks for preview
|
||||
return false !== strpos( $link, 'preview=true' ) && false !== strpos( $link, 'page_id=' ) ? $link : $this->links_model->switch_language_in_link( $link, $this->model->post->get_language( $post_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies attachment links
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param string $link attachment link
|
||||
* @param int $post_id attachment link
|
||||
* @return string modified attachment link
|
||||
*/
|
||||
public function attachment_link( $link, $post_id ) {
|
||||
return wp_get_post_parent_id( $post_id ) ? $link : $this->links_model->switch_language_in_link( $link, $this->model->post->get_language( $post_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies custom posts links.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $link Post link.
|
||||
* @param WP_Post $post Post object.
|
||||
* @return string Modified post link.
|
||||
*/
|
||||
public function post_type_link( $link, $post ) {
|
||||
// /!\ WP does not use pretty permalinks for preview
|
||||
if ( ( false === strpos( $link, 'preview=true' ) || false === strpos( $link, 'p=' ) ) && $this->model->is_translated_post_type( $post->post_type ) ) {
|
||||
$lang = $this->model->post->get_language( $post->ID );
|
||||
$link = $this->options['force_lang'] ? $this->links_model->switch_language_in_link( $link, $lang ) : $link;
|
||||
|
||||
/**
|
||||
* Filters a post or custom post type link.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $link The post link.
|
||||
* @param PLL_Language $lang The current language.
|
||||
* @param WP_Post $post The post object.
|
||||
*/
|
||||
$link = apply_filters( 'pll_post_type_link', $link, $lang, $post );
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies term links.
|
||||
*
|
||||
* @since 0.7
|
||||
*
|
||||
* @param string $link Term link.
|
||||
* @param WP_Term $term Term object.
|
||||
* @param string $tax Taxonomy name;
|
||||
* @return string Modified term link.
|
||||
*/
|
||||
public function term_link( $link, $term, $tax ) {
|
||||
if ( $this->model->is_translated_taxonomy( $tax ) ) {
|
||||
$lang = $this->model->term->get_language( $term->term_id );
|
||||
$link = $this->options['force_lang'] ? $this->links_model->switch_language_in_link( $link, $lang ) : $link;
|
||||
|
||||
/**
|
||||
* Filter a term link
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $link The term link.
|
||||
* @param PLL_Language $lang The current language.
|
||||
* @param WP_Term $term The term object.
|
||||
*/
|
||||
return apply_filters( 'pll_term_link', $link, $lang, $term );
|
||||
}
|
||||
|
||||
// In case someone calls get_term_link for the 'language' taxonomy.
|
||||
if ( 'language' === $tax ) {
|
||||
$lang = $this->model->get_language( $term->term_id );
|
||||
if ( $lang ) {
|
||||
return $this->links_model->home_url( $lang );
|
||||
}
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the preview post link on default domain when using multiple domains.
|
||||
*
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @param string $url URL used for the post preview.
|
||||
* @return string The modified url.
|
||||
*/
|
||||
public function preview_post_link( $url ) {
|
||||
return $this->links_model->remove_language_from_link( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the post type archive links to add the language parameter
|
||||
* only if the post type is translated.
|
||||
*
|
||||
* The filter was originally only on frontend but is needed on admin too for
|
||||
* compatibility with the archive link of the ACF link field since ACF 5.4.0.
|
||||
*
|
||||
* @since 1.7.6
|
||||
*
|
||||
* @param string $link The post type archive permalink.
|
||||
* @param string $post_type Post type name.
|
||||
* @return string
|
||||
*/
|
||||
public function post_type_archive_link( $link, $post_type ) {
|
||||
return $this->model->is_translated_post_type( $post_type ) && 'post' !== $post_type ? $this->links_model->switch_language_in_link( $link, $this->curlang ) : $link;
|
||||
}
|
||||
}
|
||||
106
wp-content/plugins/polylang/src/filters-sanitization.php
Normal file
106
wp-content/plugins/polylang/src/filters-sanitization.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Setup specific filters useful for sanitization.
|
||||
*
|
||||
* Extract from PLL_Admin_Filters to be able to use in a REST API context.
|
||||
*
|
||||
* @since 2.9
|
||||
*/
|
||||
class PLL_Filters_Sanitization {
|
||||
/**
|
||||
* Language used for the sanitization depending on the context.
|
||||
*
|
||||
* @var string $locale
|
||||
*/
|
||||
public $locale;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 2.9
|
||||
*
|
||||
* @param string $locale Locale code of the language.
|
||||
*/
|
||||
public function __construct( $locale ) {
|
||||
$this->locale = $locale;
|
||||
|
||||
// We need specific filters for some languages like German and Danish
|
||||
$specific_locales = array( 'da_DK', 'de_DE', 'de_DE_formal', 'de_CH', 'de_CH_informal', 'ca', 'sr_RS', 'bs_BA' );
|
||||
if ( in_array( $locale, $specific_locales ) ) {
|
||||
add_filter( 'sanitize_title', array( $this, 'sanitize_title' ), 10, 3 );
|
||||
add_filter( 'sanitize_user', array( $this, 'sanitize_user' ), 10, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the locale code of the language.
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_locale() {
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe fix the result of sanitize_title() in case the languages include German or Danish
|
||||
* Without this filter, if language of the title being sanitized is different from the language
|
||||
* used for the admin interface and if one this language is German or Danish, some specific
|
||||
* characters such as ä, ö, ü, ß are incorrectly sanitized.
|
||||
*
|
||||
* All the process is done by the remove_accents() WordPress function based on the locale value
|
||||
*
|
||||
* @link https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/formatting.php#L1920-L1944
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @param string $title Sanitized title.
|
||||
* @param string $raw_title The title prior to sanitization.
|
||||
* @param string $context The context for which the title is being sanitized.
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_title( $title, $raw_title, $context ) {
|
||||
static $once = false;
|
||||
|
||||
if ( ! $once && 'save' == $context && ! empty( $title ) ) {
|
||||
$once = true;
|
||||
add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
|
||||
$title = sanitize_title( $raw_title, '', $context );
|
||||
remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
|
||||
$once = false;
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe fix the result of sanitize_user() in case the languages include German or Danish
|
||||
*
|
||||
* All the process is done by the remove_accents() WordPress function based on the locale value
|
||||
*
|
||||
* @link https://github.com/WordPress/WordPress/blob/5.5-branch/wp-includes/formatting.php#L1920-L1944
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @param string $username Sanitized username.
|
||||
* @param string $raw_username The username prior to sanitization.
|
||||
* @param bool $strict Whether to limit the sanitization to specific characters. Default false.
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_user( $username, $raw_username, $strict ) {
|
||||
static $once = false;
|
||||
|
||||
if ( ! $once ) {
|
||||
$once = true;
|
||||
add_filter( 'locale', array( $this, 'get_locale' ), 20 ); // After the filter for the admin interface
|
||||
$username = sanitize_user( $raw_username, $strict );
|
||||
remove_filter( 'locale', array( $this, 'get_locale' ), 20 );
|
||||
$once = false;
|
||||
}
|
||||
return $username;
|
||||
}
|
||||
}
|
||||
94
wp-content/plugins/polylang/src/filters-widgets-options.php
Normal file
94
wp-content/plugins/polylang/src/filters-widgets-options.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class PLL_Widgets_Filters
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* Add new options to {@see https://developer.wordpress.org/reference/classes/wp_widget/ WP_Widget} and saves them.
|
||||
*/
|
||||
class PLL_Filters_Widgets_Options {
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* PLL_Widgets_Filters constructor.
|
||||
*
|
||||
* @since 3.0 Moved actions from PLL_Admin_Filters.
|
||||
*
|
||||
* @param PLL_Base $polylang The Polylang object.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $polylang ) {
|
||||
$this->model = $polylang->model;
|
||||
|
||||
add_action( 'in_widget_form', array( $this, 'in_widget_form' ), 10, 3 );
|
||||
add_filter( 'widget_update_callback', array( $this, 'widget_update_callback' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the language filter field to the widgets options form.
|
||||
*
|
||||
* @since 3.0 Moved PLL_Admin_Filters.
|
||||
* @since 3.1 Rename lang_choice field name and id to pll_lang as the widget setting.
|
||||
*
|
||||
* @param WP_Widget $widget The widget instance (passed by reference).
|
||||
* @param null $return Return null if new fields are added.
|
||||
* @param array $instance An array of the widget's settings.
|
||||
* @return void
|
||||
*
|
||||
* @phpstan-param WP_Widget<array<string, mixed>> $widget
|
||||
*/
|
||||
public function in_widget_form( $widget, $return, $instance ) {
|
||||
$dropdown = new PLL_Walker_Dropdown();
|
||||
|
||||
$dropdown_html = $dropdown->walk(
|
||||
array_merge(
|
||||
array( (object) array( 'slug' => 0, 'name' => __( 'All languages', 'polylang' ) ) ),
|
||||
$this->model->get_languages_list()
|
||||
),
|
||||
-1,
|
||||
array(
|
||||
'id' => $widget->get_field_id( 'pll_lang' ),
|
||||
'name' => $widget->get_field_name( 'pll_lang' ),
|
||||
'class' => 'tags-input pll-lang-choice',
|
||||
'selected' => empty( $instance['pll_lang'] ) ? '' : $instance['pll_lang'],
|
||||
)
|
||||
);
|
||||
|
||||
printf(
|
||||
'<p><label for="%1$s">%2$s %3$s</label></p>',
|
||||
esc_attr( $widget->get_field_id( 'pll_lang' ) ),
|
||||
esc_html__( 'The widget is displayed for:', 'polylang' ),
|
||||
$dropdown_html // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when widget options are saved.
|
||||
* Saves the language associated to the widget.
|
||||
*
|
||||
* @since 0.3
|
||||
* @since 3.0 Moved from PLL_Admin_Filters.
|
||||
* @since 3.1 Remove unused $old_instance and $widget parameters.
|
||||
*
|
||||
* @param array $instance The current Widget's options.
|
||||
* @param array $new_instance The new Widget's options.
|
||||
* @return array Widget options.
|
||||
*/
|
||||
public function widget_update_callback( $instance, $new_instance ) {
|
||||
if ( ! empty( $new_instance['pll_lang'] ) && $lang = $this->model->get_language( $new_instance['pll_lang'] ) ) {
|
||||
$instance['pll_lang'] = $lang->slug;
|
||||
} else {
|
||||
unset( $instance['pll_lang'] );
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
400
wp-content/plugins/polylang/src/filters.php
Normal file
400
wp-content/plugins/polylang/src/filters.php
Normal file
@@ -0,0 +1,400 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Setup filters common to admin and frontend
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
class PLL_Filters {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
public $links_model;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters
|
||||
*
|
||||
* @since 1.4
|
||||
*
|
||||
* @param PLL_Base $polylang The Polylang object.
|
||||
* @param-out PLL_Base $polylang
|
||||
*/
|
||||
public function __construct( PLL_Base &$polylang ) {
|
||||
$this->links_model = &$polylang->links_model;
|
||||
$this->model = &$polylang->model;
|
||||
$this->options = $polylang->options;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
|
||||
// Deletes our cache for sticky posts when the list is updated.
|
||||
add_action( 'update_option_sticky_posts', array( $this, 'delete_sticky_posts_cache' ) );
|
||||
add_action( 'add_option_sticky_posts', array( $this, 'delete_sticky_posts_cache' ) );
|
||||
add_action( 'delete_option_sticky_posts', array( $this, 'delete_sticky_posts_cache' ) );
|
||||
|
||||
// Filters the comments according to the current language.
|
||||
add_action( 'parse_comment_query', array( $this, 'parse_comment_query' ) );
|
||||
add_filter( 'comments_clauses', array( $this, 'comments_clauses' ), 10, 2 );
|
||||
|
||||
// Filters the get_pages function according to the current language.
|
||||
add_filter( 'get_pages_query_args', array( $this, 'get_pages_query_args' ), 10, 2 );
|
||||
|
||||
// Rewrites next and previous post links to filter them by language.
|
||||
add_filter( 'get_previous_post_join', array( $this, 'posts_join' ), 10, 5 );
|
||||
add_filter( 'get_next_post_join', array( $this, 'posts_join' ), 10, 5 );
|
||||
add_filter( 'get_previous_post_where', array( $this, 'posts_where' ), 10, 5 );
|
||||
add_filter( 'get_next_post_where', array( $this, 'posts_where' ), 10, 5 );
|
||||
|
||||
// Converts the locale to a valid W3C locale.
|
||||
add_filter( 'language_attributes', array( $this, 'language_attributes' ) );
|
||||
|
||||
// Translate the site title in emails sent to users
|
||||
add_filter( 'password_change_email', array( $this, 'translate_user_email' ) );
|
||||
add_filter( 'email_change_email', array( $this, 'translate_user_email' ) );
|
||||
|
||||
// Translates the privacy policy page.
|
||||
add_filter( 'option_wp_page_for_privacy_policy', array( $this, 'translate_page_for_privacy_policy' ), 20 ); // Since WP 4.9.6.
|
||||
add_filter( 'map_meta_cap', array( $this, 'fix_privacy_policy_page_editing' ), 10, 4 );
|
||||
|
||||
// Personal data exporter.
|
||||
add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'register_personal_data_exporter' ), 0 ); // Since WP 4.9.6.
|
||||
|
||||
// Fix for `term_exists()`.
|
||||
add_filter( 'term_exists_default_query_args', array( $this, 'term_exists_default_query_args' ), 0, 3 ); // Since WP 6.0.0.
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the cache for multilingual sticky posts.
|
||||
*
|
||||
* @since 2.8.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_sticky_posts_cache() {
|
||||
wp_cache_delete( 'sticky_posts', 'options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the language to filter a comments query.
|
||||
*
|
||||
* @since 2.0
|
||||
* @since 3.1 Always returns an array. Renamed from get_comments_queried_language().
|
||||
*
|
||||
* @param WP_Comment_Query $query WP_Comment_Query object.
|
||||
* @return PLL_Language[] The languages to use in the filter.
|
||||
*/
|
||||
protected function get_comments_queried_languages( $query ) {
|
||||
// Don't filter comments if comment ids or post ids are specified.
|
||||
$plucked = wp_array_slice_assoc( $query->query_vars, array( 'comment__in', 'parent', 'post_id', 'post__in', 'post_parent' ) );
|
||||
$fields = array_filter( $plucked );
|
||||
if ( ! empty( $fields ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Don't filter comments if a non translated post type is specified.
|
||||
if ( ! empty( $query->query_vars['post_type'] ) && ! $this->model->is_translated_post_type( $query->query_vars['post_type'] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// If comments are queried with a 'lang' parameter, keeps only language codes.
|
||||
if ( isset( $query->query_vars['lang'] ) ) {
|
||||
$languages = is_string( $query->query_vars['lang'] ) ? explode( ',', $query->query_vars['lang'] ) : $query->query_vars['lang'];
|
||||
if ( is_array( $languages ) ) {
|
||||
$languages = array_map( array( $this->model, 'get_language' ), $languages );
|
||||
return array_filter( $languages );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
return array( $this->curlang );
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a language dependent cache domain when querying comments.
|
||||
* Useful as the 'lang' parameter is not included in cache key by WordPress.
|
||||
* Needed since WP 4.6 as comments have been added to persistent cache. See #36906, #37419.
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
* @param WP_Comment_Query $query WP_Comment_Query object.
|
||||
* @return void
|
||||
*/
|
||||
public function parse_comment_query( $query ) {
|
||||
$lang = $this->get_comments_queried_languages( $query );
|
||||
if ( ! empty( $lang ) ) {
|
||||
$lang = wp_list_pluck( $lang, 'slug' );
|
||||
$key = '_' . implode( ',', $lang );
|
||||
$query->query_vars['cache_domain'] = empty( $query->query_vars['cache_domain'] ) ? 'pll' . $key : $query->query_vars['cache_domain'] . $key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the comments according to the current language.
|
||||
* Used by the recent comments widget and admin language filter.
|
||||
*
|
||||
* @since 0.2
|
||||
*
|
||||
* @param string[] $clauses SQL clauses.
|
||||
* @param WP_Comment_Query $query WP_Comment_Query object.
|
||||
* @return string[] Modified $clauses.
|
||||
*/
|
||||
public function comments_clauses( $clauses, $query ) {
|
||||
global $wpdb;
|
||||
|
||||
$lang = $this->get_comments_queried_languages( $query );
|
||||
|
||||
if ( ! empty( $lang ) ) {
|
||||
$lang = wp_list_pluck( $lang, 'slug' );
|
||||
|
||||
// If this clause is not already added by WP.
|
||||
if ( ! preg_match( "#JOIN\s+{$wpdb->posts}\s+ON\s+(({$wpdb->posts}\.)?ID|({$wpdb->comments}\.)?comment_post_ID)\s*=#", $clauses['join'] ) ) {
|
||||
$clauses['join'] .= " JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
|
||||
}
|
||||
|
||||
$clauses['join'] .= $this->model->post->join_clause();
|
||||
$clauses['where'] .= $this->model->post->where_clause( $lang );
|
||||
}
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the WP_Query in get_pages() per language.
|
||||
*
|
||||
* @since 3.4.3
|
||||
*
|
||||
* @param array $query_args Array of arguments passed to WP_Query.
|
||||
* @param array $parsed_args Array of get_pages() arguments.
|
||||
* @return array Array of arguments passed to WP_Query with the language.
|
||||
*/
|
||||
public function get_pages_query_args( $query_args, $parsed_args ) {
|
||||
if ( isset( $parsed_args['lang'] ) ) {
|
||||
$query_args['lang'] = $parsed_args['lang'];
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the sql request for get_adjacent_post to filter by the current language.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string $sql The JOIN clause in the SQL.
|
||||
* @param bool $in_same_term Whether post should be in a same taxonomy term.
|
||||
* @param int[] $excluded_terms Array of excluded term IDs.
|
||||
* @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
|
||||
* @param WP_Post $post WP_Post object.
|
||||
* @return string Modified JOIN clause.
|
||||
*/
|
||||
public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy, $post ) {
|
||||
return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string $sql The WHERE clause in the SQL.
|
||||
* @param bool $in_same_term Whether post should be in a same taxonomy term.
|
||||
* @param int[] $excluded_terms Array of excluded term IDs.
|
||||
* @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true.
|
||||
* @param WP_Post $post WP_Post object.
|
||||
* @return string Modified WHERE clause.
|
||||
*/
|
||||
public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy, $post ) {
|
||||
return $this->model->is_translated_post_type( $post->post_type ) && ! empty( $this->curlang ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts WordPress locale to valid W3 locale in html language attributes
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string $output language attributes
|
||||
* @return string
|
||||
*/
|
||||
public function language_attributes( $output ) {
|
||||
$language = $this->model->get_language( determine_locale() );
|
||||
|
||||
if ( ! $language ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
return str_replace( '"' . get_bloginfo( 'language' ) . '"', '"' . $language->get_locale( 'display' ) . '"', $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the site title in emails sent to the user (change email, reset password)
|
||||
* It is necessary to filter the email because WP evaluates the site title before calling switch_to_locale()
|
||||
*
|
||||
* @since 2.1.3
|
||||
*
|
||||
* @param string[] $email Email contents.
|
||||
* @return string[] Translated email contents.
|
||||
*/
|
||||
public function translate_user_email( $email ) {
|
||||
$blog_name = wp_specialchars_decode( pll__( get_option( 'blogname' ) ), ENT_QUOTES );
|
||||
$email['subject'] = sprintf( $email['subject'], $blog_name );
|
||||
$email['message'] = str_replace( '###SITENAME###', $blog_name, $email['message'] );
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the privacy policy page, on both frontend and admin
|
||||
*
|
||||
* @since 2.3.6
|
||||
*
|
||||
* @param int $id Privacy policy page id
|
||||
* @return int
|
||||
*/
|
||||
public function translate_page_for_privacy_policy( $id ) {
|
||||
return empty( $this->curlang ) ? $id : $this->model->post->get( $id, $this->curlang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents edit and delete links for the translations of the privacy policy page for non admin
|
||||
*
|
||||
* @since 2.3.7
|
||||
*
|
||||
* @param array $caps The user's actual capabilities.
|
||||
* @param string $cap Capability name.
|
||||
* @param int $user_id The user ID.
|
||||
* @param array $args Adds the context to the cap. The category id.
|
||||
* @return array
|
||||
*/
|
||||
public function fix_privacy_policy_page_editing( $caps, $cap, $user_id, $args ) {
|
||||
if ( ! in_array( $cap, array( 'edit_page', 'edit_post', 'delete_page', 'delete_post' ), true ) ) {
|
||||
return $caps;
|
||||
}
|
||||
|
||||
if ( empty( $args[0] ) ) {
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/*
|
||||
* We expect a post ID, but nothing prevents to receive a `WP_Post`,
|
||||
* so let make sure we manipulate a post ID in any case.
|
||||
*/
|
||||
$post = get_post( $args[0] );
|
||||
if ( ! $post ) {
|
||||
return $caps;
|
||||
}
|
||||
|
||||
$privacy_page = get_option( 'wp_page_for_privacy_policy' );
|
||||
if ( $privacy_page && in_array( $post->ID, $this->model->post->get_translations( $privacy_page ), true ) ) {
|
||||
$caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
|
||||
}
|
||||
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register our personal data exporter
|
||||
*
|
||||
* @since 2.3.6
|
||||
*
|
||||
* @param array $exporters Personal data exporters
|
||||
* @return array
|
||||
*/
|
||||
public function register_personal_data_exporter( $exporters ) {
|
||||
$exporters[] = array(
|
||||
'exporter_friendly_name' => __( 'Translated user descriptions', 'polylang' ),
|
||||
'callback' => array( $this, 'user_data_exporter' ),
|
||||
);
|
||||
return $exporters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export translated user description as WP exports only the description in the default language
|
||||
*
|
||||
* @since 2.3.6
|
||||
*
|
||||
* @param string $email_address User email address
|
||||
* @return array Personal data
|
||||
*/
|
||||
public function user_data_exporter( $email_address ) {
|
||||
$email_address = trim( $email_address );
|
||||
$data_to_export = array();
|
||||
$user_data_to_export = array();
|
||||
|
||||
if ( $user = get_user_by( 'email', $email_address ) ) {
|
||||
foreach ( $this->model->get_languages_list() as $lang ) {
|
||||
if ( ! $lang->is_default && $value = get_user_meta( $user->ID, 'description_' . $lang->slug, true ) ) {
|
||||
$user_data_to_export[] = array(
|
||||
/* translators: %s is a language native name */
|
||||
'name' => sprintf( __( 'User description - %s', 'polylang' ), $lang->name ),
|
||||
'value' => $value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $user_data_to_export ) ) {
|
||||
$data_to_export[] = array(
|
||||
'group_id' => 'user',
|
||||
'group_label' => __( 'User', 'polylang' ),
|
||||
'item_id' => "user-{$user->ID}",
|
||||
'data' => $user_data_to_export,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'data' => $data_to_export,
|
||||
'done' => true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters default query arguments for checking if a term exists.
|
||||
* In `term_exists()`, WP 6.0 uses `get_terms()`, which is filtered by language by Polylang.
|
||||
* This filter prevents `term_exists()` to be filtered by language.
|
||||
*
|
||||
* @since 3.2
|
||||
*
|
||||
* @param array $defaults An array of arguments passed to get_terms().
|
||||
* @param int|string $term The term to check. Accepts term ID, slug, or name.
|
||||
* @param string $taxonomy The taxonomy name to use. An empty string indicates the search is against all taxonomies.
|
||||
* @return array
|
||||
*/
|
||||
public function term_exists_default_query_args( $defaults, $term, $taxonomy ) {
|
||||
if ( ! empty( $taxonomy ) && ! $this->model->is_translated_taxonomy( $taxonomy ) ) {
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
if ( ! is_array( $defaults ) ) {
|
||||
$defaults = array();
|
||||
}
|
||||
|
||||
if ( ! isset( $defaults['lang'] ) ) {
|
||||
$defaults['lang'] = '';
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
}
|
||||
107
wp-content/plugins/polylang/src/format-util.php
Normal file
107
wp-content/plugins/polylang/src/format-util.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class to match values against a given format.
|
||||
*
|
||||
* @since 3.6
|
||||
* @since 3.7 Moved from Polylang Pro to Polylang.
|
||||
*/
|
||||
class PLL_Format_Util {
|
||||
/**
|
||||
* Cache for regex patterns.
|
||||
* Useful when using `filter_list()` for example.
|
||||
*
|
||||
* @var string[] Formats as array keys, patterns as array values.
|
||||
*
|
||||
* @phpstan-var array<non-empty-string, non-empty-string>
|
||||
*/
|
||||
private $patterns = array();
|
||||
|
||||
/**
|
||||
* Filters the given list to return only the values whose the key or value matches the given format.
|
||||
*
|
||||
* @since 3.6
|
||||
* @since 3.7 Only accepts arrays as first parameter.
|
||||
*
|
||||
* @param array $list A list with keys or values to match against `$format`.
|
||||
* @param string $format A format, where `*` means "any characters" (`.*`), unless escaped.
|
||||
* @param string $mode Optional. Tell if we should filter the keys or values from `$list`.
|
||||
* Possible values are `'use_keys'` and `'use_values'`. Default is `'use_keys'`.
|
||||
* @return array
|
||||
*
|
||||
* @template TArrayValue
|
||||
* @phpstan-param ($mode is 'use_keys' ? array<string, TArrayValue> : array<string>) $list
|
||||
* @phpstan-param 'use_keys'|'use_values' $mode
|
||||
* @phpstan-return ($mode is 'use_keys' ? array<string, TArrayValue> : array<string>)
|
||||
*/
|
||||
public function filter_list( array $list, string $format, string $mode = 'use_keys' ): array {
|
||||
$filter = function ( $key ) use ( $format ) {
|
||||
return $this->matches( (string) $key, $format );
|
||||
};
|
||||
|
||||
if ( 'use_values' === $mode ) {
|
||||
return array_filter( $list, $filter );
|
||||
}
|
||||
|
||||
return array_filter( $list, $filter, ARRAY_FILTER_USE_KEY );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the given string matches the given format.
|
||||
*
|
||||
* @since 3.6
|
||||
*
|
||||
* @param string $key A string to test.
|
||||
* @param string $format A format, where `*` means "any characters" (`.*`), unless escaped.
|
||||
* @return bool
|
||||
*/
|
||||
public function matches( string $key, string $format ): bool {
|
||||
if ( ! $this->is_format( $format ) ) {
|
||||
return $key === $format;
|
||||
}
|
||||
|
||||
if ( '*' === $format ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( empty( $this->patterns[ $format ] ) ) {
|
||||
$pattern = addcslashes( $format, '.+?[^]$(){}=!<>|:-#/' ); // Escape regular expression characters (list from `preg_quote()` but `*` and `\` are ignored).
|
||||
$pattern = preg_replace(
|
||||
array(
|
||||
'/\\\(?!\*)/', // Escape `\` characters except if followed by `*`.
|
||||
'/(?<!\\\)\*/', // Replace `*` characters by `.*` except if preceded by `\`.
|
||||
),
|
||||
array( '\\', '.*' ),
|
||||
$pattern
|
||||
);
|
||||
|
||||
if ( empty( $pattern ) ) {
|
||||
// Error.
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->patterns[ $format ] = $pattern;
|
||||
} else {
|
||||
$pattern = $this->patterns[ $format ];
|
||||
}
|
||||
|
||||
return (bool) preg_match( "/^{$pattern}$/", $key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the given string is a format (that includes a `*`).
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @param string $format Format to test.
|
||||
* @return bool
|
||||
*
|
||||
* @phpstan-assert-if-true non-empty-string $format
|
||||
*/
|
||||
public function is_format( string $format ): bool {
|
||||
return (bool) preg_match( '/(?<!\\\)\*/', $format ); // Match `*` characters unless if preceded by `\`.
|
||||
}
|
||||
}
|
||||
112
wp-content/plugins/polylang/src/frontend/accept-language.php
Normal file
112
wp-content/plugins/polylang/src/frontend/accept-language.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class Accept_Language.
|
||||
*
|
||||
* Represents an Accept-Language HTTP Header, as defined in RFC 2616 Section 14.4 {@see https://tools.ietf.org/html/rfc2616.html#section-14.4}.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class PLL_Accept_Language {
|
||||
public const SUBTAG_PATTERNS = array(
|
||||
'language' => '(\b[a-z]{2,3}|[a-z]{4}|[a-z]{5-8}\b)',
|
||||
'language-extension' => '(?:-(\b[a-z]{3}){1,3}\b)?',
|
||||
'script' => '(?:-(\b[a-z]{4})\b)?',
|
||||
'region' => '(?:-(\b[a-z]{2}|[0-9]{3})\b)?',
|
||||
'variant' => '(?:-(\b[0-9][a-z]{1,3}|[a-z][a-z0-9]{4,7})\b)?',
|
||||
'extension' => '(?:-(\b[a-wy-z]-[a-z0-9]{2,8})\b)?',
|
||||
'private-use' => '(?:-(\bx-[a-z0-9]{1,8})\b)?',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string[] {
|
||||
* @type string $language Usually 2 or three letters (ISO 639).
|
||||
* @type string $language-extension Up to three groups of 3 letters.
|
||||
* @type string $script Four letters.
|
||||
* @type string $region Either two letters of three digits.
|
||||
* @type string $variant Either one digit followed by 1 to 3 letters, or a letter followed by 2 to 7 alphanumerical characters.
|
||||
* @type string $extension One letter that cannot be an 'x', followed by 2 to 8 alphanumerical characters.
|
||||
* @type string $private-use Starts by 'x-', followed by 1 to 8 alphanumerical characters.
|
||||
* }
|
||||
*/
|
||||
protected $subtags;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
protected $quality;
|
||||
|
||||
/**
|
||||
* PLL_Accept_Language constructor.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string[] $subtags With subtag name as keys and subtag values as names.
|
||||
* @param mixed $quality Floating point value from 0.0 to 1.0. Higher values indicates a user's preference.
|
||||
*/
|
||||
public function __construct( $subtags, $quality = 1.0 ) {
|
||||
$this->subtags = $subtags;
|
||||
$this->quality = is_numeric( $quality ) ? floatval( $quality ) : 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from an array resulting of a PHP {@see preg_match()} or {@see preg_match_all()} call.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string[] $matches Expects first entry to be full match, following entries to be subtags and last entry to be quality factor.
|
||||
* @return PLL_Accept_Language
|
||||
*/
|
||||
public static function from_array( $matches ) {
|
||||
$subtags = array_combine(
|
||||
array_keys( array_slice( self::SUBTAG_PATTERNS, 0, count( $matches ) - 1 ) ),
|
||||
array_slice( $matches, 1, count( self::SUBTAG_PATTERNS ) )
|
||||
);
|
||||
$quality = count( $matches ) === 9 ? $matches[8] : 1.0;
|
||||
|
||||
return new PLL_Accept_Language( $subtags, $quality );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full language tag.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
$subtags = array_filter(
|
||||
$this->subtags,
|
||||
function ( $subtag ) {
|
||||
return ! empty( trim( $subtag ) );
|
||||
}
|
||||
);
|
||||
return implode( '-', $subtags );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quality factor as negotiated by the browser agent.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function get_quality() {
|
||||
return $this->quality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a subtag from the language tag.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $name A valid subtag name, {@see PLL_Accept_Language::SUBTAG_PATTERNS} for available subtag names.
|
||||
* @return string
|
||||
*/
|
||||
public function get_subtag( $name ) {
|
||||
return $this->subtags[ $name ] ?? '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class PLL_Accept_Languages_Collection.
|
||||
*
|
||||
* Represents a collection of values parsed from an Accept-Language HTTP header.
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
class PLL_Accept_Languages_Collection {
|
||||
/**
|
||||
* @var PLL_Accept_Language[]
|
||||
*/
|
||||
protected $accept_languages = array();
|
||||
|
||||
/**
|
||||
* Parse Accept-Language HTTP header according to IETF BCP 47.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param string $http_header Value of the Accept-Language HTTP Header. Formatted as stated BCP 47 for language tags {@see https://tools.ietf.org/html/bcp47}.
|
||||
* @return PLL_Accept_Languages_Collection
|
||||
*/
|
||||
public static function from_accept_language_header( $http_header ) {
|
||||
$lang_parse = array();
|
||||
// Break up string into pieces ( languages and q factors ).
|
||||
$language_pattern = implode( '', PLL_Accept_Language::SUBTAG_PATTERNS );
|
||||
$quality_pattern = '\s*;\s*q\s*=\s*((?>1|0)(?>\.[0-9]+)?)';
|
||||
$full_pattern = "/{$language_pattern}(?:{$quality_pattern})?/i";
|
||||
|
||||
preg_match_all(
|
||||
$full_pattern,
|
||||
$http_header,
|
||||
$lang_parse,
|
||||
PREG_SET_ORDER
|
||||
);
|
||||
|
||||
return new PLL_Accept_Languages_Collection(
|
||||
array_map(
|
||||
array( PLL_Accept_Language::class, 'from_array' ),
|
||||
$lang_parse
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* PLL_Accept_Languages_Collection constructor.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param PLL_Accept_Language[] $accept_languages Objects representing Accept-Language HTTP headers.
|
||||
*/
|
||||
public function __construct( $accept_languages = array() ) {
|
||||
$this->accept_languages = $accept_languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubble sort (need a stable sort for Android, so can't use a PHP sort function).
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bubble_sort() {
|
||||
$k = $this->accept_languages;
|
||||
$v = array_map(
|
||||
function ( $accept_lang ) {
|
||||
return $accept_lang->get_quality();
|
||||
},
|
||||
$this->accept_languages
|
||||
);
|
||||
|
||||
if ( $n = count( $k ) ) {
|
||||
|
||||
if ( $n > 1 ) {
|
||||
for ( $i = 2; $i <= $n; $i++ ) {
|
||||
for ( $j = 0; $j <= $n - 2; $j++ ) {
|
||||
if ( $v[ $j ] < $v[ $j + 1 ] ) {
|
||||
// Swap values.
|
||||
$temp = $v[ $j ];
|
||||
$v[ $j ] = $v[ $j + 1 ];
|
||||
$v[ $j + 1 ] = $temp;
|
||||
// Swap keys.
|
||||
$temp = $k[ $j ];
|
||||
$k[ $j ] = $k[ $j + 1 ];
|
||||
$k[ $j + 1 ] = $temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->accept_languages = array_filter(
|
||||
$k,
|
||||
function ( $accept_lang ) {
|
||||
return $accept_lang->get_quality() > 0;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks through sorted list and use first one that matches our language list.
|
||||
*
|
||||
* @since 3.0
|
||||
*
|
||||
* @param PLL_Language[] $languages The language list.
|
||||
* @return string|false A language slug if there's a match, false otherwise.
|
||||
*/
|
||||
public function find_best_match( $languages = array() ) {
|
||||
foreach ( $this->accept_languages as $accept_lang ) {
|
||||
// First loop to match the exact locale.
|
||||
foreach ( $languages as $language ) {
|
||||
if ( 0 === strcasecmp( $accept_lang, $language->get_locale( 'display' ) ) ) {
|
||||
return $language->slug;
|
||||
}
|
||||
}
|
||||
|
||||
// In order of priority.
|
||||
$subsets = array();
|
||||
if ( ! empty( $accept_lang->get_subtag( 'region' ) ) ) {
|
||||
$subsets[] = $accept_lang->get_subtag( 'language' ) . '-' . $accept_lang->get_subtag( 'region' );
|
||||
$subsets[] = $accept_lang->get_subtag( 'region' );
|
||||
}
|
||||
if ( ! empty( $accept_lang->get_subtag( 'variant' ) ) ) {
|
||||
$subsets[] = $accept_lang->get_subtag( 'language' ) . '-' . $accept_lang->get_subtag( 'variant' );
|
||||
}
|
||||
$subsets[] = $accept_lang->get_subtag( 'language' );
|
||||
|
||||
// More loops to match the subsets.
|
||||
foreach ( $languages as $language ) {
|
||||
foreach ( $subsets as $subset ) {
|
||||
|
||||
if ( 0 === stripos( $subset, $language->slug ) || 0 === stripos( $language->get_locale( 'display' ), $subset ) ) {
|
||||
return $language->slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
275
wp-content/plugins/polylang/src/frontend/canonical.php
Normal file
275
wp-content/plugins/polylang/src/frontend/canonical.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages canonical redirect on frontend.
|
||||
*
|
||||
* @since 3.3
|
||||
*/
|
||||
class PLL_Canonical {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
protected $links_model;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
protected $curlang;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @param PLL_Frontend $polylang Main Polylang object.
|
||||
*/
|
||||
public function __construct( PLL_Frontend &$polylang ) {
|
||||
$this->links_model = &$polylang->links_model;
|
||||
$this->model = &$polylang->model;
|
||||
$this->options = $polylang->options;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the language code is not in agreement with the language of the content,
|
||||
* redirects incoming links to the proper URL to avoid duplicate content.
|
||||
*
|
||||
* @since 0.9.6
|
||||
*
|
||||
* @global WP_Query $wp_query WordPress Query object.
|
||||
* @global bool $is_IIS
|
||||
*
|
||||
* @param string $requested_url Optional, defaults to requested url.
|
||||
* @param bool $do_redirect Optional, whether to perform the redirect or not.
|
||||
* @return string|void Returns if redirect is not performed.
|
||||
*/
|
||||
public function check_canonical_url( $requested_url = '', $do_redirect = true ) {
|
||||
global $wp_query;
|
||||
|
||||
// Don't redirect in same cases as WP.
|
||||
if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $GLOBALS['is_IIS'] && ! iis7_supports_permalinks() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't redirect mysite.com/?attachment_id= to mysite.com/en/?attachment_id=.
|
||||
if ( 1 == $this->options['force_lang'] && is_attachment() && isset( $_GET['attachment_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the default language code is not hidden and the static front page url contains the page name,
|
||||
* the customizer lands here and the code below would redirect to the list of posts.
|
||||
*/
|
||||
if ( is_customize_preview() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $requested_url ) ) {
|
||||
$requested_url = pll_get_requested_url();
|
||||
}
|
||||
|
||||
if ( ( is_single() && ( ! is_attachment() || get_option( 'wp_attachment_pages_enabled' ) ) ) || ( is_page() && ! is_front_page() ) ) {
|
||||
$post = get_post();
|
||||
if ( $post instanceof WP_Post && $this->model->is_translated_post_type( $post->post_type ) ) {
|
||||
$language = $this->model->post->get_language( (int) $post->ID );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $wp_query->tax_query ) ) {
|
||||
if ( $this->model->is_translated_taxonomy( $this->get_queried_taxonomy( $wp_query->tax_query ) ) ) {
|
||||
$term_id = $this->get_queried_term_id( $wp_query->tax_query );
|
||||
if ( $term_id ) {
|
||||
$language = $this->model->term->get_language( $term_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $wp_query->is_posts_page ) {
|
||||
$page_id = get_query_var( 'page_id' );
|
||||
if ( ! $page_id ) {
|
||||
$page_id = get_queried_object_id();
|
||||
}
|
||||
if ( $page_id && is_numeric( $page_id ) ) {
|
||||
$language = $this->model->post->get_language( (int) $page_id );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 3 === $this->options['force_lang'] ) {
|
||||
$requested_host = wp_parse_url( $requested_url, PHP_URL_HOST );
|
||||
foreach ( $this->options['domains'] as $lang => $domain ) {
|
||||
$host = wp_parse_url( $domain, PHP_URL_HOST );
|
||||
if ( $requested_host && $host && ltrim( $requested_host, 'w.' ) === ltrim( $host, 'w.' ) ) {
|
||||
$language = $this->model->get_language( $lang );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $language ) ) {
|
||||
/** @var PLL_Language $language */
|
||||
$language = $this->curlang;
|
||||
$redirect_url = $requested_url;
|
||||
} else {
|
||||
$redirect_url = $this->redirect_canonical( $requested_url, $language );
|
||||
$redirect_url = $this->options['force_lang'] ?
|
||||
$this->links_model->switch_language_in_link( $redirect_url, $language ) :
|
||||
$this->links_model->remove_language_from_link( $redirect_url ); // Works only for default permalinks.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters the canonical url detected by Polylang.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string|false $redirect_url False or the url to redirect to.
|
||||
* @param PLL_Language $language The language detected.
|
||||
*/
|
||||
$redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language );
|
||||
|
||||
if ( ! $redirect_url || $requested_url === $redirect_url ) {
|
||||
return $requested_url;
|
||||
}
|
||||
|
||||
if ( ! $do_redirect ) {
|
||||
return $redirect_url;
|
||||
}
|
||||
|
||||
// Protect against chained redirects.
|
||||
if ( $redirect_url === $this->check_canonical_url( $redirect_url, false ) && wp_validate_redirect( $redirect_url ) ) {
|
||||
wp_safe_redirect( $redirect_url, 301, POLYLANG );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the term_id of the requested term.
|
||||
*
|
||||
* @since 2.9
|
||||
*
|
||||
* @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
|
||||
* @return int
|
||||
*/
|
||||
protected function get_queried_term_id( $tax_query ) {
|
||||
$queried_terms = $tax_query->queried_terms;
|
||||
$taxonomy = $this->get_queried_taxonomy( $tax_query );
|
||||
|
||||
if ( ! isset( $queried_terms[ $taxonomy ]['terms'] ) || ! is_array( $queried_terms[ $taxonomy ]['terms'] ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( ! isset( $queried_terms[ $taxonomy ]['field'] ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$field = $queried_terms[ $taxonomy ]['field'];
|
||||
$term = reset( $queried_terms[ $taxonomy ]['terms'] );
|
||||
$lang = isset( $queried_terms['language']['terms'] ) ? reset( $queried_terms['language']['terms'] ) : '';
|
||||
|
||||
// We can get a term_id when requesting a plain permalink, eg /?cat=1.
|
||||
if ( 'term_id' === $field ) {
|
||||
return $term;
|
||||
}
|
||||
|
||||
// We get a slug when requesting a pretty permalink. Let's query all corresponding terms.
|
||||
$args = array(
|
||||
'lang' => '',
|
||||
'taxonomy' => $taxonomy,
|
||||
$field => $term,
|
||||
'hide_empty' => false,
|
||||
'fields' => 'ids',
|
||||
);
|
||||
$term_ids = get_terms( $args );
|
||||
|
||||
if ( ! is_array( $term_ids ) || empty( $term_ids ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$term_ids = array_filter( $term_ids, 'is_numeric' );
|
||||
|
||||
$filtered_terms_by_lang = array_filter(
|
||||
$term_ids,
|
||||
function ( $term_id ) use ( $lang ) {
|
||||
$term_lang = $this->model->term->get_language( (int) $term_id );
|
||||
|
||||
return ! empty( $term_lang ) && $term_lang->slug === $lang;
|
||||
}
|
||||
);
|
||||
|
||||
$tr_term = (int) reset( $filtered_terms_by_lang );
|
||||
|
||||
if ( ! empty( $tr_term ) ) {
|
||||
// The queried term exists in the desired language.
|
||||
return $tr_term;
|
||||
}
|
||||
|
||||
// The queried term doesn't exist in the desired language, let's return the first one retrieved.
|
||||
return (int) reset( $term_ids );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the taxonomy being queried.
|
||||
*
|
||||
* @since 2.9
|
||||
*
|
||||
* @param WP_Tax_Query $tax_query An instance of WP_Tax_Query.
|
||||
* @return string A taxonomy slug
|
||||
*/
|
||||
protected function get_queried_taxonomy( $tax_query ) {
|
||||
$queried_terms = $tax_query->queried_terms;
|
||||
unset( $queried_terms['language'] );
|
||||
|
||||
return (string) key( $queried_terms );
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the canonical redirect url through the dedicated WP function.
|
||||
*
|
||||
* @since 3.3
|
||||
*
|
||||
* @global WP_Query $wp_query WordPress Query object.
|
||||
*
|
||||
* @param string $url Requested url.
|
||||
* @param PLL_Language $language Language of the queried object.
|
||||
* @return string
|
||||
*/
|
||||
protected function redirect_canonical( $url, $language ) {
|
||||
/**
|
||||
* @var WP_Query
|
||||
*/
|
||||
global $wp_query;
|
||||
|
||||
$this->curlang = $language; // Hack to filter the `page_for_posts` option in the correct language.
|
||||
|
||||
$backup_wp_query = $wp_query;
|
||||
|
||||
if ( isset( $wp_query->tax_query ) ) {
|
||||
unset( $wp_query->tax_query->queried_terms['language'] );
|
||||
unset( $wp_query->query['lang'] );
|
||||
}
|
||||
|
||||
$redirect_url = redirect_canonical( $url, false );
|
||||
|
||||
$wp_query = $backup_wp_query;
|
||||
|
||||
return $redirect_url ?: $url;
|
||||
}
|
||||
}
|
||||
160
wp-content/plugins/polylang/src/frontend/choose-lang-content.php
Normal file
160
wp-content/plugins/polylang/src/frontend/choose-lang-content.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Choose the language when it is set from content
|
||||
* The language is set either in parse_query with priority 2 or in wp with priority 5
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Choose_Lang_Content extends PLL_Choose_Lang {
|
||||
|
||||
/**
|
||||
* Defers the language choice to the 'wp' action (when the content is known)
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
if ( ! did_action( 'pll_language_defined' ) ) {
|
||||
// Set the languages from content
|
||||
add_action( 'wp', array( $this, 'wp' ), 5 ); // Priority 5 for post types and taxonomies registered in wp hook with default priority
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites parent::set_language to remove the 'wp' action if the language is set before.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Language|false $curlang Optional. Current language. Default is `false`.
|
||||
* @return void
|
||||
*/
|
||||
protected function set_language( $curlang = false ): void {
|
||||
parent::set_language( $curlang );
|
||||
remove_action( 'wp', array( $this, 'wp' ), 5 ); // won't attempt to set the language a 2nd time
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language based on the queried content.
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.8 Renamed from `get_language_from_content()`.
|
||||
*
|
||||
* @return PLL_Language|false
|
||||
*/
|
||||
protected function get_current_language() {
|
||||
// No language set for 404.
|
||||
if ( is_404() || ( is_attachment() && ! $this->options['media_support'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$var = get_query_var( 'lang' );
|
||||
|
||||
if ( ! empty( $var ) && is_string( $var ) ) {
|
||||
$lang = explode( ',', $var );
|
||||
return $this->model->get_language( reset( $lang ) ); // Choose the first queried language.
|
||||
}
|
||||
|
||||
if ( is_singular() ) {
|
||||
foreach ( $this->get_singular_query_vars() as $var ) {
|
||||
if ( ! empty( $var ) && is_numeric( $var ) ) {
|
||||
return $this->model->post->get_language( (int) $var );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) {
|
||||
$tax_object = get_taxonomy( $taxonomy );
|
||||
|
||||
if ( empty( $tax_object ) || empty( $tax_object->query_var ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$var = get_query_var( $tax_object->query_var );
|
||||
|
||||
if ( ! is_string( $var ) || empty( $var ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$term = get_term_by( 'slug', $var, $taxonomy );
|
||||
|
||||
if ( ! $term instanceof WP_Term ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->model->term->get_language( $term->term_id );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of query vars corresponding to "singular" pages.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
private function get_singular_query_vars(): Generator {
|
||||
yield get_queried_object_id();
|
||||
yield get_query_var( 'p' );
|
||||
yield get_query_var( 'page_id' );
|
||||
yield get_query_var( 'attachment_id' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language for the home page.
|
||||
* Adds the lang query var when querying archives with no language code.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param WP_Query $query Instance of WP_Query.
|
||||
* @return void
|
||||
*/
|
||||
public function parse_main_query( $query ) {
|
||||
if ( empty( $GLOBALS['wp_the_query'] ) || $query !== $GLOBALS['wp_the_query'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$qv = $query->query_vars;
|
||||
|
||||
// Homepage is requested, let's set the language
|
||||
// Take care to avoid posts page for which is_home = 1
|
||||
if ( empty( $query->query ) && ( is_home() || is_page() ) ) {
|
||||
$this->set_language( $this->get_home_language() );
|
||||
$this->home_requested();
|
||||
}
|
||||
|
||||
parent::parse_main_query( $query );
|
||||
|
||||
$is_archive = ( count( $query->query ) == 1 && ! empty( $qv['paged'] ) ) ||
|
||||
$query->is_date ||
|
||||
$query->is_author ||
|
||||
( ! empty( $qv['post_type'] ) && $query->is_post_type_archive && $this->model->is_translated_post_type( $qv['post_type'] ) );
|
||||
|
||||
// Sets the language in case we hide the default language
|
||||
// Use $query->query['s'] as is_search is not set when search is empty
|
||||
// http://wordpress.org/support/topic/search-for-empty-string-in-default-language
|
||||
if ( $this->options['hide_default'] && ! isset( $qv['lang'] ) && ( $is_archive || isset( $query->query['s'] ) || ( count( $query->query ) == 1 && ! empty( $qv['feed'] ) ) ) ) {
|
||||
$this->set_language( $this->model->get_default_language() );
|
||||
$this->set_curlang_in_query( $query );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language from content.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function wp(): void {
|
||||
parent::set_language();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Choose the language when the language is managed by different domains
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
class PLL_Choose_Lang_Domain extends PLL_Choose_Lang_Url {
|
||||
|
||||
/**
|
||||
* Don't set any language cookie
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybe_setcookie() {}
|
||||
|
||||
/**
|
||||
* Don't redirect according to browser preferences
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @return PLL_Language
|
||||
*/
|
||||
public function get_preferred_language() {
|
||||
return $this->model->get_language( $this->links_model->get_language_from_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds query vars to query for home pages in all languages
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function home_requested() {
|
||||
$this->set_curlang_in_query( $GLOBALS['wp_query'] );
|
||||
/** This action is documented in src/frontend/choose-lang.php */
|
||||
do_action( 'pll_home_requested' );
|
||||
}
|
||||
}
|
||||
125
wp-content/plugins/polylang/src/frontend/choose-lang-url.php
Normal file
125
wp-content/plugins/polylang/src/frontend/choose-lang-url.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Choose the language when the language code is added to all urls
|
||||
* The language is set in plugins_loaded with priority 1 as done by WPML
|
||||
* Some actions have to be delayed to wait for $wp_rewrite availability
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Choose_Lang_Url extends PLL_Choose_Lang {
|
||||
/**
|
||||
* The name of the index file which is the entry point to all requests.
|
||||
* We need this before the global $wp_rewrite is created.
|
||||
* Also hardcoded in WP_Rewrite.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $index = 'index.php';
|
||||
|
||||
/**
|
||||
* Sets the language
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
if ( ! did_action( 'pll_language_defined' ) ) {
|
||||
$this->set_language();
|
||||
}
|
||||
|
||||
add_filter( 'request', array( $this, 'request' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language according to information found in the url.
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.8 Renamed from `get_language_from_url()`.
|
||||
*
|
||||
* @return PLL_Language|false
|
||||
*/
|
||||
protected function get_current_language() {
|
||||
$host = str_replace( 'www.', '', (string) wp_parse_url( $this->links_model->home, PHP_URL_HOST ) ); // Remove www. for the comparison.
|
||||
$home_path = (string) wp_parse_url( $this->links_model->home, PHP_URL_PATH );
|
||||
|
||||
$requested_url = pll_get_requested_url();
|
||||
$requested_host = str_replace( 'www.', '', (string) wp_parse_url( $requested_url, PHP_URL_HOST ) ); // Remove www. for the comparison.
|
||||
$requested_path = rtrim( str_replace( $this->index, '', (string) wp_parse_url( $requested_url, PHP_URL_PATH ) ), '/' ); // Some PHP setups turn requests for / into /index.php in REQUEST_URI.
|
||||
$requested_query = wp_parse_url( $requested_url, PHP_URL_QUERY );
|
||||
|
||||
// Home is requested.
|
||||
if ( $requested_host === $host && $requested_path === $home_path && empty( $requested_query ) ) {
|
||||
add_action( 'setup_theme', array( $this, 'home_requested' ) );
|
||||
return $this->get_home_language();
|
||||
}
|
||||
|
||||
// Take care to post & page preview http://wordpress.org/support/topic/static-frontpage-url-parameter-url-language-information.
|
||||
if ( isset( $_GET['preview'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
foreach ( array( 'p', 'page_id' ) as $var ) {
|
||||
if ( empty( $_GET[ $var ] ) || ! is_numeric( $_GET[ $var ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
continue;
|
||||
}
|
||||
$lang = $this->model->post->get_language( (int) $_GET[ $var ] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return $lang ?: $this->model->get_default_language();
|
||||
}
|
||||
}
|
||||
|
||||
// Take care to (unattached) attachments.
|
||||
if ( ! empty( $_GET['attachment_id'] ) && is_numeric( $_GET['attachment_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
return $this->model->post->get_language( (int) $_GET['attachment_id'] ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
$slug = $this->links_model->get_language_from_url();
|
||||
|
||||
if ( ! empty( $slug ) ) {
|
||||
return $this->model->get_language( $slug );
|
||||
}
|
||||
|
||||
if ( $this->options['hide_default'] ) {
|
||||
return $this->model->get_default_language();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the current language in query vars
|
||||
* useful for subdomains and multiple domains
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param array $qv main request query vars
|
||||
* @return array modified query vars
|
||||
*/
|
||||
public function request( $qv ) {
|
||||
// FIXME take care not to break untranslated content
|
||||
// FIXME media ?
|
||||
|
||||
// Untranslated post types
|
||||
if ( isset( $qv['post_type'] ) && ! $this->model->is_translated_post_type( $qv['post_type'] ) ) {
|
||||
return $qv;
|
||||
}
|
||||
|
||||
// Untranslated taxonomies
|
||||
$tax_qv = array_filter( wp_list_pluck( get_taxonomies( array(), 'objects' ), 'query_var' ) ); // Get all taxonomies query vars
|
||||
$tax_qv = array_intersect( $tax_qv, array_keys( $qv ) ); // Get all queried taxonomies query vars
|
||||
|
||||
if ( ! $this->model->is_translated_taxonomy( array_keys( $tax_qv ) ) ) {
|
||||
return $qv;
|
||||
}
|
||||
|
||||
if ( isset( $this->curlang ) && empty( $qv['lang'] ) ) {
|
||||
$qv['lang'] = $this->curlang->slug;
|
||||
}
|
||||
|
||||
return $qv;
|
||||
}
|
||||
}
|
||||
377
wp-content/plugins/polylang/src/frontend/choose-lang.php
Normal file
377
wp-content/plugins/polylang/src/frontend/choose-lang.php
Normal file
@@ -0,0 +1,377 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class to choose the language
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
abstract class PLL_Choose_Lang {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
public $links_model;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Frontend $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( PLL_Frontend &$polylang ) {
|
||||
$this->links_model = &$polylang->links_model;
|
||||
$this->model = &$polylang->model;
|
||||
$this->options = $polylang->options;
|
||||
|
||||
$this->curlang = &$polylang->curlang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language for ajax requests
|
||||
* and setup actions
|
||||
* Any child class must call this method if it overrides it
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
if ( Polylang::is_ajax_on_front() || ! wp_using_themes() ) {
|
||||
$this->set_language( empty( $_REQUEST['lang'] ) ? $this->get_preferred_language() : $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
|
||||
add_action( 'pre_comment_on_post', array( $this, 'pre_comment_on_post' ) ); // sets the language of comment
|
||||
add_action( 'parse_query', array( $this, 'parse_main_query' ), 2 ); // sets the language in special cases
|
||||
add_action( 'wp', array( $this, 'maybe_setcookie' ), 7 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current language.
|
||||
* Also fires the action 'pll_language_defined'.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Language|false $curlang Optional. Current language. Default is `false`.
|
||||
* @return void
|
||||
*/
|
||||
protected function set_language( $curlang = false ): void {
|
||||
// Don't set the language a second time.
|
||||
if ( isset( $this->curlang ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $curlang instanceof PLL_Language ) {
|
||||
$curlang = $this->get_current_language();
|
||||
|
||||
if ( ! $curlang instanceof PLL_Language ) {
|
||||
$curlang = $this->get_preferred_language();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the language before it is set.
|
||||
*
|
||||
* @since 0.9
|
||||
* @since 3.8 Is used all the time, not only when the language is defined by the content.
|
||||
*
|
||||
* @param PLL_Language|false $curlang Language object or false if none was found.
|
||||
*/
|
||||
$curlang = apply_filters( 'pll_get_current_language', $curlang ?? false );
|
||||
|
||||
if ( ! $curlang instanceof PLL_Language ) {
|
||||
$curlang = $this->model->get_default_language();
|
||||
|
||||
if ( ! $curlang instanceof PLL_Language ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->curlang = $curlang;
|
||||
|
||||
$GLOBALS['text_direction'] = $this->curlang->is_rtl ? 'rtl' : 'ltr';
|
||||
if ( did_action( 'wp_default_styles' ) ) {
|
||||
wp_styles()->text_direction = $GLOBALS['text_direction'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when the current language is defined.
|
||||
*
|
||||
* @since 0.9.5
|
||||
*
|
||||
* @param string $slug Current language code.
|
||||
* @param PLL_Language $curlang Current language object.
|
||||
*/
|
||||
do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language to assign as the current one.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return PLL_Language|false
|
||||
*/
|
||||
abstract protected function get_current_language();
|
||||
|
||||
/**
|
||||
* Set a cookie to remember the language.
|
||||
* Setting PLL_COOKIE to false will disable cookie although it will break some functionalities
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybe_setcookie() {
|
||||
// Don't set cookie in javascript when a cache plugin is active.
|
||||
if ( ! pll_is_cache_active() && ! empty( $this->curlang ) && ! is_404() && ! is_favicon() ) {
|
||||
$args = array(
|
||||
'domain' => 2 === $this->options['force_lang'] ? wp_parse_url( $this->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN,
|
||||
'samesite' => 3 === $this->options['force_lang'] ? 'None' : 'Lax',
|
||||
);
|
||||
PLL_Cookie::set( $this->curlang->slug, $args );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preferred language according to the browser preferences.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @return string|bool The preferred language slug or false.
|
||||
*/
|
||||
public function get_preferred_browser_language() {
|
||||
if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
|
||||
$accept_langs = PLL_Accept_Languages_Collection::from_accept_language_header( sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) );
|
||||
|
||||
$accept_langs->bubble_sort();
|
||||
|
||||
$languages = $this->model->languages->filter( 'hide_empty' )->get_list(); // Hides languages with no post.
|
||||
|
||||
/**
|
||||
* Filters the list of languages to use to match the browser preferences.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $languages Array of PLL_Language objects.
|
||||
*/
|
||||
$languages = apply_filters( 'pll_languages_for_browser_preferences', $languages );
|
||||
|
||||
return $accept_langs->find_best_match( $languages );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred language
|
||||
* either from the cookie if it's a returning visit
|
||||
* or according to browser preference
|
||||
* or the default language
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return PLL_Language|false browser preferred language or default language
|
||||
*/
|
||||
public function get_preferred_language() {
|
||||
$language = false;
|
||||
$cookie = false;
|
||||
|
||||
if ( isset( $_COOKIE[ PLL_COOKIE ] ) ) {
|
||||
// Check first if the user was already browsing this site.
|
||||
$language = sanitize_key( $_COOKIE[ PLL_COOKIE ] );
|
||||
$cookie = true;
|
||||
} elseif ( $this->options['browser'] ) {
|
||||
$language = $this->get_preferred_browser_language();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the visitor's preferred language (normally set first by cookie
|
||||
* if this is not the first visit, then by the browser preferences).
|
||||
* If no preferred language has been found or set by this filter,
|
||||
* Polylang fallbacks to the default language
|
||||
*
|
||||
* @since 1.0
|
||||
* @since 2.7 Added $cookie parameter.
|
||||
*
|
||||
* @param string|bool $language Preferred language code, false if none has been found.
|
||||
* @param bool $cookie Whether the preferred language has been defined by the cookie.
|
||||
*/
|
||||
$slug = apply_filters( 'pll_preferred_language', $language, $cookie );
|
||||
|
||||
// Return default if there is no preferences in the browser or preferences does not match our languages or it is requested not to use the browser preference.
|
||||
$lang = $this->model->get_language( $slug );
|
||||
return $lang ?: $this->model->get_default_language();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language when home page is requested.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return PLL_Language|false
|
||||
*/
|
||||
protected function get_home_language() {
|
||||
// Test referer in case PLL_COOKIE is set to false. Since WP 3.6.1, wp_get_referer() validates the host which is exactly what we want.
|
||||
// Thanks to Ov3rfly http://wordpress.org/support/topic/enhance-feature-when-front-page-is-visited-set-language-according-to-browser.
|
||||
if ( $this->options['hide_default'] && ( wp_get_referer() || ! $this->options['browser'] ) ) {
|
||||
return $this->model->get_default_language();
|
||||
}
|
||||
return $this->get_preferred_language(); // Returns the language according to browser preference or default language.
|
||||
}
|
||||
|
||||
/**
|
||||
* To call when the home page has been requested
|
||||
* Make sure to call this after 'setup_theme' has been fired as we need $wp_query
|
||||
* Performs a redirection to the home page in the current language if needed
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function home_requested() {
|
||||
if ( empty( $this->curlang ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We are already on the right page
|
||||
if ( $this->curlang->is_default && $this->options['hide_default'] ) {
|
||||
$this->set_curlang_in_query( $GLOBALS['wp_query'] );
|
||||
|
||||
/**
|
||||
* Fires when the site root page is requested
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
do_action( 'pll_home_requested' );
|
||||
}
|
||||
// Redirect to the home page in the right language
|
||||
// Test to avoid crash if get_home_url returns something wrong
|
||||
// FIXME why this happens? http://wordpress.org/support/topic/polylang-crashes-1
|
||||
// Don't redirect if $_POST is not empty as it could break other plugins
|
||||
elseif ( is_string( $redirect = $this->curlang->get_home_url() ) && empty( $_POST ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
// Don't forget the query string which may be added by plugins
|
||||
$query_string = wp_parse_url( pll_get_requested_url(), PHP_URL_QUERY );
|
||||
if ( ! empty( $query_string ) ) {
|
||||
$redirect .= ( $this->links_model->using_permalinks ? '?' : '&' ) . $query_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a visitor reaches the site home, Polylang redirects to the home page in the correct language.
|
||||
* This filter allows plugins to modify the redirected url or prevent this redirection
|
||||
* /!\ this filter may be fired *before* the theme is loaded
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @param string $redirect the url the visitor will be redirected to
|
||||
*/
|
||||
$redirect = apply_filters( 'pll_redirect_home', $redirect );
|
||||
if ( $redirect && wp_validate_redirect( $redirect ) ) {
|
||||
$this->maybe_setcookie();
|
||||
header( 'Vary: Accept-Language' );
|
||||
wp_safe_redirect( $redirect, 302, POLYLANG );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the language when posting a comment
|
||||
*
|
||||
* @since 0.8.4
|
||||
*
|
||||
* @param int $post_id the post being commented
|
||||
* @return void
|
||||
*/
|
||||
public function pre_comment_on_post( $post_id ) {
|
||||
$this->set_language( $this->model->post->get_language( $post_id ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies some main query vars for the home page and the page for posts
|
||||
* to enable one home page (and one page for posts) per language.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param WP_Query $query Instance of WP_Query.
|
||||
* @return void
|
||||
*/
|
||||
public function parse_main_query( $query ) {
|
||||
if ( ! $query->is_main_query() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This filter allows to set the language based on information contained in the main query
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param PLL_Language|false $lang Language object or false.
|
||||
* @param WP_Query $query WP_Query object.
|
||||
*/
|
||||
if ( $lang = apply_filters( 'pll_set_language_from_query', false, $query ) ) {
|
||||
$this->set_language( $lang );
|
||||
$this->set_curlang_in_query( $query );
|
||||
} elseif ( ( count( $query->query ) == 1 || ( is_paged() && count( $query->query ) == 2 ) ) && $lang = get_query_var( 'lang' ) ) {
|
||||
$lang = $this->model->get_language( $lang );
|
||||
$this->set_language( $lang ); // Set the language now otherwise it will be too late to filter sticky posts!
|
||||
|
||||
// Set is_home on translated home page when it displays posts. It must be true on page 2, 3... too.
|
||||
$query->is_home = true;
|
||||
$query->is_tax = false;
|
||||
$query->is_archive = false;
|
||||
|
||||
// Filters is_front_page() in case a static front page is not translated in this language.
|
||||
add_filter( 'option_show_on_front', array( $this, 'filter_option_show_on_front' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the option show_on_front when the current front page displays posts.
|
||||
*
|
||||
* This is useful when a static front page is not translated in all languages.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function filter_option_show_on_front() {
|
||||
return 'posts';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current language in the query.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param WP_Query $query Instance of WP_Query.
|
||||
* @return void
|
||||
*/
|
||||
protected function set_curlang_in_query( &$query ) {
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
$pll_query = new PLL_Query( $query, $this->model );
|
||||
$pll_query->set_language( $this->curlang );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto translates the posts and terms ids
|
||||
* Useful for example for themes querying a specific cat
|
||||
*
|
||||
* @since 1.1
|
||||
*/
|
||||
class PLL_Frontend_Auto_Translate {
|
||||
/**
|
||||
* @var PLL_Model
|
||||
*/
|
||||
public $model;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->model = &$polylang->model;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
|
||||
add_action( 'parse_query', array( $this, 'translate_included_ids_in_query' ), 100 ); // After all Polylang filters.
|
||||
add_filter( 'get_terms_args', array( $this, 'get_terms_args' ), 20, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the translated post in the current language.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param int $post_id The ID of the post to translate.
|
||||
* @return int
|
||||
*
|
||||
* @phpstan-return int<0, max>
|
||||
*/
|
||||
protected function get_post( $post_id ) {
|
||||
return $this->model->post->get( $post_id, $this->curlang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the translated term in the current language.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param int $term_id The ID of the term to translate.
|
||||
* @return int
|
||||
*
|
||||
* @phpstan-return int<0, max>
|
||||
*/
|
||||
protected function get_term( $term_id ) {
|
||||
return $this->model->term->get( $term_id, $this->curlang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters posts query to automatically translate included ids
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @param WP_Query $query WP_Query object
|
||||
* @return void
|
||||
*/
|
||||
public function translate_included_ids_in_query( $query ) {
|
||||
global $wpdb;
|
||||
$qv = &$query->query_vars;
|
||||
|
||||
if ( $query->is_main_query() || isset( $qv['lang'] ) || ( ! empty( $qv['post_type'] ) && ! $this->model->is_translated_post_type( $qv['post_type'] ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// /!\ always keep untranslated as is
|
||||
|
||||
// Term ids separated by a comma
|
||||
$arr = array();
|
||||
if ( ! empty( $qv['cat'] ) ) {
|
||||
foreach ( explode( ',', $qv['cat'] ) as $cat ) {
|
||||
$tr = $this->get_term( abs( (int) $cat ) );
|
||||
$arr[] = $cat < 0 ? -$tr : $tr;
|
||||
}
|
||||
|
||||
$qv['cat'] = implode( ',', $arr );
|
||||
}
|
||||
|
||||
// Category_name
|
||||
$arr = array();
|
||||
if ( ! empty( $qv['category_name'] ) ) {
|
||||
foreach ( explode( ',', $qv['category_name'] ) as $slug ) {
|
||||
$arr[] = $this->get_translated_term_by( 'slug', $slug, 'category' );
|
||||
}
|
||||
|
||||
$qv['category_name'] = implode( ',', $arr );
|
||||
}
|
||||
|
||||
// Array of term ids
|
||||
foreach ( array( 'category__and', 'category__in', 'category__not_in', 'tag__and', 'tag__in', 'tag__not_in' ) as $key ) {
|
||||
$arr = array();
|
||||
if ( ! empty( $qv[ $key ] ) ) {
|
||||
foreach ( $qv[ $key ] as $cat ) {
|
||||
$tr = $this->get_term( $cat );
|
||||
$arr[] = $tr ?: $cat;
|
||||
}
|
||||
$qv[ $key ] = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
// Tag
|
||||
if ( ! empty( $qv['tag'] ) ) {
|
||||
$qv['tag'] = $this->translate_terms_list( $qv['tag'], 'post_tag' );
|
||||
}
|
||||
|
||||
// tag_id can only take one id
|
||||
if ( ! empty( $qv['tag_id'] ) && $tr_id = $this->get_term( $qv['tag_id'] ) ) {
|
||||
$qv['tag_id'] = $tr_id;
|
||||
}
|
||||
|
||||
// Array of tag slugs
|
||||
foreach ( array( 'tag_slug__and', 'tag_slug__in' ) as $key ) {
|
||||
$arr = array();
|
||||
if ( ! empty( $qv[ $key ] ) ) {
|
||||
foreach ( $qv[ $key ] as $slug ) {
|
||||
$arr[] = $this->get_translated_term_by( 'slug', $slug, 'post_tag' );
|
||||
}
|
||||
|
||||
$qv[ $key ] = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom taxonomies
|
||||
// According to the codex, this type of query is deprecated as of WP 3.1 but it does not appear in WP 3.5 source code
|
||||
foreach ( array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) ) as $taxonomy ) {
|
||||
$tax = get_taxonomy( $taxonomy );
|
||||
if ( ! empty( $tax ) && ! empty( $tax->query_var ) && ! empty( $qv[ $tax->query_var ] ) ) {
|
||||
$qv[ $tax->query_var ] = $this->translate_terms_list( $qv[ $tax->query_var ], $taxonomy );
|
||||
}
|
||||
}
|
||||
|
||||
// Tax_query since WP 3.1
|
||||
if ( ! empty( $qv['tax_query'] ) && is_array( $qv['tax_query'] ) ) {
|
||||
$qv['tax_query'] = $this->translate_tax_query_recursive( $qv['tax_query'] );
|
||||
}
|
||||
|
||||
// p, page_id, post_parent can only take one id
|
||||
foreach ( array( 'p', 'page_id', 'post_parent' ) as $key ) {
|
||||
if ( ! empty( $qv[ $key ] ) && $tr_id = $this->get_post( $qv[ $key ] ) ) {
|
||||
$qv[ $key ] = $tr_id;
|
||||
}
|
||||
}
|
||||
|
||||
// name, can only take one slug
|
||||
if ( ! empty( $qv['name'] ) && is_string( $qv['name'] ) ) {
|
||||
if ( empty( $qv['post_type'] ) ) {
|
||||
$post_types = array( 'post' );
|
||||
} elseif ( 'any' === $qv['post_type'] ) {
|
||||
$post_types = get_post_types( array( 'exclude_from_search' => false ) ); // May return a empty array
|
||||
} else {
|
||||
$post_types = (array) $qv['post_type'];
|
||||
}
|
||||
|
||||
if ( ! empty( $post_types ) ) {
|
||||
// No function to get post by name except get_posts itself
|
||||
$id = $wpdb->get_var(
|
||||
sprintf(
|
||||
"SELECT ID from {$wpdb->posts}
|
||||
WHERE {$wpdb->posts}.post_type IN ( '%s' )
|
||||
AND post_name='%s'",
|
||||
implode( "', '", esc_sql( $post_types ) ),
|
||||
esc_sql( $qv['name'] )
|
||||
)
|
||||
);
|
||||
if ( $id ) {
|
||||
$tr_id = $this->get_post( $id );
|
||||
if ( $tr_id ) {
|
||||
$tr = get_post( $tr_id );
|
||||
if ( $tr ) {
|
||||
$qv['name'] = $tr->post_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pagename, the page id is already available in queried_object_id
|
||||
if ( ! empty( $qv['pagename'] ) && ! empty( $query->queried_object_id ) && $tr_id = $this->get_post( $query->queried_object_id ) ) {
|
||||
$query->queried_object_id = $tr_id;
|
||||
$qv['pagename'] = get_page_uri( $tr_id );
|
||||
}
|
||||
|
||||
// Array of post ids
|
||||
// post_parent__in & post_parent__not_in since WP 3.6
|
||||
foreach ( array( 'post__in', 'post__not_in', 'post_parent__in', 'post_parent__not_in' ) as $key ) { // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn
|
||||
$arr = array();
|
||||
if ( ! empty( $qv[ $key ] ) ) {
|
||||
// post__in used by the 2 functions below
|
||||
// Useless to filter them as output is already in the right language and would result in performance loss
|
||||
foreach ( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ) as $trace ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions
|
||||
if ( in_array( $trace['function'], array( 'wp_nav_menu', 'gallery_shortcode' ) ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $qv[ $key ] as $p ) {
|
||||
$tr = $this->get_post( $p );
|
||||
$arr[] = $tr ?: $p;
|
||||
}
|
||||
|
||||
$qv[ $key ] = $arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the terms query to automatically translate included ids.
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @param array $args An array of get_terms() arguments.
|
||||
* @param array $taxonomies An array of taxonomy names.
|
||||
* @return array
|
||||
*/
|
||||
public function get_terms_args( $args, $taxonomies ) {
|
||||
if ( ! isset( $args['lang'] ) && ! empty( $args['include'] ) && ( empty( $taxonomies ) || $this->model->is_translated_taxonomy( $taxonomies ) ) ) {
|
||||
$arr = array();
|
||||
|
||||
foreach ( wp_parse_id_list( $args['include'] ) as $id ) {
|
||||
$tr = $this->get_term( $id );
|
||||
$arr[] = $tr ?: $id;
|
||||
}
|
||||
|
||||
$args['include'] = $arr;
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates tax queries
|
||||
* Compatible with nested tax queries introduced in WP 4.1
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param array $tax_queries An array of tax queries.
|
||||
* @return array Translated tax queries.
|
||||
*/
|
||||
protected function translate_tax_query_recursive( $tax_queries ) {
|
||||
foreach ( $tax_queries as $key => $q ) {
|
||||
if ( ! is_array( $q ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $q['taxonomy'], $q['terms'] ) && $this->model->is_translated_taxonomy( $q['taxonomy'] ) ) {
|
||||
$arr = array();
|
||||
$field = isset( $q['field'] ) && in_array( $q['field'], array( 'slug', 'name' ) ) ? $q['field'] : 'term_id';
|
||||
foreach ( (array) $q['terms'] as $t ) {
|
||||
$arr[] = $this->get_translated_term_by( $field, $t, $q['taxonomy'] );
|
||||
}
|
||||
|
||||
$tax_queries[ $key ]['terms'] = $arr;
|
||||
} else {
|
||||
// Nested queries.
|
||||
$tax_queries[ $key ] = $this->translate_tax_query_recursive( $q );
|
||||
}
|
||||
}
|
||||
|
||||
return $tax_queries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a term given one field.
|
||||
*
|
||||
* @since 2.3.3
|
||||
*
|
||||
* @param string $field Either 'slug', 'name', 'term_id', or 'term_taxonomy_id'
|
||||
* @param string|int $term Search for this term value
|
||||
* @param string $taxonomy Taxonomy name.
|
||||
* @return string|int Translated term slug, name, term_id or term_taxonomy_id
|
||||
*/
|
||||
protected function get_translated_term_by( $field, $term, $taxonomy ) {
|
||||
if ( 'term_id' === $field ) {
|
||||
if ( $tr_id = $this->get_term( $term ) ) {
|
||||
return $tr_id;
|
||||
}
|
||||
} else {
|
||||
$terms = get_terms( array( 'taxonomy' => $taxonomy, $field => $term, 'lang' => '' ) );
|
||||
|
||||
if ( ! empty( $terms ) && is_array( $terms ) ) {
|
||||
$t = reset( $terms );
|
||||
if ( ! $t instanceof WP_Term ) {
|
||||
return $term;
|
||||
}
|
||||
$tr_id = $this->get_term( $t->term_id );
|
||||
|
||||
if ( ! is_wp_error( $tr = get_term( $tr_id, $taxonomy ) ) ) {
|
||||
return $tr->$field;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $term;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a list of term slugs provided either as an array or a string
|
||||
* with slugs separated by a comma or a '+'.
|
||||
*
|
||||
* @since 3.2.8
|
||||
*
|
||||
* @param string|string[] $query_var The list of term slugs.
|
||||
* @param string $taxonomy The taxonomy for terms.
|
||||
* @return string|string[] The translated list.
|
||||
*/
|
||||
protected function translate_terms_list( $query_var, $taxonomy ) {
|
||||
$slugs = array();
|
||||
|
||||
if ( is_array( $query_var ) ) {
|
||||
$slugs = $query_var;
|
||||
} elseif ( is_string( $query_var ) ) {
|
||||
$sep = strpos( $query_var, ',' ) !== false ? ',' : '+'; // Two possible separators.
|
||||
$slugs = explode( $sep, $query_var );
|
||||
}
|
||||
|
||||
foreach ( $slugs as &$slug ) {
|
||||
if ( ! is_string( $slug ) ) {
|
||||
// We got an unexpected query var, let return it unchanged.
|
||||
return $query_var;
|
||||
}
|
||||
$slug = $this->get_translated_term_by( 'slug', $slug, $taxonomy );
|
||||
}
|
||||
|
||||
if ( ! empty( $sep ) ) {
|
||||
return implode( $sep, $slugs );
|
||||
}
|
||||
|
||||
return $slugs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages links filters on frontend
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Links|null
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* Our internal non persistent cache object
|
||||
*
|
||||
* @var PLL_Cache<string>
|
||||
*/
|
||||
public $cache;
|
||||
|
||||
/**
|
||||
* Stores a list of files and functions that home_url() must not filter.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $black_list = array();
|
||||
|
||||
/**
|
||||
* Stores a list of files and functions that home_url() must filter.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $white_list = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Adds filters once the language is defined
|
||||
* Low priority on links filters to come after any other modification
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param PLL_Frontend $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
$this->curlang = &$polylang->curlang;
|
||||
$this->cache = new PLL_Cache();
|
||||
|
||||
// Rewrites author and date links to filter them by language
|
||||
foreach ( array( 'feed_link', 'author_link', 'search_link', 'year_link', 'month_link', 'day_link' ) as $filter ) {
|
||||
add_filter( $filter, array( $this, 'archive_link' ), 20 );
|
||||
}
|
||||
|
||||
// Meta in the html head section
|
||||
add_action( 'wp_head', array( $this, 'wp_head' ), 1 );
|
||||
|
||||
// Modifies the home url
|
||||
if ( pll_get_constant( 'PLL_FILTER_HOME_URL', true ) ) {
|
||||
add_filter( 'home_url', array( $this, 'home_url' ), 10, 2 );
|
||||
}
|
||||
|
||||
if ( $this->options['force_lang'] > 1 ) {
|
||||
// Rewrites next and previous post links when not automatically done by WordPress
|
||||
add_filter( 'get_pagenum_link', array( $this, 'archive_link' ), 20 );
|
||||
|
||||
add_filter( 'get_shortlink', array( $this, 'shortlink' ), 20, 2 );
|
||||
|
||||
// Rewrites ajax url
|
||||
add_filter( 'admin_url', array( $this, 'admin_url' ), 10, 2 );
|
||||
}
|
||||
|
||||
add_filter( 'oembed_endpoint_url', array( $this, 'add_current_language_url_query' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the author and date links to add the language parameter (as well as feed link).
|
||||
*
|
||||
* @since 0.4
|
||||
*
|
||||
* @param string $link The permalink to the archive.
|
||||
* @return string The modified link.
|
||||
*/
|
||||
public function archive_link( $link ) {
|
||||
return $this->links_model->switch_language_in_link( $link, $this->curlang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies page links
|
||||
* and caches the result
|
||||
*
|
||||
* @since 1.7
|
||||
*
|
||||
* @param string $link The page link.
|
||||
* @param int $post_id The post ID.
|
||||
* @return string The modified page link.
|
||||
*/
|
||||
public function _get_page_link( $link, $post_id ) {
|
||||
$cache_key = "post:{$post_id}:{$link}";
|
||||
if ( false === $_link = $this->cache->get( $cache_key ) ) {
|
||||
$_link = parent::_get_page_link( $link, $post_id );
|
||||
$this->cache->set( $cache_key, $_link );
|
||||
}
|
||||
return $_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies attachment links
|
||||
* and caches the result
|
||||
*
|
||||
* @since 1.6.2
|
||||
*
|
||||
* @param string $link The attachment link.
|
||||
* @param int $post_id The attachment post ID.
|
||||
* @return string The modified attachment link.
|
||||
*/
|
||||
public function attachment_link( $link, $post_id ) {
|
||||
$cache_key = "post:{$post_id}:{$link}";
|
||||
if ( false === $_link = $this->cache->get( $cache_key ) ) {
|
||||
$_link = parent::attachment_link( $link, $post_id );
|
||||
$this->cache->set( $cache_key, $_link );
|
||||
}
|
||||
return $_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies custom posts links
|
||||
* and caches the result.
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $link The post link.
|
||||
* @param WP_Post $post The post object.
|
||||
* @return string The modified post link.
|
||||
*/
|
||||
public function post_type_link( $link, $post ) {
|
||||
$cache_key = "post:{$post->ID}:{$link}";
|
||||
if ( false === $_link = $this->cache->get( $cache_key ) ) {
|
||||
$_link = parent::post_type_link( $link, $post );
|
||||
$this->cache->set( $cache_key, $_link );
|
||||
}
|
||||
return $_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies filtered taxonomies ( post format like ) and translated taxonomies links
|
||||
* and caches the result.
|
||||
*
|
||||
* @since 0.7
|
||||
*
|
||||
* @param string $link The term link.
|
||||
* @param WP_Term $term The term object.
|
||||
* @param string $tax The taxonomy name.
|
||||
* @return string The modified link.
|
||||
*/
|
||||
public function term_link( $link, $term, $tax ) {
|
||||
$cache_key = "term:{$term->term_id}:{$link}";
|
||||
if ( false === $_link = $this->cache->get( $cache_key ) ) {
|
||||
if ( in_array( $tax, $this->model->get_filtered_taxonomies() ) ) {
|
||||
$_link = $this->links_model->switch_language_in_link( $link, $this->curlang );
|
||||
|
||||
/** This filter is documented in src/filters-links.php */
|
||||
$_link = apply_filters( 'pll_term_link', $_link, $this->curlang, $term );
|
||||
}
|
||||
|
||||
else {
|
||||
$_link = parent::term_link( $link, $term, $tax );
|
||||
}
|
||||
$this->cache->set( $cache_key, $_link );
|
||||
}
|
||||
return $_link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the post short link when using one domain or subdomain per language.
|
||||
*
|
||||
* @since 2.6.9
|
||||
*
|
||||
* @param string $link Post permalink.
|
||||
* @param int $post_id Post id.
|
||||
* @return string Post permalink with the correct domain.
|
||||
*/
|
||||
public function shortlink( $link, $post_id ) {
|
||||
$post_type = get_post_type( $post_id );
|
||||
return $this->model->is_translated_post_type( $post_type ) ? $this->links_model->switch_language_in_link( $link, $this->model->post->get_language( $post_id ) ) : $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs references to translated pages ( if exists ) in the html head section
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function wp_head() {
|
||||
// Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2
|
||||
// Don't output anything on paged pages and paged posts
|
||||
if ( is_paged() || ( is_singular() && ( $page = get_query_var( 'page' ) ) && $page > 1 ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$urls = array();
|
||||
|
||||
// Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
if ( $url = $this->links->get_translation_url( $language ) ) {
|
||||
$urls[ $language->get_locale( 'display' ) ] = $url;
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs the section only if there are translations ( $urls always contains self link )
|
||||
if ( ! empty( $urls ) && count( $urls ) > 1 ) {
|
||||
$languages = array();
|
||||
$hreflangs = array();
|
||||
|
||||
// Prepare the list of languages to remove the country code
|
||||
foreach ( array_keys( $urls ) as $locale ) {
|
||||
$split = explode( '-', $locale );
|
||||
$languages[ $locale ] = reset( $split );
|
||||
}
|
||||
|
||||
$count = array_count_values( $languages );
|
||||
|
||||
foreach ( $urls as $locale => $url ) {
|
||||
$lang = $count[ $languages[ $locale ] ] > 1 ? $locale : $languages[ $locale ]; // Output the country code only when necessary
|
||||
$hreflangs[ $lang ] = $url;
|
||||
}
|
||||
|
||||
// Adds the site root url when the default language code is not hidden
|
||||
// See https://wordpress.org/support/topic/implementation-of-hreflangx-default
|
||||
if ( is_front_page() && ! $this->options['hide_default'] && $this->options['force_lang'] < 3 ) {
|
||||
$hreflangs['x-default'] = home_url( '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the list of rel hreflang attributes
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @param array $hreflangs Array of urls with language codes as keys
|
||||
*/
|
||||
$hreflangs = apply_filters( 'pll_rel_hreflang_attributes', $hreflangs );
|
||||
|
||||
foreach ( $hreflangs as $lang => $url ) {
|
||||
printf( '<link rel="alternate" href="%s" hreflang="%s" />' . "\n", esc_url( $url ), esc_attr( $lang ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the home url to get the right language.
|
||||
*
|
||||
* @since 0.4
|
||||
*
|
||||
* @param string $url The home URL including scheme and path.
|
||||
* @param string $path Path relative to the home URL.
|
||||
* @return string
|
||||
*/
|
||||
public function home_url( $url, $path ) {
|
||||
if ( ! ( did_action( 'template_redirect' ) || did_action( 'login_init' ) ) || rtrim( $url, '/' ) != $this->links_model->home ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
// We *want* to filter the home url in these cases
|
||||
if ( empty( $this->white_list ) ) {
|
||||
// On Windows get_theme_root() mixes / and \
|
||||
// We want only \ for the comparison with debug_backtrace
|
||||
$theme_root = get_theme_root();
|
||||
$theme_root = ( false === strpos( $theme_root, '\\' ) ) ? $theme_root : str_replace( '/', '\\', $theme_root );
|
||||
|
||||
$white_list = array(
|
||||
array( 'file' => $theme_root ),
|
||||
array( 'function' => 'wp_nav_menu' ),
|
||||
array( 'function' => 'login_footer' ),
|
||||
array( 'function' => 'get_custom_logo' ),
|
||||
array( 'function' => 'render_block_core_site_title' ),
|
||||
array( 'function' => 'render_block_core_home_link' ),
|
||||
);
|
||||
|
||||
if ( 3 === $this->options['force_lang'] ) {
|
||||
$white_list[] = array( 'function' => 'redirect_canonical' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the white list of the Polylang 'home_url' filter.
|
||||
*
|
||||
* @since 1.1.2
|
||||
*
|
||||
* @param string[][] $white_list An array of arrays each of them having a 'file' key
|
||||
* and/or a 'function' key to decide which functions in
|
||||
* which files using home_url() calls must be filtered.
|
||||
*/
|
||||
$this->white_list = apply_filters( 'pll_home_url_white_list', $white_list );
|
||||
}
|
||||
|
||||
// We don't want to filter the home url in these cases.
|
||||
if ( empty( $this->black_list ) ) {
|
||||
$black_list = array(
|
||||
array( 'file' => 'searchform.php' ), // Since WP 3.6 searchform.php is passed through get_search_form.
|
||||
array( 'function' => 'get_search_form' ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the black list of the Polylang 'home_url' filter.
|
||||
*
|
||||
* @since 1.1.2
|
||||
*
|
||||
* @param string[][] $black_list An array of arrays each of them having a 'file' key
|
||||
* and/or a 'function' key to decide which functions in
|
||||
* which files using home_url() calls must be filtered.
|
||||
*/
|
||||
$this->black_list = apply_filters( 'pll_home_url_black_list', $black_list );
|
||||
}
|
||||
|
||||
$traces = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
|
||||
unset( $traces[0], $traces[1] ); // We don't need the last 2 calls: this function + call_user_func_array (or apply_filters on PHP7+)
|
||||
|
||||
foreach ( $traces as $trace ) {
|
||||
// Black list first
|
||||
foreach ( $this->black_list as $v ) {
|
||||
if ( ( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) ) || ( ! empty( $v['function'] ) && $trace['function'] === $v['function'] ) ) {
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $this->white_list as $v ) {
|
||||
if ( ( ! empty( $v['function'] ) && $trace['function'] === $v['function'] ) ||
|
||||
( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) && in_array( $trace['function'], array( 'home_url', 'get_home_url', 'bloginfo', 'get_bloginfo' ) ) ) ) {
|
||||
$ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return empty( $ok ) ? $url : ( empty( $path ) ? rtrim( $this->links->get_home_url( $this->curlang ), '/' ) : $this->links->get_home_url( $this->curlang ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the ajax url when using domains or subdomains.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param string $url The admin url with path evaluated by WordPress.
|
||||
* @param string $path Path relative to the admin URL.
|
||||
* @return string
|
||||
*/
|
||||
public function admin_url( $url, $path ) {
|
||||
return 'admin-ajax.php' === $path ? $this->links_model->switch_language_in_link( $url, $this->curlang ) : $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the current language to URL query.
|
||||
*
|
||||
* @since 3.7.5
|
||||
*
|
||||
* @param string $url The oEmbed endpoint URL.
|
||||
* @return string The oEmbed endpoint URL with the language.
|
||||
*/
|
||||
public function add_current_language_url_query( $url ) {
|
||||
if ( empty( $this->curlang ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return add_query_arg( 'lang', $this->curlang->slug, $url );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filters search forms when using permalinks
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Frontend_Filters_Search {
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
public $links_model;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->links_model = &$polylang->links_model;
|
||||
$this->curlang = &$polylang->curlang;
|
||||
|
||||
// Adds the language information in the search form
|
||||
// Low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form
|
||||
add_filter( 'get_search_form', array( $this, 'get_search_form' ), 99 );
|
||||
|
||||
// Adds the language information in the search block.
|
||||
add_filter( 'render_block_core/search', array( $this, 'get_search_form' ) );
|
||||
|
||||
// Adds the language information in admin bar search form
|
||||
add_action( 'add_admin_bar_menus', array( $this, 'add_admin_bar_menus' ) );
|
||||
|
||||
|
||||
// Adds javascript at the end of the document
|
||||
// Was used for WP < 3.6. kept just in case
|
||||
if ( defined( 'PLL_SEARCH_FORM_JS' ) && PLL_SEARCH_FORM_JS ) {
|
||||
add_action( 'wp_footer', array( $this, 'wp_print_footer_scripts' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language information in the search form.
|
||||
*
|
||||
* Does not work if searchform.php ( prior to WP 3.6 ) is used or if the search form is hardcoded in another template file
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string $form The search form HTML.
|
||||
* @return string Modified search form.
|
||||
*/
|
||||
public function get_search_form( $form ) {
|
||||
if ( empty( $form ) || empty( $this->curlang ) ) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
if ( $this->links_model->using_permalinks ) {
|
||||
// Take care to modify only the url in the <form> tag.
|
||||
preg_match( '#<form.+?>#s', $form, $matches );
|
||||
$old = reset( $matches );
|
||||
if ( empty( $old ) ) {
|
||||
return $form;
|
||||
}
|
||||
// Replace action attribute (a text with no space and no closing tag within double quotes or simple quotes or without quotes).
|
||||
$new = preg_replace( '#\saction=("[^"\r\n]+"|\'[^\'\r\n]+\'|[^\'"][^>\s]+)#', ' action="' . esc_url( $this->curlang->get_search_url() ) . '"', $old );
|
||||
if ( empty( $new ) ) {
|
||||
return $form;
|
||||
}
|
||||
$form = str_replace( $old, $new, $form );
|
||||
} else {
|
||||
$form = str_replace( '</form>', '<input type="hidden" name="lang" value="' . esc_attr( $this->curlang->slug ) . '" /></form>', $form );
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the language information in the admin bar search form.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_admin_bar_menus() {
|
||||
// Backward compatibility with WP < 6.6. The priority was 4 before this version, 9999 since then.
|
||||
$priority = has_action( 'admin_bar_menu', 'wp_admin_bar_search_menu' );
|
||||
if ( ! is_int( $priority ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', $priority );
|
||||
add_action( 'admin_bar_menu', array( $this, 'admin_bar_search_menu' ), $priority );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the admin bar search form to pass our get_search_form filter. See #21342.
|
||||
* Code last checked: WP 5.4.1.
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance, passed by reference.
|
||||
* @return void
|
||||
*/
|
||||
public function admin_bar_search_menu( $wp_admin_bar ) {
|
||||
$form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">';
|
||||
$form .= '<input class="adminbar-input" name="s" id="adminbar-search" type="text" value="" maxlength="150" />';
|
||||
$form .= '<label for="adminbar-search" class="screen-reader-text">' .
|
||||
/* translators: Hidden accessibility text. */
|
||||
esc_html__( 'Search', 'polylang' ) .
|
||||
'</label>';
|
||||
$form .= '<input type="submit" class="adminbar-button" value="' . esc_attr__( 'Search', 'polylang' ) . '" />';
|
||||
$form .= '</form>';
|
||||
|
||||
$wp_admin_bar->add_node(
|
||||
array(
|
||||
'parent' => 'top-secondary',
|
||||
'id' => 'search',
|
||||
'title' => $this->get_search_form( $form ), // Pass the get_search_form filter.
|
||||
'meta' => array(
|
||||
'class' => 'admin-bar-search',
|
||||
'tabindex' => -1,
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows modifying the search form if it does not pass get_search_form.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function wp_print_footer_scripts() {
|
||||
/*
|
||||
* Don't use directly e[0] just in case there is somewhere else an element named 's'
|
||||
* Check before if the hidden input has not already been introduced by get_search_form ( FIXME: is there a way to improve this ) ?
|
||||
* Thanks to AndyDeGroo for improving the code for compatibility with old browsers
|
||||
* http://wordpress.org/support/topic/development-of-polylang-version-08?replies=6#post-2645559
|
||||
*/
|
||||
$lang = esc_js( $this->curlang->slug );
|
||||
$js = "e = document.getElementsByName( 's' );
|
||||
for ( i = 0; i < e.length; i++ ) {
|
||||
if ( e[i].tagName.toUpperCase() == 'INPUT' ) {
|
||||
s = e[i].parentNode.parentNode.children;
|
||||
l = 0;
|
||||
for ( j = 0; j < s.length; j++ ) {
|
||||
if ( s[j].name == 'lang' ) {
|
||||
l = 1;
|
||||
}
|
||||
}
|
||||
if ( l == 0 ) {
|
||||
var ih = document.createElement( 'input' );
|
||||
ih.type = 'hidden';
|
||||
ih.name = 'lang';
|
||||
ih.value = '{$lang}';
|
||||
e[i].parentNode.appendChild( ih );
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
$type_attr = current_theme_supports( 'html5', 'script' ) ? '' : ' type="text/javascript"';
|
||||
|
||||
if ( $type_attr ) {
|
||||
$js = "/* <![CDATA[ */\n{$js}\n/* ]]> */";
|
||||
}
|
||||
|
||||
echo "<script{$type_attr}>\n{$js}\n</script>\n"; // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filters widgets by language on frontend
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
class PLL_Frontend_Filters_Widgets {
|
||||
/**
|
||||
* Internal non persistent cache object.
|
||||
*
|
||||
* @var PLL_Cache<array>
|
||||
*/
|
||||
public $cache;
|
||||
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param object $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
$this->curlang = &$polylang->curlang;
|
||||
$this->cache = new PLL_Cache();
|
||||
|
||||
add_filter( 'sidebars_widgets', array( $this, 'sidebars_widgets' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove widgets from sidebars if they are not visible in the current language
|
||||
* Needed to allow is_active_sidebar() to return false if all widgets are not for the current language. See #54
|
||||
*
|
||||
* @since 2.1
|
||||
* @since 2.4 The result is cached as the function can be very expensive in case there are a lot of widgets
|
||||
*
|
||||
* @param array $sidebars_widgets An associative array of sidebars and their widgets
|
||||
* @return array
|
||||
*/
|
||||
public function sidebars_widgets( $sidebars_widgets ) {
|
||||
global $wp_registered_widgets;
|
||||
|
||||
if ( empty( $wp_registered_widgets ) ) {
|
||||
return $sidebars_widgets;
|
||||
}
|
||||
|
||||
$cache_key = $this->cache->get_unique_key( 'sidebars_widgets_', $sidebars_widgets );
|
||||
$_sidebars_widgets = $this->cache->get( $cache_key );
|
||||
|
||||
if ( false !== $_sidebars_widgets ) {
|
||||
return $_sidebars_widgets;
|
||||
}
|
||||
|
||||
$sidebars_widgets = $this->filter_widgets_sidebars( $sidebars_widgets, $wp_registered_widgets );
|
||||
|
||||
return $this->cache->set( $cache_key, $sidebars_widgets );
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles the removal of widgets in the sidebars depending on their display language.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param array $widget_data An array containing the widget data
|
||||
* @param array $sidebars_widgets An associative array of sidebars and their widgets
|
||||
* @param string $sidebar Sidebar name
|
||||
* @param int $key Widget number
|
||||
* @return array An associative array of sidebars and their widgets
|
||||
*/
|
||||
public function handle_widget_in_sidebar_callback( $widget_data, $sidebars_widgets, $sidebar, $key ) {
|
||||
// Remove the widget if not visible in the current language
|
||||
if ( ! empty( $widget_data['settings'][ $widget_data['number'] ]['pll_lang'] ) && $widget_data['settings'][ $widget_data['number'] ]['pll_lang'] !== $this->curlang->slug ) {
|
||||
unset( $sidebars_widgets[ $sidebar ][ $key ] );
|
||||
}
|
||||
return $sidebars_widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Browse the widgets sidebars and sort the ones that should be displayed or not.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param array $sidebars_widgets An associative array of sidebars and their widgets
|
||||
* @param array $wp_registered_widgets Array of all registered widgets.
|
||||
* @return array An associative array of sidebars and their widgets
|
||||
*/
|
||||
public function filter_widgets_sidebars( $sidebars_widgets, $wp_registered_widgets ) {
|
||||
foreach ( $sidebars_widgets as $sidebar => $widgets ) {
|
||||
if ( 'wp_inactive_widgets' === $sidebar || empty( $widgets ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $widgets as $key => $widget ) {
|
||||
if ( ! $this->is_widget_object( $wp_registered_widgets, $widget ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$widget_data = $this->get_widget_data( $wp_registered_widgets, $widget );
|
||||
|
||||
$sidebars_widgets = $this->handle_widget_in_sidebar_callback( $widget_data, $sidebars_widgets, $sidebar, $key );
|
||||
}
|
||||
}
|
||||
return $sidebars_widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the widget is an object.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param array $wp_registered_widgets Array of all registered widgets.
|
||||
* @param string $widget String that identifies the widget.
|
||||
* @return bool True if object, false otherwise.
|
||||
*/
|
||||
protected function is_widget_object( $wp_registered_widgets, $widget ) {
|
||||
// Nothing can be done if the widget is created using pre WP2.8 API :(
|
||||
// There is no object, so we can't access it to get the widget options
|
||||
return isset( $wp_registered_widgets[ $widget ]['callback'] ) &&
|
||||
is_array( $wp_registered_widgets[ $widget ]['callback'] ) &&
|
||||
isset( $wp_registered_widgets[ $widget ]['callback'][0] ) &&
|
||||
is_object( $wp_registered_widgets[ $widget ]['callback'][0] ) &&
|
||||
method_exists( $wp_registered_widgets[ $widget ]['callback'][0], 'get_settings' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get widgets settings and number.
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param array $wp_registered_widgets Array of all registered widgets.
|
||||
* @param string $widget String that identifies the widget.
|
||||
* @return array An array containing the widget settings and number.
|
||||
*/
|
||||
protected function get_widget_data( $wp_registered_widgets, $widget ) {
|
||||
$widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->get_settings();
|
||||
$number = $wp_registered_widgets[ $widget ]['params'][0]['number'];
|
||||
|
||||
return array(
|
||||
'settings' => $widget_settings,
|
||||
'number' => $number,
|
||||
);
|
||||
}
|
||||
}
|
||||
216
wp-content/plugins/polylang/src/frontend/frontend-filters.php
Normal file
216
wp-content/plugins/polylang/src/frontend/frontend-filters.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filters content by language on frontend
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Frontend_Filters extends PLL_Filters {
|
||||
/**
|
||||
* Constructor: setups filters and actions
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Frontend $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
// Filters the WordPress locale
|
||||
add_filter( 'locale', array( $this, 'get_locale' ) );
|
||||
|
||||
// Filter sticky posts by current language
|
||||
add_filter( 'option_sticky_posts', array( $this, 'option_sticky_posts' ) );
|
||||
|
||||
// Rewrites archives links to filter them by language
|
||||
add_filter( 'getarchives_join', array( $this, 'getarchives_join' ), 10, 2 );
|
||||
add_filter( 'getarchives_where', array( $this, 'getarchives_where' ), 10, 2 );
|
||||
|
||||
// Filters the widgets according to the current language
|
||||
add_filter( 'widget_display_callback', array( $this, 'widget_display_callback' ) );
|
||||
|
||||
if ( $this->options['media_support'] ) {
|
||||
add_filter( 'widget_media_image_instance', array( $this, 'widget_media_instance' ), 1 ); // Since WP 4.8
|
||||
}
|
||||
|
||||
// Strings translation ( must be applied before WordPress applies its default formatting filters )
|
||||
foreach ( array( 'widget_text', 'widget_title' ) as $filter ) {
|
||||
add_filter( $filter, 'pll__', 1 );
|
||||
}
|
||||
|
||||
// Translates biography
|
||||
add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 );
|
||||
|
||||
if ( Polylang::is_ajax_on_front() ) {
|
||||
add_filter( 'load_textdomain_mofile', array( $this, 'load_textdomain_mofile' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale based on current language
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_locale() {
|
||||
return $this->curlang->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters sticky posts by current language.
|
||||
*
|
||||
* @since 0.8
|
||||
*
|
||||
* @param int[] $posts List of sticky posts ids.
|
||||
* @return int[] Modified list of sticky posts ids.
|
||||
*/
|
||||
public function option_sticky_posts( $posts ) {
|
||||
global $wpdb;
|
||||
|
||||
// Do not filter sticky posts on REST requests as $this->curlang is *not* the 'lang' parameter set in the request.
|
||||
if ( defined( 'REST_REQUEST' ) || empty( $this->curlang ) || empty( $posts ) ) {
|
||||
return $posts;
|
||||
}
|
||||
|
||||
$_posts = wp_cache_get( 'sticky_posts', 'options' ); // This option is usually cached in 'all_options' by WP.
|
||||
$tt_id = $this->curlang->get_tax_prop( 'language', 'term_taxonomy_id' );
|
||||
|
||||
if ( ! empty( $_posts ) && is_array( $_posts ) && ! empty( $_posts[ $tt_id ] ) && is_array( $_posts[ $tt_id ] ) ) {
|
||||
return $_posts[ $tt_id ];
|
||||
}
|
||||
|
||||
$languages = array();
|
||||
foreach ( $this->model->get_languages_list() as $language ) {
|
||||
$languages[] = $language->get_tax_prop( 'language', 'term_taxonomy_id' );
|
||||
}
|
||||
|
||||
$relations = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
sprintf(
|
||||
"SELECT object_id, term_taxonomy_id FROM {$wpdb->term_relationships} WHERE object_id IN (%s) AND term_taxonomy_id IN (%s)",
|
||||
implode( ',', array_fill( 0, count( $posts ), '%d' ) ),
|
||||
implode( ',', array_fill( 0, count( $languages ), '%d' ) )
|
||||
),
|
||||
array_merge( $posts, $languages )
|
||||
)
|
||||
);
|
||||
|
||||
$_posts = array_fill_keys( $languages, array() ); // Init with empty arrays.
|
||||
|
||||
foreach ( $relations as $relation ) {
|
||||
$_posts[ $relation->term_taxonomy_id ][] = (int) $relation->object_id;
|
||||
}
|
||||
|
||||
wp_cache_add( 'sticky_posts', $_posts, 'options' );
|
||||
return $_posts[ $tt_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the sql request for wp_get_archives to filter by the current language
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $sql JOIN clause
|
||||
* @param array $r wp_get_archives arguments
|
||||
* @return string modified JOIN clause
|
||||
*/
|
||||
public function getarchives_join( $sql, $r ) {
|
||||
return ! empty( $r['post_type'] ) && $this->model->is_translated_post_type( $r['post_type'] ) ? $sql . $this->model->post->join_clause() : $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the sql request for wp_get_archives to filter by the current language
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $sql WHERE clause
|
||||
* @param array $r wp_get_archives arguments
|
||||
* @return string modified WHERE clause
|
||||
*/
|
||||
public function getarchives_where( $sql, $r ) {
|
||||
if ( ! $this->curlang instanceof PLL_Language ) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
if ( empty( $r['post_type'] ) || ! $this->model->is_translated_post_type( $r['post_type'] ) ) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
return $sql . $this->model->post->where_clause( $this->curlang );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the widgets according to the current language
|
||||
* Don't display if a language filter is set and this is not the current one
|
||||
* Needed for {@see https://developer.wordpress.org/reference/functions/the_widget/ the_widget()}.
|
||||
*
|
||||
* @since 0.3
|
||||
*
|
||||
* @param array $instance Widget settings
|
||||
* @return bool|array false if we hide the widget, unmodified $instance otherwise
|
||||
*/
|
||||
public function widget_display_callback( $instance ) {
|
||||
return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates media in media widgets
|
||||
*
|
||||
* @since 2.1.5
|
||||
*
|
||||
* @param array $instance Widget instance data
|
||||
* @return array
|
||||
*/
|
||||
public function widget_media_instance( $instance ) {
|
||||
if ( empty( $instance['pll_lang'] ) && $instance['attachment_id'] && $tr_id = pll_get_post( $instance['attachment_id'] ) ) {
|
||||
$instance['attachment_id'] = $tr_id;
|
||||
$attachment = get_post( $tr_id );
|
||||
|
||||
if ( $instance['caption'] && ! empty( $attachment->post_excerpt ) ) {
|
||||
$instance['caption'] = $attachment->post_excerpt;
|
||||
}
|
||||
|
||||
if ( $instance['alt'] && $alt_text = get_post_meta( $tr_id, '_wp_attachment_image_alt', true ) ) {
|
||||
$instance['alt'] = $alt_text;
|
||||
}
|
||||
|
||||
if ( $instance['image_title'] && ! empty( $attachment->post_title ) ) {
|
||||
$instance['image_title'] = $attachment->post_title;
|
||||
}
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the biography.
|
||||
*
|
||||
* @since 0.9
|
||||
*
|
||||
* @param null $null Expecting the default null value.
|
||||
* @param int $id The user id.
|
||||
* @param string $meta_key The metadata key.
|
||||
* @param bool $single Whether to return only the first value of the specified $meta_key.
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_user_metadata( $null, $id, $meta_key, $single ) {
|
||||
return 'description' === $meta_key && ! empty( $this->curlang ) && ! $this->curlang->is_default ? get_user_meta( $id, 'description_' . $this->curlang->slug, $single ) : $null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the translation files to load when doing ajax on front
|
||||
* This is needed because WP the language files associated to the user locale when a user is logged in
|
||||
*
|
||||
* @since 2.2.6
|
||||
*
|
||||
* @param string $mofile Translation file name
|
||||
* @return string
|
||||
*/
|
||||
public function load_textdomain_mofile( $mofile ) {
|
||||
$user_locale = get_user_locale();
|
||||
return str_replace( "{$user_locale}.mo", "{$this->curlang->locale}.mo", $mofile );
|
||||
}
|
||||
}
|
||||
228
wp-content/plugins/polylang/src/frontend/frontend-links.php
Normal file
228
wp-content/plugins/polylang/src/frontend/frontend-links.php
Normal file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages links filters and url of translations on frontend
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Frontend_Links extends PLL_Links {
|
||||
|
||||
/**
|
||||
* Internal non persistent cache object.
|
||||
*
|
||||
* @var PLL_Cache<string>
|
||||
*/
|
||||
public $cache;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Frontend $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
$this->curlang = &$polylang->curlang;
|
||||
$this->cache = new PLL_Cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url of the translation (if it exists) of the current page.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param PLL_Language $language Language object.
|
||||
* @return string
|
||||
*/
|
||||
public function get_translation_url( $language ) {
|
||||
global $wp_query;
|
||||
|
||||
if ( false !== $translation_url = $this->cache->get( 'translation_url:' . $language->slug ) ) {
|
||||
return $translation_url;
|
||||
}
|
||||
|
||||
// Make sure that we have the queried object
|
||||
// See https://wordpress.org/support/topic/patch-for-fixing-a-notice
|
||||
$queried_object_id = $wp_query->get_queried_object_id();
|
||||
|
||||
/**
|
||||
* Filters the translation url before Polylang attempts to find one.
|
||||
* Internally used by Polylang for the static front page and posts page.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string $url Empty string or the url of the translation of the current page.
|
||||
* @param PLL_Language $language Language of the translation.
|
||||
* @param int $queried_object_id Queried object ID.
|
||||
*/
|
||||
if ( ! $url = apply_filters( 'pll_pre_translation_url', '', $language, $queried_object_id ) ) {
|
||||
$qv = $wp_query->query_vars;
|
||||
|
||||
// Post and attachment
|
||||
if ( is_single() && ( $this->options['media_support'] || ! is_attachment() ) && ( $id = $this->model->post->get( $queried_object_id, $language ) ) && $this->model->post->current_user_can_read( $id ) ) {
|
||||
$url = get_permalink( $id );
|
||||
}
|
||||
|
||||
// Page
|
||||
elseif ( is_page() && ( $id = $this->model->post->get( $queried_object_id, $language ) ) && $this->model->post->current_user_can_read( $id ) ) {
|
||||
$url = get_page_link( $id );
|
||||
}
|
||||
|
||||
elseif ( is_search() ) {
|
||||
$url = $this->get_archive_url( $language );
|
||||
|
||||
// Special case for search filtered by translated taxonomies: taxonomy terms are translated in the translation url
|
||||
if ( ! empty( $wp_query->tax_query->queries ) ) {
|
||||
foreach ( $wp_query->tax_query->queries as $tax_query ) {
|
||||
if ( ! empty( $tax_query['taxonomy'] ) && $this->model->is_translated_taxonomy( $tax_query['taxonomy'] ) ) {
|
||||
|
||||
$tax = get_taxonomy( $tax_query['taxonomy'] );
|
||||
$terms = get_terms( array( 'taxonomy' => $tax->name, 'fields' => 'id=>slug' ) ); // Filtered by current language
|
||||
|
||||
foreach ( $tax_query['terms'] as $slug ) {
|
||||
$term_id = array_search( $slug, $terms ); // What is the term_id corresponding to taxonomy term?
|
||||
if ( $term_id && $term_id = $this->model->term->get_translation( $term_id, $language ) ) { // Get the translated term_id
|
||||
$term = get_term( $term_id, $tax->name );
|
||||
|
||||
if ( ! $term instanceof WP_Term ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$url = str_replace( $slug, $term->slug, $url );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translated taxonomy
|
||||
// Take care that is_tax() is false for categories and tags
|
||||
elseif ( ( is_category() || is_tag() || is_tax() ) && ( $term = get_queried_object() ) && $this->model->is_translated_taxonomy( $term->taxonomy ) ) {
|
||||
$lang = $this->model->term->get_language( $term->term_id );
|
||||
|
||||
if ( ! $lang || $language->slug == $lang->slug ) {
|
||||
$url = get_term_link( $term, $term->taxonomy ); // Self link
|
||||
}
|
||||
|
||||
elseif ( $tr_id = $this->model->term->get_translation( $term->term_id, $language ) ) {
|
||||
$tr_term = get_term( $tr_id, $term->taxonomy );
|
||||
if ( $tr_term instanceof WP_Term ) {
|
||||
// Check if translated term ( or children ) have posts
|
||||
$count = $tr_term->count || ( is_taxonomy_hierarchical( $term->taxonomy ) && array_sum( wp_list_pluck( get_terms( array( 'taxonomy' => $term->taxonomy, 'child_of' => $tr_term->term_id, 'lang' => $language->slug ) ), 'count' ) ) );
|
||||
|
||||
/**
|
||||
* Filter whether to hide an archive translation url
|
||||
*
|
||||
* @since 2.2.4
|
||||
*
|
||||
* @param bool $hide True to hide the translation url.
|
||||
* defaults to true when the translated archive is empty, false otherwise.
|
||||
* @param string $lang The language code of the translation
|
||||
* @param array $args Arguments used to evaluated the number of posts in the archive
|
||||
*/
|
||||
if ( ! apply_filters( 'pll_hide_archive_translation_url', ! $count, $language->slug, array( 'taxonomy' => $term->taxonomy ) ) ) {
|
||||
$url = get_term_link( $tr_term, $term->taxonomy );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post type archive
|
||||
elseif ( is_post_type_archive() ) {
|
||||
if ( $this->model->is_translated_post_type( $qv['post_type'] ) ) {
|
||||
$args = array( 'post_type' => $qv['post_type'] );
|
||||
$count = $this->model->count_posts( $language, $args );
|
||||
|
||||
/** This filter is documented in src/frontend/frontend-links.php */
|
||||
if ( ! apply_filters( 'pll_hide_archive_translation_url', ! $count, $language->slug, $args ) ) {
|
||||
$url = $this->get_archive_url( $language );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Other archives
|
||||
elseif ( is_archive() ) {
|
||||
$keys = array( 'post_type', 'm', 'year', 'monthnum', 'day', 'author', 'author_name' );
|
||||
$keys = array_merge( $keys, $this->model->get_filtered_taxonomies_query_vars() );
|
||||
$args = array_intersect_key( $qv, array_flip( $keys ) );
|
||||
$count = $this->model->count_posts( $language, $args );
|
||||
|
||||
/** This filter is documented in src/frontend/frontend-links.php */
|
||||
if ( ! apply_filters( 'pll_hide_archive_translation_url', ! $count, $language->slug, $args ) ) {
|
||||
$url = $this->get_archive_url( $language );
|
||||
}
|
||||
}
|
||||
|
||||
// Front page when it is the list of posts
|
||||
elseif ( is_front_page() ) {
|
||||
$url = $this->get_home_url( $language );
|
||||
}
|
||||
}
|
||||
|
||||
$url = ! empty( $url ) && ! is_wp_error( $url ) ? $url : null;
|
||||
|
||||
/**
|
||||
* Filter the translation url of the current page before Polylang caches it
|
||||
*
|
||||
* @since 1.1.2
|
||||
*
|
||||
* @param null|string $url The translation url, null if none was found
|
||||
* @param string $language The language code of the translation
|
||||
*/
|
||||
$translation_url = (string) apply_filters( 'pll_translation_url', $url, $language->slug );
|
||||
|
||||
// Don't cache before template_redirect to avoid a conflict with Barrel + WP Bakery Page Builder
|
||||
if ( did_action( 'template_redirect' ) ) {
|
||||
$this->cache->set( 'translation_url:' . $language->slug, $translation_url );
|
||||
}
|
||||
|
||||
return $translation_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translation of the current archive url
|
||||
* used also for search
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Language $language An object representing a language.
|
||||
* @return string
|
||||
*/
|
||||
public function get_archive_url( $language ) {
|
||||
$url = pll_get_requested_url();
|
||||
$url = $this->links_model->switch_language_in_link( $url, $language );
|
||||
$url = $this->links_model->remove_paged_from_link( $url );
|
||||
|
||||
/**
|
||||
* Filter the archive url
|
||||
*
|
||||
* @since 1.6
|
||||
*
|
||||
* @param string $url Url of the archive
|
||||
* @param object $language Language of the archive
|
||||
*/
|
||||
return apply_filters( 'pll_get_archive_url', $url, $language );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the home url in the right language.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param PLL_Language|string $language Optional, defaults to current language.
|
||||
* @param bool $is_search Optional, whether we need the home url for a search form, defaults to false.
|
||||
*/
|
||||
public function get_home_url( $language = '', $is_search = false ) {
|
||||
if ( empty( $language ) ) {
|
||||
$language = $this->curlang;
|
||||
}
|
||||
|
||||
return parent::get_home_url( $language, $is_search );
|
||||
}
|
||||
}
|
||||
341
wp-content/plugins/polylang/src/frontend/frontend-nav-menu.php
Normal file
341
wp-content/plugins/polylang/src/frontend/frontend-nav-menu.php
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages custom menus translations as well as the language switcher menu item on frontend
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Frontend_Nav_Menu extends PLL_Nav_Menu {
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null|false
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Frontend|PLL_REST_Request $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
$this->curlang = &$polylang->curlang;
|
||||
|
||||
// Split the language switcher menu item in several language menu items
|
||||
add_filter( 'wp_get_nav_menu_items', array( $this, 'wp_get_nav_menu_items' ), 20 ); // after the customizer menus
|
||||
add_filter( 'wp_nav_menu_objects', array( $this, 'wp_nav_menu_objects' ) );
|
||||
add_filter( 'nav_menu_link_attributes', array( $this, 'nav_menu_link_attributes' ), 10, 2 );
|
||||
|
||||
// Filters menus by language
|
||||
add_filter( 'theme_mod_nav_menu_locations', array( $this, 'nav_menu_locations' ), 20 );
|
||||
add_filter( 'wp_nav_menu_args', array( $this, 'wp_nav_menu_args' ) );
|
||||
|
||||
// The customizer
|
||||
if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_before_customizer' ) );
|
||||
add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_after_customizer' ), 2000 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts menu items by menu order.
|
||||
*
|
||||
* @since 1.7.9
|
||||
*
|
||||
* @param stdClass $a The first object to compare.
|
||||
* @param stdClass $b The second object to compare.
|
||||
* @return int -1 or 1 if $a is considered to be respectively less than or greater than $b.
|
||||
*/
|
||||
protected function usort_menu_items( $a, $b ) {
|
||||
return ( $a->menu_order < $b->menu_order ) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a language switcher menu item title based on options
|
||||
*
|
||||
* @since 2.2.6
|
||||
*
|
||||
* @param string $flag Formatted flag
|
||||
* @param string $name Language name
|
||||
* @param array $options Language switcher options
|
||||
* @return string Formatted menu item title
|
||||
*/
|
||||
protected function get_item_title( $flag, $name, $options ) {
|
||||
if ( $options['show_flags'] ) {
|
||||
if ( $options['show_names'] ) {
|
||||
$title = sprintf( '%1$s<span style="margin-%2$s:0.3em;">%3$s</span>', $flag, is_rtl() ? 'right' : 'left', esc_html( $name ) );
|
||||
} else {
|
||||
$title = $flag;
|
||||
}
|
||||
} else {
|
||||
$title = esc_html( $name );
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the one language switcher menu item of backend in several menu items on frontend.
|
||||
* Takes care to menu_order as it is used later in wp_nav_menu().
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @param stdClass[] $items Menu items.
|
||||
* @return stdClass[] Modified menu items.
|
||||
*/
|
||||
public function wp_get_nav_menu_items( $items ) {
|
||||
if ( empty( $this->curlang ) ) {
|
||||
return $items;
|
||||
}
|
||||
|
||||
if ( doing_action( 'customize_register' ) ) { // needed since WP 4.3, doing_action available since WP 3.9
|
||||
return $items;
|
||||
}
|
||||
|
||||
// The customizer menus does not sort the items and we need them to be sorted before splitting the language switcher
|
||||
usort( $items, array( $this, 'usort_menu_items' ) );
|
||||
|
||||
$new_items = array();
|
||||
|
||||
$offset = 0;
|
||||
|
||||
foreach ( $items as $item ) {
|
||||
if ( $options = get_post_meta( $item->ID, '_pll_menu_item', true ) ) {
|
||||
/** This filter is documented in src/switcher.php */
|
||||
$options = apply_filters( 'pll_the_languages_args', $options ); // Honor the filter here for 'show_flags', 'show_names' and 'dropdown'.
|
||||
|
||||
$switcher = new PLL_Switcher();
|
||||
$args = array_merge( array( 'raw' => 1 ), $options );
|
||||
|
||||
/** @var array */
|
||||
$the_languages = $switcher->the_languages( PLL()->links, $args );
|
||||
|
||||
// parent item for dropdown
|
||||
if ( ! empty( $options['dropdown'] ) ) {
|
||||
$name = isset( $options['display_names_as'] ) && 'slug' === $options['display_names_as'] ? $this->curlang->slug : $this->curlang->name;
|
||||
$item->title = $this->get_item_title( $this->curlang->get_display_flag( empty( $options['show_names'] ) ? 'alt' : 'no-alt' ), $name, $options );
|
||||
$item->attr_title = '';
|
||||
$item->classes = array( 'pll-parent-menu-item' );
|
||||
$item->menu_order += $offset;
|
||||
$new_items[] = $item;
|
||||
++$offset;
|
||||
}
|
||||
|
||||
$i = 0; // for incrementation of menu order only in case of dropdown
|
||||
foreach ( $the_languages as $lang ) {
|
||||
++$i;
|
||||
$lang_item = clone $item;
|
||||
$lang_item->ID = $lang_item->ID . '-' . $lang['slug']; // A unique ID
|
||||
$lang_item->title = $this->get_item_title( $lang['flag'], $lang['name'], $options );
|
||||
$lang_item->attr_title = '';
|
||||
$lang_item->url = $lang['url'];
|
||||
$lang_item->lang = $lang['locale']; // Save this for use in nav_menu_link_attributes
|
||||
$lang_item->classes = $lang['classes'];
|
||||
if ( ! empty( $options['dropdown'] ) ) {
|
||||
$lang_item->menu_order = $item->menu_order + $i;
|
||||
$lang_item->menu_item_parent = $item->db_id;
|
||||
$lang_item->db_id = 0; // to avoid recursion
|
||||
} else {
|
||||
$lang_item->menu_order += $offset;
|
||||
}
|
||||
$new_items[] = $lang_item;
|
||||
++$offset;
|
||||
}
|
||||
--$offset;
|
||||
} else {
|
||||
$item->menu_order += $offset;
|
||||
$new_items[] = $item;
|
||||
}
|
||||
}
|
||||
return $new_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ancestors of a menu item.
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @param stdClass $item Menu item.
|
||||
* @return int[] Ancestors ids.
|
||||
*/
|
||||
public function get_ancestors( $item ) {
|
||||
$ids = array();
|
||||
$ancestor_id = (int) $item->db_id;
|
||||
while ( ( $ancestor_id = get_post_meta( $ancestor_id, '_menu_item_menu_item_parent', true ) ) && ! in_array( $ancestor_id, $ids ) ) {
|
||||
$ids[] = $ancestor_id;
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes current-menu and current-menu-ancestor classes to lang switcher when not on the home page.
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @param stdClass[] $items An array of menu items.
|
||||
* @return stdClass[]
|
||||
*/
|
||||
public function wp_nav_menu_objects( $items ) {
|
||||
$k_ids = array();
|
||||
$r_ids = array();
|
||||
|
||||
foreach ( $items as $item ) {
|
||||
if ( ! empty( $item->classes ) && is_array( $item->classes ) ) {
|
||||
if ( in_array( 'current-lang', $item->classes ) ) {
|
||||
$item->current = false;
|
||||
$item->classes = array_diff( $item->classes, array( 'current-menu-item' ) );
|
||||
$r_ids = array_merge( $r_ids, $this->get_ancestors( $item ) ); // Remove the classes for these ancestors.
|
||||
} elseif ( in_array( 'current-menu-item', $item->classes ) ) {
|
||||
$k_ids = array_merge( $k_ids, $this->get_ancestors( $item ) ); // Keep the classes for these ancestors.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$r_ids = array_diff( $r_ids, $k_ids );
|
||||
|
||||
foreach ( $items as $item ) {
|
||||
if ( ! empty( $item->db_id ) && in_array( $item->db_id, $r_ids ) ) {
|
||||
$item->classes = array_diff( $item->classes, array( 'current-menu-ancestor', 'current-menu-parent', 'current_page_parent', 'current_page_ancestor' ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds hreflang attribute for the language switcher menu items.
|
||||
* available since WP 3.6.
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @param string[] $atts HTML attributes applied to the menu item's `<a>` element.
|
||||
* @param stdClass $item Menu item.
|
||||
* @return string[] Modified attributes.
|
||||
*/
|
||||
public function nav_menu_link_attributes( $atts, $item ) {
|
||||
if ( isset( $item->lang ) ) {
|
||||
$atts['lang'] = esc_attr( $item->lang );
|
||||
$atts['hreflang'] = $atts['lang'];
|
||||
}
|
||||
return $atts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the theme nav menus locations with the right menu in the right language
|
||||
* Needs to wait for the language to be defined
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param array|bool $menus list of nav menus locations, false if menu locations have not been filled yet
|
||||
* @return array|bool modified list of nav menus locations
|
||||
*/
|
||||
public function nav_menu_locations( $menus ) {
|
||||
if ( is_array( $menus ) && ! empty( $this->curlang ) ) {
|
||||
// First get multilingual menu locations from DB
|
||||
$theme = get_option( 'stylesheet' );
|
||||
|
||||
foreach ( array_keys( $menus ) as $loc ) {
|
||||
$menus[ $loc ] = empty( $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ] ) ? 0 : $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ];
|
||||
}
|
||||
|
||||
// Support for theme customizer
|
||||
if ( is_customize_preview() ) {
|
||||
global $wp_customize;
|
||||
foreach ( $wp_customize->unsanitized_post_values() as $key => $value ) {
|
||||
if ( false !== strpos( $key, 'nav_menu_locations[' ) ) {
|
||||
$loc = substr( trim( $key, ']' ), 19 );
|
||||
$infos = $this->explode_location( $loc );
|
||||
if ( $infos['lang'] === $this->curlang->slug ) {
|
||||
$menus[ $infos['location'] ] = (int) $value;
|
||||
} elseif ( $this->curlang->is_default ) {
|
||||
$menus[ $loc ] = (int) $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to translate the nav menu when it is hardcoded or when no location is defined in wp_nav_menu().
|
||||
*
|
||||
* @since 1.7.10
|
||||
*
|
||||
* @param array $args Array of `wp_nav_menu()` arguments.
|
||||
* @return array
|
||||
*/
|
||||
public function wp_nav_menu_args( $args ) {
|
||||
$theme = get_option( 'stylesheet' );
|
||||
|
||||
if ( empty( $this->curlang ) || empty( $this->options['nav_menus'][ $theme ] ) ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
// Get the nav menu based on the requested menu
|
||||
$menu = wp_get_nav_menu_object( $args['menu'] );
|
||||
|
||||
// Attempt to find a translation of this menu
|
||||
// This obviously does not work if the nav menu has no associated theme location
|
||||
if ( $menu ) {
|
||||
foreach ( $this->options['nav_menus'][ $theme ] as $menus ) {
|
||||
if ( in_array( $menu->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) {
|
||||
$args['menu'] = $menus[ $this->curlang->slug ];
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the first menu that has items and and is in the current language if we still can't find a menu
|
||||
if ( ! $menu && ! $args['theme_location'] ) {
|
||||
$menus = wp_get_nav_menus();
|
||||
foreach ( $menus as $menu_maybe ) {
|
||||
if ( wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) ) ) {
|
||||
foreach ( $this->options['nav_menus'][ $theme ] as $menus ) {
|
||||
if ( in_array( $menu_maybe->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) {
|
||||
$args['menu'] = $menus[ $this->curlang->slug ];
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the nav menu location before the customizer so that it matches the temporary location in the customizer
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param array $args wp_nav_menu $args
|
||||
* @return array modified $args
|
||||
*/
|
||||
public function filter_args_before_customizer( $args ) {
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
$args['theme_location'] = $this->combine_location( $args['theme_location'], $this->curlang );
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the nav menu location after the customizer to get back the true nav menu location for the theme
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param array $args wp_nav_menu $args
|
||||
* @return array modified $args
|
||||
*/
|
||||
public function filter_args_after_customizer( $args ) {
|
||||
$infos = $this->explode_location( $args['theme_location'] );
|
||||
$args['theme_location'] = $infos['location'];
|
||||
return $args;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages the static front page and the page for posts on frontend
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
class PLL_Frontend_Static_Pages extends PLL_Static_Pages {
|
||||
/**
|
||||
* Instance of a child class of PLL_Links_Model.
|
||||
*
|
||||
* @var PLL_Links_Model
|
||||
*/
|
||||
protected $links_model;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Links|null
|
||||
*/
|
||||
protected $links;
|
||||
|
||||
/**
|
||||
* Stores plugin's options.
|
||||
*
|
||||
* @var \WP_Syntex\Polylang\Options\Options
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Constructor: setups filters and actions.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param PLL_Frontend $polylang The Polylang object.
|
||||
*/
|
||||
public function __construct( PLL_Frontend &$polylang ) {
|
||||
parent::__construct( $polylang );
|
||||
|
||||
$this->links_model = &$polylang->links_model;
|
||||
$this->links = &$polylang->links;
|
||||
$this->options = $polylang->options;
|
||||
|
||||
add_action( 'pll_home_requested', array( $this, 'pll_home_requested' ) );
|
||||
|
||||
// Manages the redirection of the homepage.
|
||||
add_filter( 'redirect_canonical', array( $this, 'redirect_canonical' ) );
|
||||
|
||||
add_filter( 'pll_pre_translation_url', array( $this, 'pll_pre_translation_url' ), 10, 3 );
|
||||
add_filter( 'pll_check_canonical_url', array( $this, 'pll_check_canonical_url' ) );
|
||||
|
||||
add_filter( 'pll_set_language_from_query', array( $this, 'page_on_front_query' ), 10, 2 );
|
||||
add_filter( 'pll_set_language_from_query', array( $this, 'page_for_posts_query' ), 10, 2 );
|
||||
|
||||
// Specific cases for the customizer.
|
||||
add_action( 'customize_register', array( $this, 'filter_customizer' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the page_id query var when the site root page is requested
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function pll_home_requested() {
|
||||
set_query_var( 'page_id', $this->curlang->page_on_front );
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the canonical redirect of the homepage when using a page on front.
|
||||
*
|
||||
* @since 0.1
|
||||
*
|
||||
* @param string $redirect_url The redirect url.
|
||||
* @return string|false The modified url, false if the redirect is canceled.
|
||||
*/
|
||||
public function redirect_canonical( $redirect_url ) {
|
||||
if ( is_page() && ! is_feed() && get_queried_object_id() == $this->curlang->page_on_front ) {
|
||||
$url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), get_query_var( 'page' ) ) : $this->links->get_home_url();
|
||||
|
||||
// Don't forget additional query vars
|
||||
$query = wp_parse_url( $redirect_url, PHP_URL_QUERY );
|
||||
if ( ! empty( $query ) ) {
|
||||
parse_str( $query, $query_vars );
|
||||
$query_vars = rawurlencode_deep( $query_vars ); // WP encodes query vars values
|
||||
$url = add_query_arg( $query_vars, $url );
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
return $redirect_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the url of the page on front and page for posts.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string $url Empty string or the url of the translation of the current page.
|
||||
* @param PLL_Language $language Language of the translation.
|
||||
* @param int $queried_object_id Queried object ID.
|
||||
* @return string The translation url.
|
||||
*/
|
||||
public function pll_pre_translation_url( $url, $language, $queried_object_id ) {
|
||||
if ( empty( $queried_object_id ) ) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
// Page for posts.
|
||||
if ( $GLOBALS['wp_query']->is_posts_page ) {
|
||||
$id = $this->model->post->get( $queried_object_id, $language );
|
||||
|
||||
if ( ! empty( $id ) ) {
|
||||
return (string) get_permalink( $id );
|
||||
}
|
||||
}
|
||||
|
||||
// Page on front.
|
||||
if ( is_front_page() && ! empty( $language->page_on_front ) ) {
|
||||
$id = $this->model->post->get( $queried_object_id, $language );
|
||||
|
||||
if ( $language->page_on_front === $id ) {
|
||||
return $language->get_home_url();
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the canonical redirect if we are on a static front page.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param string $redirect_url The redirect url.
|
||||
* @return string|false
|
||||
*/
|
||||
public function pll_check_canonical_url( $redirect_url ) {
|
||||
return $this->options['redirect_lang'] && ! $this->options['force_lang'] && ! empty( $this->curlang->page_on_front ) && is_page( $this->curlang->page_on_front ) ? false : $redirect_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the query for a the static front page (redirected from the language page)?
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @param WP_Query $query The WP_Query object.
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_front_page( $query ) {
|
||||
$query = array_diff( array_keys( $query->query ), array( 'preview', 'page', 'paged', 'cpage', 'orderby' ) );
|
||||
return 1 === count( $query ) && in_array( 'lang', $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups query vars when requesting a static front page
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param PLL_Language|false $lang The current language, false if it is not set yet.
|
||||
* @param WP_Query $query The main WP query.
|
||||
* @return PLL_Language|false
|
||||
*/
|
||||
public function page_on_front_query( $lang, $query ) {
|
||||
if ( ! empty( $lang ) || ! $this->page_on_front ) {
|
||||
return $lang;
|
||||
}
|
||||
|
||||
// Redirect the language page to the homepage when using a static front page
|
||||
if ( ( $this->options['redirect_lang'] || $this->options['hide_default'] ) && $this->is_front_page( $query ) && $lang = $this->model->get_language( get_query_var( 'lang' ) ) ) {
|
||||
$query->is_archive = false;
|
||||
$query->is_tax = false;
|
||||
if ( 'page' === get_option( 'show_on_front' ) && ! empty( $lang->page_on_front ) ) {
|
||||
$query->set( 'page_id', $lang->page_on_front );
|
||||
$query->is_singular = true;
|
||||
$query->is_page = true;
|
||||
unset( $query->query_vars['lang'], $query->queried_object ); // Reset queried object
|
||||
} else {
|
||||
// Handle case where the static front page hasn't be translated to avoid a possible infinite redirect loop.
|
||||
$query->is_home = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix paged static front page in plain permalinks when Settings > Reading doesn't match the default language
|
||||
elseif ( ! $this->links_model->using_permalinks && count( $query->query ) === 1 && ! empty( $query->query['page'] ) ) {
|
||||
$lang = $this->model->get_default_language();
|
||||
if ( empty( $lang ) ) {
|
||||
return $lang;
|
||||
}
|
||||
$query->set( 'page_id', $lang->page_on_front );
|
||||
$query->is_singular = true;
|
||||
$query->is_page = true;
|
||||
$query->is_archive = false;
|
||||
$query->is_tax = false;
|
||||
unset( $query->query_vars['lang'], $query->queried_object ); // Reset queried object
|
||||
}
|
||||
|
||||
// Set the language when requesting a static front page
|
||||
else {
|
||||
$page_id = $this->get_page_id( $query );
|
||||
$languages = $this->model->get_languages_list();
|
||||
$pages = wp_list_pluck( $languages, 'page_on_front' );
|
||||
|
||||
if ( ! empty( $page_id ) && false !== $n = array_search( $page_id, $pages ) ) {
|
||||
$lang = $languages[ $n ];
|
||||
}
|
||||
}
|
||||
|
||||
// Fix <!--nextpage--> for page_on_front
|
||||
if ( ( $this->options['force_lang'] < 2 || ! $this->options['redirect_lang'] ) && $this->links_model->using_permalinks && ! empty( $lang ) && isset( $query->query['paged'] ) ) {
|
||||
$query->set( 'page', $query->query['paged'] );
|
||||
unset( $query->query['paged'] );
|
||||
} elseif ( ! $this->links_model->using_permalinks && ! empty( $query->query['page'] ) ) {
|
||||
$query->is_paged = true;
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups query vars when requesting a posts page
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param PLL_Language|false $lang The current language, false if it is not set yet.
|
||||
* @param WP_Query $query The main WP query.
|
||||
* @return PLL_Language|false
|
||||
*/
|
||||
public function page_for_posts_query( $lang, $query ) {
|
||||
if ( ! empty( $lang ) || ! $this->page_for_posts ) {
|
||||
return $lang;
|
||||
}
|
||||
|
||||
$page_id = $this->get_page_id( $query );
|
||||
|
||||
if ( empty( $page_id ) ) {
|
||||
return $lang;
|
||||
}
|
||||
|
||||
$pages = $this->model->get_languages_list( array( 'fields' => 'page_for_posts' ) );
|
||||
$pages = array_filter( $pages );
|
||||
|
||||
if ( in_array( $page_id, $pages ) ) {
|
||||
_prime_post_caches( $pages ); // Fill the cache with all pages for posts to avoid one query per page later.
|
||||
|
||||
$lang = $this->model->post->get_language( $page_id );
|
||||
$query->is_singular = false;
|
||||
$query->is_page = false;
|
||||
$query->is_home = true;
|
||||
$query->is_posts_page = true;
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the queried page_id (if it exists ).
|
||||
*
|
||||
* If permalinks are used, WordPress does set and use `$query->queried_object_id` and sets `$query->query_vars['page_id']` to 0,
|
||||
* and does set and use `$query->query_vars['page_id']` if permalinks are not used :(.
|
||||
*
|
||||
* @since 1.5
|
||||
*
|
||||
* @param WP_Query $query Instance of WP_Query.
|
||||
* @return int The page_id.
|
||||
*/
|
||||
protected function get_page_id( $query ) {
|
||||
if ( ! empty( $query->query_vars['pagename'] ) && isset( $query->queried_object_id ) ) {
|
||||
return $query->queried_object_id;
|
||||
}
|
||||
|
||||
return $query->query_vars['page_id'] ?? 0; // No page queried.
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds support for the theme customizer.
|
||||
*
|
||||
* @since 3.4.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function filter_customizer() {
|
||||
add_filter( 'pre_option_page_on_front', array( $this, 'customize_page' ), 20 ); // After the customizer.
|
||||
add_filter( 'pre_option_page_for_post', array( $this, 'customize_page' ), 20 );
|
||||
|
||||
add_filter( 'pll_pre_translation_url', array( $this, 'customize_translation_url' ), 20, 2 ); // After the generic hook in this class.
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the page ID when customized.
|
||||
*
|
||||
* @since 3.4.2
|
||||
*
|
||||
* @param int|false $pre A page ID if the setting is customized, false otherwise.
|
||||
* @return int|false
|
||||
*/
|
||||
public function customize_page( $pre ) {
|
||||
return is_numeric( $pre ) ? pll_get_post( (int) $pre ) : $pre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the translation URL if the option 'show_on_front' is customized.
|
||||
*
|
||||
* @since 3.4.2
|
||||
*
|
||||
* @param string $url An empty string or the URL of the translation of the current page.
|
||||
* @param PLL_Language $language The language of the translation.
|
||||
* @return string
|
||||
*/
|
||||
public function customize_translation_url( $url, $language ) {
|
||||
if ( 'posts' === get_option( 'show_on_front' ) && is_front_page() ) {
|
||||
// When the page on front displays posts, the home URL is the same as the search URL.
|
||||
return $language->get_search_url();
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
319
wp-content/plugins/polylang/src/frontend/frontend.php
Normal file
319
wp-content/plugins/polylang/src/frontend/frontend.php
Normal file
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Polylang class when on frontend, accessible from @see PLL().
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Frontend extends PLL_Base {
|
||||
/**
|
||||
* Current language.
|
||||
*
|
||||
* @var PLL_Language|null
|
||||
*/
|
||||
public $curlang;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Auto_Translate|null
|
||||
*/
|
||||
public $auto_translate;
|
||||
|
||||
/**
|
||||
* The class selecting the current language.
|
||||
*
|
||||
* @var PLL_Choose_Lang|null
|
||||
*/
|
||||
public $choose_lang;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Filters|null
|
||||
*/
|
||||
public $filters;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Filters_Links|null
|
||||
*/
|
||||
public $filters_links;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Filters_Search|null
|
||||
*/
|
||||
public $filters_search;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Links|null
|
||||
*/
|
||||
public $links;
|
||||
|
||||
/**
|
||||
* @var PLL_Default_Term|null
|
||||
*/
|
||||
public $default_term;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Nav_Menu|null
|
||||
*/
|
||||
public $nav_menu;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Static_Pages|null
|
||||
*/
|
||||
public $static_pages;
|
||||
|
||||
/**
|
||||
* @var PLL_Frontend_Filters_Widgets|null
|
||||
*/
|
||||
public $filters_widgets;
|
||||
|
||||
/**
|
||||
* @var PLL_Canonical
|
||||
*/
|
||||
public $canonical;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param PLL_Links_Model $links_model Reference to the links model.
|
||||
*/
|
||||
public function __construct( &$links_model ) {
|
||||
parent::__construct( $links_model );
|
||||
|
||||
add_action( 'pll_language_defined', array( $this, 'pll_language_defined' ), 1 );
|
||||
|
||||
// Avoids the language being the queried object when querying multiple taxonomies
|
||||
add_action( 'parse_tax_query', array( $this, 'parse_tax_query' ), 1 );
|
||||
|
||||
// Prevent unnecessary queries to the DB.
|
||||
add_action( 'parse_tax_query', array( $this, 'transform_query' ) );
|
||||
|
||||
// Filters posts by language
|
||||
add_action( 'parse_query', array( $this, 'parse_query' ), 6 );
|
||||
|
||||
// Not before 'check_canonical_url'
|
||||
if ( ! defined( 'PLL_AUTO_TRANSLATE' ) || PLL_AUTO_TRANSLATE ) {
|
||||
add_action( 'template_redirect', array( $this, 'auto_translate' ), 7 );
|
||||
}
|
||||
|
||||
add_action( 'admin_bar_menu', array( $this, 'remove_customize_admin_bar' ), 41 ); // After WP_Admin_Bar::add_menus
|
||||
|
||||
/*
|
||||
* Static front page and page for posts.
|
||||
*
|
||||
* Early instantiated to be able to correctly initialize language properties.
|
||||
* Also loaded in customizer preview, directly reading the request as we act before WP.
|
||||
*/
|
||||
if ( 'page' === get_option( 'show_on_front' ) || ( isset( $_REQUEST['wp_customize'] ) && 'on' === $_REQUEST['wp_customize'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
|
||||
$this->static_pages = new PLL_Frontend_Static_Pages( $this );
|
||||
}
|
||||
|
||||
$this->model->set_languages_ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups the language chooser based on options
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
|
||||
$this->links = new PLL_Frontend_Links( $this );
|
||||
|
||||
$this->default_term = new PLL_Default_Term( $this );
|
||||
$this->default_term->add_hooks();
|
||||
|
||||
// Setup the language chooser
|
||||
$c = array( 'Content', 'Url', 'Url', 'Domain' );
|
||||
$class = 'PLL_Choose_Lang_' . $c[ $this->options['force_lang'] ];
|
||||
$this->choose_lang = new $class( $this );
|
||||
$this->choose_lang->init();
|
||||
|
||||
// Need to load nav menu class early to correctly define the locations in the customizer when the language is set from the content
|
||||
$this->nav_menu = new PLL_Frontend_Nav_Menu( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups filters and nav menus once the language has been defined
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function pll_language_defined() {
|
||||
// Filters
|
||||
$this->filters_links = new PLL_Frontend_Filters_Links( $this );
|
||||
$this->filters = new PLL_Frontend_Filters( $this );
|
||||
$this->filters_search = new PLL_Frontend_Filters_Search( $this );
|
||||
$this->filters_widgets = new PLL_Frontend_Filters_Widgets( $this );
|
||||
|
||||
/*
|
||||
* Redirects to canonical url before WordPress redirect_canonical
|
||||
* but after Nextgen Gallery which hacks $_SERVER['REQUEST_URI'] !!!
|
||||
* and restores it in 'template_redirect' with priority 1.
|
||||
*/
|
||||
$this->canonical = new PLL_Canonical( $this );
|
||||
add_action( 'template_redirect', array( $this->canonical, 'check_canonical_url' ), 4 );
|
||||
|
||||
// Auto translate for Ajax
|
||||
if ( ( ! defined( 'PLL_AUTO_TRANSLATE' ) || PLL_AUTO_TRANSLATE ) && wp_doing_ajax() ) {
|
||||
$this->auto_translate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When querying multiple taxonomies, makes sure that the language is not the queried object.
|
||||
*
|
||||
* @since 1.8
|
||||
*
|
||||
* @param WP_Query $query WP_Query object.
|
||||
* @return void
|
||||
*/
|
||||
public function parse_tax_query( $query ) {
|
||||
$pll_query = new PLL_Query( $query, $this->model );
|
||||
$queried_taxonomies = $pll_query->get_queried_taxonomies();
|
||||
|
||||
if ( ! empty( $queried_taxonomies ) && 'language' == reset( $queried_taxonomies ) ) {
|
||||
$query->tax_query->queried_terms['language'] = array_shift( $query->tax_query->queried_terms );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents unnecessary queries to the database by transforming
|
||||
* a language query by `slug` to a language query by `term_taxonomy_id`.
|
||||
*
|
||||
* These extra queries occur when the language slug is displayed in the current's page URL, because WP wants a list
|
||||
* of `term_taxonomy_id`.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @param WP_Query $query WP_Query object.
|
||||
* @return void
|
||||
*/
|
||||
public function transform_query( $query ): void {
|
||||
( new PLL_Query( $query, $this->model ) )->transform_query();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies some query vars to "hide" that the language is a taxonomy and avoid conflicts.
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @param WP_Query $query WP_Query object.
|
||||
* @return void
|
||||
*/
|
||||
public function parse_query( $query ) {
|
||||
$qv = $query->query_vars;
|
||||
$pll_query = new PLL_Query( $query, $this->model );
|
||||
$taxonomies = $pll_query->get_queried_taxonomies();
|
||||
|
||||
// Allow filtering recent posts and secondary queries by the current language
|
||||
if ( ! empty( $this->curlang ) ) {
|
||||
$pll_query->filter_query( $this->curlang );
|
||||
}
|
||||
|
||||
// Modifies query vars when the language is queried
|
||||
if ( ! empty( $qv['lang'] ) || ( ! empty( $taxonomies ) && array( 'language' ) == array_values( $taxonomies ) ) ) {
|
||||
// Do we query a custom taxonomy?
|
||||
$taxonomies = array_diff( $taxonomies, array( 'language', 'category', 'post_tag' ) );
|
||||
|
||||
// Remove pages query when the language is set unless we do a search
|
||||
// Take care not to break the single page, attachment and taxonomies queries!
|
||||
if ( empty( $qv['post_type'] ) && ! $query->is_search && ! $query->is_singular && empty( $taxonomies ) && ! $query->is_category && ! $query->is_tag ) {
|
||||
$query->set( 'post_type', 'post' );
|
||||
}
|
||||
|
||||
// Unset the is_archive flag for language pages to prevent loading the archive template
|
||||
// Keep archive flag for comment feed otherwise the language filter does not work
|
||||
if ( empty( $taxonomies ) && ! $query->is_comment_feed && ! $query->is_post_type_archive && ! $query->is_date && ! $query->is_author && ! $query->is_category && ! $query->is_tag ) {
|
||||
$query->is_archive = false;
|
||||
}
|
||||
|
||||
// Unset the is_tax flag except if another custom tax is queried
|
||||
if ( empty( $taxonomies ) && ( $query->is_category || $query->is_tag || $query->is_author || $query->is_post_type_archive || $query->is_date || $query->is_search || $query->is_feed ) ) {
|
||||
$query->is_tax = false;
|
||||
unset( $query->queried_object ); // FIXME useless?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto translate posts and terms ids
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function auto_translate() {
|
||||
$this->auto_translate = new PLL_Frontend_Auto_Translate( $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets some variables when the blog is switched.
|
||||
* Overrides the parent method.
|
||||
*
|
||||
* @since 1.5.1
|
||||
*
|
||||
* @param int $new_blog_id New blog ID.
|
||||
* @param int $prev_blog_id Previous blog ID.
|
||||
* @return void
|
||||
*/
|
||||
public function switch_blog( $new_blog_id, $prev_blog_id ) {
|
||||
if ( (int) $new_blog_id === (int) $prev_blog_id ) {
|
||||
// Do nothing if same blog.
|
||||
return;
|
||||
}
|
||||
|
||||
parent::switch_blog( $new_blog_id, $prev_blog_id );
|
||||
|
||||
// Need to check that some languages are defined when user is logged in, has several blogs, some without any languages.
|
||||
if ( ! $this->is_active_on_current_site() || ! $this->model->has_languages() || ! did_action( 'pll_language_defined' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
static $restore_curlang;
|
||||
|
||||
if ( empty( $restore_curlang ) ) {
|
||||
$restore_curlang = $this->curlang->slug; // To always remember the current language through blogs.
|
||||
}
|
||||
|
||||
$lang = $this->model->get_language( $restore_curlang );
|
||||
$this->curlang = $lang ?: $this->model->get_default_language();
|
||||
if ( empty( $this->curlang ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset( $this->static_pages ) ) {
|
||||
$this->static_pages->init();
|
||||
}
|
||||
|
||||
// Send the slug instead of the locale here to avoid conflicts with same locales.
|
||||
$this->load_strings_translations( $this->curlang->slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the customize admin bar on front-end when using a block theme.
|
||||
*
|
||||
* WordPress removes the Customizer menu if a block theme is activated and no other plugins interact with it.
|
||||
* As Polylang interacts with the Customizer, we have to delete this menu ourselves in the case of a block theme,
|
||||
* unless another plugin than Polylang interacts with the Customizer.
|
||||
*
|
||||
* @since 3.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove_customize_admin_bar() {
|
||||
if ( ! $this->should_customize_menu_be_removed() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wp_admin_bar;
|
||||
|
||||
remove_action( 'wp_before_admin_bar_render', 'wp_customize_support_script' ); // To avoid the script launch.
|
||||
$wp_admin_bar->remove_menu( 'customize' );
|
||||
}
|
||||
}
|
||||
174
wp-content/plugins/polylang/src/functions.php
Normal file
174
wp-content/plugins/polylang/src/functions.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Define wordpress.com VIP equivalent of uncached functions
|
||||
* WordPress backward compatibility functions
|
||||
* and miscellaneous utility functions
|
||||
*/
|
||||
|
||||
if ( ! function_exists( 'wpcom_vip_get_page_by_path' ) ) {
|
||||
/**
|
||||
* Retrieves a page given its path.
|
||||
*
|
||||
* @since 2.8.3
|
||||
*
|
||||
* @param string $page_path Page path.
|
||||
* @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
|
||||
* a WP_Post object, an associative array, or a numeric array, respectively. Default OBJECT.
|
||||
* @param string|array $post_type Optional. Post type or array of post types. Default 'page'.
|
||||
* @return WP_Post|array|null WP_Post (or array) on success, or null on failure.
|
||||
*/
|
||||
function wpcom_vip_get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
|
||||
return get_page_by_path( $page_path, $output, $post_type ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.get_page_by_path_get_page_by_path
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we should load the cache compatibility
|
||||
*
|
||||
* @since 2.3.8
|
||||
*
|
||||
* @return bool True if the cache compatibility must be loaded
|
||||
*/
|
||||
function pll_is_cache_active() {
|
||||
/**
|
||||
* Filters whether we should load the cache compatibility
|
||||
*
|
||||
* @since 2.3.8
|
||||
*
|
||||
* @bool $is_cache True if a known cache plugin is active
|
||||
* incl. WP Fastest Cache which doesn't use WP_CACHE
|
||||
*/
|
||||
return apply_filters( 'pll_is_cache_active', ( defined( 'WP_CACHE' ) && WP_CACHE ) || defined( 'WPFC_MAIN_PATH' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the the current requested url
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return string Requested url
|
||||
*/
|
||||
function pll_get_requested_url() {
|
||||
if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
|
||||
return set_url_scheme( sanitize_url( wp_unslash( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) );
|
||||
}
|
||||
|
||||
/** @var string */
|
||||
$home_url = get_option( 'home' );
|
||||
|
||||
/*
|
||||
* In WP CLI context, few developers define superglobals in wp-config.php
|
||||
* as proposed in https://make.wordpress.org/cli/handbook/common-issues/#php-notice-undefined-index-on-_server-superglobal
|
||||
* So let's return the unfiltered home url to avoid a bunch of notices.
|
||||
*/
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
return $home_url;
|
||||
}
|
||||
|
||||
/*
|
||||
* When using system CRON instead of WP_CRON, the superglobals are likely undefined.
|
||||
*/
|
||||
if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
|
||||
return $home_url;
|
||||
}
|
||||
|
||||
if ( WP_DEBUG ) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions
|
||||
trigger_error( '$_SERVER[\'HTTP_HOST\'] or $_SERVER[\'REQUEST_URI\'] are required but not set.' );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we should load the block editor plugin or the legacy languages metabox.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*
|
||||
* @return bool True to use the block editor plugin.
|
||||
*/
|
||||
function pll_use_block_editor_plugin() {
|
||||
/**
|
||||
* Filters whether we should load the block editor plugin or the legacy languages metabox.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*
|
||||
* @param bool $use_plugin True when loading the block editor plugin.
|
||||
*/
|
||||
return class_exists( 'WP_Syntex\Polylang_Pro\Editors\Screens\Abstract_Screen' ) && apply_filters( 'pll_use_block_editor_plugin', ! defined( 'PLL_USE_BLOCK_EDITOR_PLUGIN' ) || PLL_USE_BLOCK_EDITOR_PLUGIN );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a plugin is active.
|
||||
*
|
||||
* We define our own function because `is_plugin_active()` is available only in the backend.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param string $plugin_name Plugin basename.
|
||||
* @return bool True if activated, false otherwise.
|
||||
*/
|
||||
function pll_is_plugin_active( string $plugin_name ) {
|
||||
$sitewide_plugins = get_site_option( 'active_sitewide_plugins' );
|
||||
$sitewide_plugins = ! empty( $sitewide_plugins ) && is_array( $sitewide_plugins ) ? array_keys( $sitewide_plugins ) : array();
|
||||
$current_site_plugins = (array) get_option( 'active_plugins', array() );
|
||||
$plugins = array_merge( $sitewide_plugins, $current_site_plugins );
|
||||
|
||||
return in_array( $plugin_name, $plugins );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares and registers notices.
|
||||
*
|
||||
* Wraps `add_settings_error()` to make its use more consistent.
|
||||
*
|
||||
* @since 3.6
|
||||
*
|
||||
* @param WP_Error $error Error object.
|
||||
* @return void
|
||||
*/
|
||||
function pll_add_notice( WP_Error $error ) {
|
||||
if ( ! $error->has_errors() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $error->get_error_codes() as $error_code ) {
|
||||
// Extract the "error" type.
|
||||
$data = $error->get_error_data( $error_code );
|
||||
$type = empty( $data ) || ! is_string( $data ) ? 'error' : $data;
|
||||
|
||||
$message = wp_kses(
|
||||
implode( '<br>', $error->get_error_messages( $error_code ) ),
|
||||
array(
|
||||
'a' => array( 'href' => true ),
|
||||
'br' => array(),
|
||||
'code' => array(),
|
||||
'em' => array(),
|
||||
)
|
||||
);
|
||||
|
||||
add_settings_error( 'polylang', $error_code, $message, $type );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the given REST request is in the edit context.
|
||||
*
|
||||
* @since 3.5
|
||||
*
|
||||
* @param WP_REST_Request $request A REST request.
|
||||
* @return bool
|
||||
*
|
||||
* @phpstan-param WP_REST_Request<array> $request
|
||||
*/
|
||||
function pll_is_edit_rest_request( WP_REST_Request $request ): bool {
|
||||
if ( in_array( $request->get_method(), array( 'PATCH', 'POST', 'PUT' ), true ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'GET' === $request->get_method() && 'edit' === $request->get_param( 'context' );
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*
|
||||
* /!\ THE CONSTANTS `POLYLANG_BASENAME` AND `POLYLANG_VERSION` MUST BE DEFINED.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A generic (de)activation class compatible with multisite.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
abstract class PLL_Abstract_Activable {
|
||||
/**
|
||||
* (De)Activation for all blogs.
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.8 Moved from the class `PLL_Install_Base`.
|
||||
* Visibility changed from `protected`.
|
||||
* Made it `static`.
|
||||
* Removed first parameter `$what`.
|
||||
*
|
||||
* @param bool $networkwide Whether the plugin is (de)activated for all sites in the network or just the current site.
|
||||
* @return void
|
||||
*/
|
||||
public static function do_for_all_blogs( $networkwide ): void {
|
||||
if ( is_multisite() && $networkwide ) {
|
||||
// Network.
|
||||
foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0 ) ) as $blog_id ) {
|
||||
switch_to_blog( $blog_id );
|
||||
static::process();
|
||||
}
|
||||
restore_current_blog();
|
||||
} else {
|
||||
// Single blog.
|
||||
static::process();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin's basename.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plugin_basename(): string {
|
||||
return pll_get_constant( 'POLYLANG_BASENAME', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin's version.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_plugin_version(): string {
|
||||
return pll_get_constant( 'POLYLANG_VERSION', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* The process to run on plugin (de)activation.
|
||||
*
|
||||
* @since 0.5
|
||||
* @since 3.8 Moved from the class `PLL_Install_Base`.
|
||||
* Renamed from `_activate()`/`_deactivate()`.
|
||||
* Made it `static` and `abstract`.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract protected static function process(): void;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*
|
||||
* /!\ THE CONSTANTS `POLYLANG_BASENAME` AND `POLYLANG_VERSION` MUST BE DEFINED.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A generic activation class compatible with multisite.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
abstract class PLL_Abstract_Activate extends PLL_Abstract_Activable {
|
||||
/**
|
||||
* Adds the required hooks.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add_hooks(): void {
|
||||
// Plugin activation.
|
||||
register_activation_hook( static::get_plugin_basename(), array( static::class, 'do_for_all_blogs' ) );
|
||||
|
||||
// Site creation on multisite.
|
||||
add_action( 'wp_initialize_site', array( static::class, 'new_site' ), 50 ); // After WP (prio 10).
|
||||
}
|
||||
|
||||
/**
|
||||
* Site creation on multisite (to set default options).
|
||||
*
|
||||
* @since 2.6.8
|
||||
* @since 3.8 Moved from the class `PLL_Install_Base`.
|
||||
*
|
||||
* @param WP_Site $new_site New site object.
|
||||
* @return void
|
||||
*/
|
||||
public static function new_site( $new_site ): void {
|
||||
switch_to_blog( $new_site->id );
|
||||
static::process();
|
||||
restore_current_blog();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*
|
||||
* /!\ THE CONSTANTS `POLYLANG_BASENAME` AND `POLYLANG_VERSION` MUST BE DEFINED.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A generic deactivation class compatible with multisite.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
abstract class PLL_Abstract_Deactivate extends PLL_Abstract_Activable {
|
||||
/**
|
||||
* Adds the required hooks.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add_hooks(): void {
|
||||
register_deactivation_hook( static::get_plugin_basename(), array( static::class, 'do_for_all_blogs' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects plugin deactivation.
|
||||
*
|
||||
* @since 1.7
|
||||
* @since 3.8 Moved from the class `PLL_Install_Base`.
|
||||
*
|
||||
* @return bool True if the plugin is currently being deactivated.
|
||||
*/
|
||||
public static function is_deactivation(): bool {
|
||||
return isset( $_GET['action'], $_GET['plugin'] ) && 'deactivate' === $_GET['action'] && static::get_plugin_basename() === $_GET['plugin']; // phpcs:ignore WordPress.Security.NonceVerification
|
||||
}
|
||||
}
|
||||
72
wp-content/plugins/polylang/src/install/activate.php
Normal file
72
wp-content/plugins/polylang/src/install/activate.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*
|
||||
* /!\ THE CONSTANTS `POLYLANG_BASENAME` AND `POLYLANG_VERSION` MUST BE DEFINED.
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
use WP_Syntex\Polylang\Options\Registry as Options_Registry;
|
||||
|
||||
/**
|
||||
* Activation class compatible with multisite.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class PLL_Activate extends PLL_Abstract_Activate {
|
||||
/**
|
||||
* Adds the required hooks.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add_hooks(): void {
|
||||
register_activation_hook( static::get_plugin_basename(), array( PLL_Wizard::class, 'start_wizard' ) );
|
||||
|
||||
parent::add_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* The process to run on plugin activation.
|
||||
*
|
||||
* @since 0.5
|
||||
* @since 3.8 Moved from the class `PLL_Install`.
|
||||
* Renamed from `_activate()`.
|
||||
* Made it `static`.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function process(): void {
|
||||
add_action( 'pll_init_options_for_blog', array( Options_Registry::class, 'register' ) );
|
||||
$options = new Options();
|
||||
|
||||
if ( ! empty( $options['version'] ) ) {
|
||||
// Check if we will be able to upgrade.
|
||||
if ( version_compare( $options['version'], static::get_plugin_version(), '<' ) ) {
|
||||
( new PLL_Upgrade( $options ) )->can_activate();
|
||||
}
|
||||
} else {
|
||||
$options['version'] = static::get_plugin_version();
|
||||
}
|
||||
|
||||
$options->save(); // Force save here to prevent any conflicts with another instance of `Options`.
|
||||
|
||||
add_option(
|
||||
'pll_language_from_content_available',
|
||||
0 === $options['force_lang'] ? 'yes' : 'no'
|
||||
);
|
||||
|
||||
// Avoid 1 query on every pages if no wpml strings is registered.
|
||||
add_option( 'polylang_wpml_strings', array() );
|
||||
|
||||
add_option( 'pll_language_taxonomies', array() );
|
||||
|
||||
/*
|
||||
* Don't use flush_rewrite_rules at network activation. See #32471.
|
||||
* Thanks to RavanH for the trick. See https://polylang.wordpress.com/2015/06/10/polylang-1-7-6-and-multisite/.
|
||||
* Rewrite rules are created at next page load.
|
||||
*/
|
||||
delete_option( 'rewrite_rules' );
|
||||
}
|
||||
}
|
||||
27
wp-content/plugins/polylang/src/install/deactivate.php
Normal file
27
wp-content/plugins/polylang/src/install/deactivate.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*
|
||||
* /!\ THE CONSTANTS `POLYLANG_BASENAME` AND `POLYLANG_VERSION` MUST BE DEFINED.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deactivation class compatible with multisite.
|
||||
*
|
||||
* @since 3.8
|
||||
*/
|
||||
class PLL_Deactivate extends PLL_Abstract_Deactivate {
|
||||
/**
|
||||
* The process to run on plugin deactivation.
|
||||
*
|
||||
* @since 0.5
|
||||
* @since 3.8 Moved from the class `PLL_Install`.
|
||||
* Renamed from `_deactivate()`.
|
||||
* Made it `static`.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function process(): void {
|
||||
delete_option( 'rewrite_rules' ); // Don't use flush_rewrite_rules at network activation. See #32471.
|
||||
}
|
||||
}
|
||||
729
wp-content/plugins/polylang/src/install/plugin-updater.php
Normal file
729
wp-content/plugins/polylang/src/install/plugin-updater.php
Normal file
@@ -0,0 +1,729 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows plugins to use their own update API.
|
||||
* Modified version with:
|
||||
* - 'polylang' text domain,
|
||||
* - missing comments for translators,
|
||||
* - a bug fix (https://github.com/polylang/polylang/pull/1629).
|
||||
*
|
||||
* @author Easy Digital Downloads
|
||||
* @version 1.9.4
|
||||
*/
|
||||
class PLL_Plugin_Updater {
|
||||
|
||||
private $api_url = '';
|
||||
private $api_data = array();
|
||||
private $plugin_file = '';
|
||||
private $name = '';
|
||||
private $slug = '';
|
||||
private $version = '';
|
||||
private $wp_override = false;
|
||||
private $beta = false;
|
||||
private $failed_request_cache_key;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @uses plugin_basename()
|
||||
* @uses hook()
|
||||
*
|
||||
* @param string $_api_url The URL pointing to the custom API endpoint.
|
||||
* @param string $_plugin_file Path to the plugin file.
|
||||
* @param array $_api_data Optional data to send with API calls.
|
||||
*/
|
||||
public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
|
||||
|
||||
global $edd_plugin_data;
|
||||
|
||||
$this->api_url = trailingslashit( $_api_url );
|
||||
$this->api_data = $_api_data;
|
||||
$this->plugin_file = $_plugin_file;
|
||||
$this->name = plugin_basename( $_plugin_file );
|
||||
$this->slug = basename( dirname( $_plugin_file ) );
|
||||
$this->version = $_api_data['version'];
|
||||
$this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
|
||||
$this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
|
||||
$this->failed_request_cache_key = 'edd_sl_failed_http_' . md5( $this->api_url );
|
||||
|
||||
$edd_plugin_data[ $this->slug ] = $this->api_data;
|
||||
|
||||
/**
|
||||
* Fires after the $edd_plugin_data is setup.
|
||||
*
|
||||
* @since x.x.x
|
||||
*
|
||||
* @param array $edd_plugin_data Array of EDD SL plugin data.
|
||||
*/
|
||||
do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
|
||||
|
||||
// Set up hooks.
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up WordPress filters to hook into WP's update process.
|
||||
*
|
||||
* @uses add_filter()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
|
||||
add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
|
||||
add_action( 'after_plugin_row', array( $this, 'show_update_notification' ), 10, 2 );
|
||||
add_action( 'admin_init', array( $this, 'show_changelog' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for Updates at the defined API endpoint and modify the update array.
|
||||
*
|
||||
* This function dives into the update API just when WordPress creates its update array,
|
||||
* then adds a custom API call and injects the custom plugin data retrieved from the API.
|
||||
* It is reassembled from parts of the native WordPress plugin update code.
|
||||
* See wp-includes/update.php line 121 for the original wp_update_plugins() function.
|
||||
*
|
||||
* @uses api_request()
|
||||
*
|
||||
* @param array $_transient_data Update array build by WordPress.
|
||||
* @return array Modified update array with custom plugin data.
|
||||
*/
|
||||
public function check_update( $_transient_data ) {
|
||||
|
||||
if ( ! is_object( $_transient_data ) ) {
|
||||
$_transient_data = new stdClass();
|
||||
}
|
||||
|
||||
if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
|
||||
return $_transient_data;
|
||||
}
|
||||
|
||||
$current = $this->get_update_transient_data();
|
||||
if ( false !== $current && is_object( $current ) && isset( $current->new_version ) ) {
|
||||
if ( version_compare( $this->version, $current->new_version, '<' ) ) {
|
||||
$_transient_data->response[ $this->name ] = $current;
|
||||
} else {
|
||||
// Populating the no_update information is required to support auto-updates in WordPress 5.5.
|
||||
$_transient_data->no_update[ $this->name ] = $current;
|
||||
}
|
||||
}
|
||||
$_transient_data->last_checked = time();
|
||||
$_transient_data->checked[ $this->name ] = $this->version;
|
||||
|
||||
return $_transient_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repo API data from store.
|
||||
* Save to cache.
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function get_repo_api_data() {
|
||||
$version_info = $this->get_cached_version_info();
|
||||
|
||||
if ( false === $version_info ) {
|
||||
$version_info = $this->api_request(
|
||||
'plugin_latest_version',
|
||||
array(
|
||||
'slug' => $this->slug,
|
||||
'beta' => $this->beta,
|
||||
)
|
||||
);
|
||||
if ( ! $version_info ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is required for your plugin to support auto-updates in WordPress 5.5.
|
||||
$version_info->plugin = $this->name;
|
||||
$version_info->id = $this->name;
|
||||
$version_info->tested = $this->get_tested_version( $version_info );
|
||||
if ( ! isset( $version_info->requires ) ) {
|
||||
$version_info->requires = '';
|
||||
}
|
||||
if ( ! isset( $version_info->requires_php ) ) {
|
||||
$version_info->requires_php = '';
|
||||
}
|
||||
|
||||
$this->set_version_info_cache( $version_info );
|
||||
}
|
||||
|
||||
// Added by Polylang.
|
||||
$defaults = array(
|
||||
'url' => '',
|
||||
'package' => '',
|
||||
'new_version' => '',
|
||||
'tested' => '',
|
||||
'requires' => '',
|
||||
'requires_php' => '',
|
||||
'icons' => new stdClass(),
|
||||
'banners' => new stdClass(),
|
||||
);
|
||||
|
||||
$version_info = (object) array_merge( $defaults, (array) $version_info );
|
||||
// End of added by Polylang.
|
||||
|
||||
return $version_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a limited set of data from the API response.
|
||||
* This is used for the update_plugins transient.
|
||||
*
|
||||
* @since 3.8.12
|
||||
* @return \stdClass|false
|
||||
*/
|
||||
private function get_update_transient_data() {
|
||||
$version_info = $this->get_repo_api_data();
|
||||
|
||||
if ( ! $version_info ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$limited_data = new \stdClass();
|
||||
$limited_data->slug = $this->slug;
|
||||
$limited_data->plugin = $this->name;
|
||||
$limited_data->url = $version_info->url;
|
||||
$limited_data->package = $version_info->package;
|
||||
$limited_data->icons = $this->convert_object_to_array( $version_info->icons );
|
||||
$limited_data->banners = $this->convert_object_to_array( $version_info->banners );
|
||||
$limited_data->new_version = $version_info->new_version;
|
||||
$limited_data->tested = $version_info->tested;
|
||||
$limited_data->requires = $version_info->requires;
|
||||
$limited_data->requires_php = $version_info->requires_php;
|
||||
|
||||
return $limited_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin's tested version.
|
||||
*
|
||||
* @since 1.9.2
|
||||
* @param object $version_info
|
||||
* @return null|string
|
||||
*/
|
||||
private function get_tested_version( $version_info ) {
|
||||
|
||||
// There is no tested version.
|
||||
if ( empty( $version_info->tested ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Strip off extra version data so the result is x.y or x.y.z.
|
||||
list( $current_wp_version ) = explode( '-', get_bloginfo( 'version' ) );
|
||||
|
||||
// The tested version is greater than or equal to the current WP version, no need to do anything.
|
||||
if ( version_compare( $version_info->tested, $current_wp_version, '>=' ) ) {
|
||||
return $version_info->tested;
|
||||
}
|
||||
$current_version_parts = explode( '.', $current_wp_version );
|
||||
$tested_parts = explode( '.', $version_info->tested );
|
||||
|
||||
// The current WordPress version is x.y.z, so update the tested version to match it.
|
||||
if ( isset( $current_version_parts[2] ) && $current_version_parts[0] === $tested_parts[0] && $current_version_parts[1] === $tested_parts[1] ) {
|
||||
$tested_parts[2] = $current_version_parts[2];
|
||||
}
|
||||
|
||||
return implode( '.', $tested_parts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the update notification on multisite subsites.
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $plugin
|
||||
*/
|
||||
public function show_update_notification( $file, $plugin ) {
|
||||
|
||||
// Return early if in the network admin, or if this is not a multisite install.
|
||||
if ( is_network_admin() || ! is_multisite() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow single site admins to see that an update is available.
|
||||
if ( ! current_user_can( 'activate_plugins' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->name !== $file ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not print any message if update does not exist.
|
||||
$update_cache = get_site_transient( 'update_plugins' );
|
||||
|
||||
if ( ! isset( $update_cache->response[ $this->name ] ) ) {
|
||||
if ( ! is_object( $update_cache ) ) {
|
||||
$update_cache = new stdClass();
|
||||
}
|
||||
$update_cache->response[ $this->name ] = $this->get_repo_api_data();
|
||||
}
|
||||
|
||||
// Return early if this plugin isn't in the transient->response or if the site is running the current or newer version of the plugin.
|
||||
if ( empty( $update_cache->response[ $this->name ] ) || version_compare( $this->version, $update_cache->response[ $this->name ]->new_version, '>=' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
'<tr class="plugin-update-tr %3$s" id="%1$s-update" data-slug="%1$s" data-plugin="%2$s">',
|
||||
$this->slug,
|
||||
$file,
|
||||
in_array( $this->name, $this->get_active_plugins(), true ) ? 'active' : 'inactive'
|
||||
);
|
||||
|
||||
echo '<td colspan="3" class="plugin-update colspanchange">';
|
||||
echo '<div class="update-message notice inline notice-warning notice-alt"><p>';
|
||||
|
||||
$changelog_link = '';
|
||||
if ( ! empty( $update_cache->response[ $this->name ]->sections->changelog ) ) {
|
||||
$changelog_link = add_query_arg(
|
||||
array(
|
||||
'edd_sl_action' => 'view_plugin_changelog',
|
||||
'plugin' => urlencode( $this->name ),
|
||||
'slug' => urlencode( $this->slug ),
|
||||
'TB_iframe' => 'true',
|
||||
'width' => 77,
|
||||
'height' => 911,
|
||||
),
|
||||
self_admin_url( 'index.php' )
|
||||
);
|
||||
}
|
||||
$update_link = add_query_arg(
|
||||
array(
|
||||
'action' => 'upgrade-plugin',
|
||||
'plugin' => urlencode( $this->name ),
|
||||
),
|
||||
self_admin_url( 'update.php' )
|
||||
);
|
||||
|
||||
printf(
|
||||
/* translators: the plugin name. */
|
||||
esc_html__( 'There is a new version of %1$s available.', 'polylang' ),
|
||||
esc_html( $plugin['Name'] )
|
||||
);
|
||||
|
||||
if ( ! current_user_can( 'update_plugins' ) ) {
|
||||
echo ' ';
|
||||
esc_html_e( 'Contact your network administrator to install the update.', 'polylang' );
|
||||
} elseif ( empty( $update_cache->response[ $this->name ]->package ) && ! empty( $changelog_link ) ) {
|
||||
echo ' ';
|
||||
printf(
|
||||
/* translators: 1. opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate. */
|
||||
__( '%1$sView version %2$s details%3$s.', 'polylang' ),
|
||||
'<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
|
||||
esc_html( $update_cache->response[ $this->name ]->new_version ),
|
||||
'</a>'
|
||||
);
|
||||
} elseif ( ! empty( $changelog_link ) ) {
|
||||
echo ' ';
|
||||
printf(
|
||||
/* translators: 1. and 4. are opening anchor tags 2. the new plugin version 3. and 5. are closing anchor tags. */
|
||||
__( '%1$sView version %2$s details%3$s or %4$supdate now%5$s.', 'polylang' ),
|
||||
'<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
|
||||
esc_html( $update_cache->response[ $this->name ]->new_version ),
|
||||
'</a>',
|
||||
'<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
|
||||
'</a>'
|
||||
);
|
||||
} else {
|
||||
printf(
|
||||
' %1$s%2$s%3$s',
|
||||
'<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
|
||||
esc_html__( 'Update now.', 'polylang' ),
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
do_action( "in_plugin_update_message-{$file}", $plugin, $plugin );
|
||||
|
||||
echo '</p></div></td></tr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugins active in a multisite network.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_active_plugins() {
|
||||
$active_plugins = (array) get_option( 'active_plugins' );
|
||||
$active_network_plugins = (array) get_site_option( 'active_sitewide_plugins' );
|
||||
|
||||
return array_merge( $active_plugins, array_keys( $active_network_plugins ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates information on the "View version x.x details" page with custom data.
|
||||
*
|
||||
* @uses api_request()
|
||||
*
|
||||
* @param mixed $_data
|
||||
* @param string $_action
|
||||
* @param object $_args
|
||||
* @return object $_data
|
||||
*/
|
||||
public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
|
||||
|
||||
if ( 'plugin_information' !== $_action ) {
|
||||
|
||||
return $_data;
|
||||
|
||||
}
|
||||
|
||||
if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->slug ) ) {
|
||||
|
||||
return $_data;
|
||||
|
||||
}
|
||||
|
||||
$to_send = array(
|
||||
'slug' => $this->slug,
|
||||
'is_ssl' => is_ssl(),
|
||||
'fields' => array(
|
||||
'banners' => array(),
|
||||
'reviews' => false,
|
||||
'icons' => array(),
|
||||
),
|
||||
);
|
||||
|
||||
// Get the transient where we store the api request for this plugin for 24 hours
|
||||
$edd_api_request_transient = $this->get_cached_version_info();
|
||||
|
||||
//If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
|
||||
if ( empty( $edd_api_request_transient ) ) {
|
||||
|
||||
$api_response = $this->api_request( 'plugin_information', $to_send );
|
||||
if ( empty( $api_response ) ) {
|
||||
return $_data;
|
||||
}
|
||||
|
||||
// Expires in 3 hours
|
||||
$this->set_version_info_cache( $api_response );
|
||||
|
||||
$_data = $api_response;
|
||||
} else {
|
||||
$_data = $edd_api_request_transient;
|
||||
}
|
||||
|
||||
// Convert sections into an associative array, since we're getting an object, but Core expects an array.
|
||||
if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
|
||||
$_data->sections = $this->convert_object_to_array( $_data->sections );
|
||||
}
|
||||
|
||||
// Convert banners into an associative array, since we're getting an object, but Core expects an array.
|
||||
if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
|
||||
$_data->banners = $this->convert_object_to_array( $_data->banners );
|
||||
}
|
||||
|
||||
// Convert icons into an associative array, since we're getting an object, but Core expects an array.
|
||||
if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
|
||||
$_data->icons = $this->convert_object_to_array( $_data->icons );
|
||||
}
|
||||
|
||||
// Convert contributors into an associative array, since we're getting an object, but Core expects an array.
|
||||
if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
|
||||
$_data->contributors = $this->convert_object_to_array( $_data->contributors );
|
||||
}
|
||||
|
||||
if ( ! isset( $_data->plugin ) ) {
|
||||
$_data->plugin = $this->name;
|
||||
}
|
||||
|
||||
if ( ! isset( $_data->version ) && ! empty( $_data->new_version ) ) {
|
||||
$_data->version = $_data->new_version;
|
||||
}
|
||||
|
||||
return $_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert some objects to arrays when injecting data into the update API
|
||||
*
|
||||
* Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
|
||||
* decoding, they are objects. This method allows us to pass in the object and return an associative array.
|
||||
*
|
||||
* @since 3.6.5
|
||||
*
|
||||
* @param stdClass $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function convert_object_to_array( $data ) {
|
||||
if ( ! is_array( $data ) && ! is_object( $data ) ) {
|
||||
return array();
|
||||
}
|
||||
$new_data = array();
|
||||
foreach ( $data as $key => $value ) {
|
||||
$new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
|
||||
}
|
||||
|
||||
return $new_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable SSL verification in order to prevent download update failures
|
||||
*
|
||||
* @param array $args
|
||||
* @param string $url
|
||||
* @return object $array
|
||||
*/
|
||||
public function http_request_args( $args, $url ) {
|
||||
|
||||
if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
|
||||
$args['sslverify'] = $this->verify_ssl();
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the API and, if successfull, returns the object delivered by the API.
|
||||
*
|
||||
* @uses get_bloginfo()
|
||||
* @uses wp_remote_post()
|
||||
* @uses is_wp_error()
|
||||
*
|
||||
* @param string $_action The requested action.
|
||||
* @param array $_data Parameters for the API action.
|
||||
* @return false|object|void
|
||||
*/
|
||||
private function api_request( $_action, $_data ) {
|
||||
$data = array_merge( $this->api_data, $_data );
|
||||
|
||||
if ( $data['slug'] !== $this->slug ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't allow a plugin to ping itself
|
||||
if ( trailingslashit( home_url() ) === $this->api_url ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->request_recently_failed() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->get_version_from_remote();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a request has recently failed.
|
||||
*
|
||||
* @since 1.9.1
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function request_recently_failed() {
|
||||
$failed_request_details = get_option( $this->failed_request_cache_key );
|
||||
|
||||
// Request has never failed.
|
||||
if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Request previously failed, but the timeout has expired.
|
||||
* This means we're allowed to try again.
|
||||
*/
|
||||
if ( time() > $failed_request_details ) {
|
||||
delete_option( $this->failed_request_cache_key );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a failed HTTP request for this API URL.
|
||||
* We set a timestamp for 1 hour from now. This prevents future API requests from being
|
||||
* made to this domain for 1 hour. Once the timestamp is in the past, API requests
|
||||
* will be allowed again. This way if the site is down for some reason we don't bombard
|
||||
* it with failed API requests.
|
||||
*
|
||||
* @see EDD_SL_Plugin_Updater::request_recently_failed
|
||||
*
|
||||
* @since 1.9.1
|
||||
*/
|
||||
private function log_failed_request() {
|
||||
update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* If available, show the changelog for sites in a multisite install.
|
||||
*/
|
||||
public function show_changelog() {
|
||||
|
||||
if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $_REQUEST['plugin'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'update_plugins' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to install plugin updates', 'polylang' ), esc_html__( 'Error', 'polylang' ), array( 'response' => 403 ) );
|
||||
}
|
||||
|
||||
$version_info = $this->get_repo_api_data();
|
||||
if ( isset( $version_info->sections ) ) {
|
||||
$sections = $this->convert_object_to_array( $version_info->sections );
|
||||
if ( ! empty( $sections['changelog'] ) ) {
|
||||
echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current version information from the remote site.
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
private function get_version_from_remote() {
|
||||
$api_params = array(
|
||||
'edd_action' => 'get_version',
|
||||
'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
|
||||
'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
|
||||
'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
|
||||
'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
|
||||
'slug' => $this->slug,
|
||||
'author' => $this->api_data['author'],
|
||||
'url' => home_url(),
|
||||
'beta' => $this->beta,
|
||||
'php_version' => phpversion(),
|
||||
'wp_version' => get_bloginfo( 'version' ),
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the parameters sent in the API request.
|
||||
*
|
||||
* @param array $api_params The array of data sent in the request.
|
||||
* @param array $this->api_data The array of data set up in the class constructor.
|
||||
* @param string $this->plugin_file The full path and filename of the file.
|
||||
*/
|
||||
$api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
|
||||
|
||||
$request = wp_remote_post(
|
||||
$this->api_url,
|
||||
array(
|
||||
'timeout' => 15,
|
||||
'sslverify' => $this->verify_ssl(),
|
||||
'body' => $api_params,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
|
||||
$this->log_failed_request();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$request = json_decode( wp_remote_retrieve_body( $request ) );
|
||||
|
||||
if ( $request && isset( $request->sections ) ) {
|
||||
$request->sections = maybe_unserialize( $request->sections );
|
||||
} else {
|
||||
$request = false;
|
||||
}
|
||||
|
||||
if ( $request && isset( $request->banners ) ) {
|
||||
$request->banners = maybe_unserialize( $request->banners );
|
||||
}
|
||||
|
||||
if ( $request && isset( $request->icons ) ) {
|
||||
$request->icons = maybe_unserialize( $request->icons );
|
||||
}
|
||||
|
||||
if ( ! empty( $request->sections ) ) {
|
||||
foreach ( $request->sections as $key => $section ) {
|
||||
$request->$key = (array) $section;
|
||||
}
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version info from the cache, if it exists.
|
||||
*
|
||||
* @param string $cache_key
|
||||
* @return object
|
||||
*/
|
||||
public function get_cached_version_info( $cache_key = '' ) {
|
||||
|
||||
if ( empty( $cache_key ) ) {
|
||||
$cache_key = $this->get_cache_key();
|
||||
}
|
||||
|
||||
$cache = get_option( $cache_key );
|
||||
|
||||
// Cache is expired
|
||||
if ( empty( $cache['timeout'] ) || time() > $cache['timeout'] ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
|
||||
$cache['value'] = json_decode( $cache['value'] );
|
||||
if ( ! empty( $cache['value']->icons ) ) {
|
||||
$cache['value']->icons = (array) $cache['value']->icons;
|
||||
}
|
||||
|
||||
return $cache['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the plugin version information to the database.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $cache_key
|
||||
*/
|
||||
public function set_version_info_cache( $value = '', $cache_key = '' ) {
|
||||
|
||||
if ( empty( $cache_key ) ) {
|
||||
$cache_key = $this->get_cache_key();
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'timeout' => strtotime( '+3 hours', time() ),
|
||||
'value' => wp_json_encode( $value ),
|
||||
);
|
||||
|
||||
update_option( $cache_key, $data, 'no' );
|
||||
|
||||
// Delete the duplicate option
|
||||
delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the SSL of the store should be verified.
|
||||
*
|
||||
* @since 1.6.13
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_ssl() {
|
||||
return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique key (option name) for a plugin.
|
||||
*
|
||||
* @since 1.9.0
|
||||
* @return string
|
||||
*/
|
||||
private function get_cache_key() {
|
||||
$string = $this->slug . $this->api_data['license'] . $this->beta;
|
||||
|
||||
return 'edd_sl_' . md5( serialize( $string ) );
|
||||
}
|
||||
}
|
||||
237
wp-content/plugins/polylang/src/install/t15s.php
Normal file
237
wp-content/plugins/polylang/src/install/t15s.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows to download translations from TranslationsPress
|
||||
* This is a modified version of the library available at https://github.com/WP-Translations/t15s-registry
|
||||
* This version aims to be compatible with PHP 5.2, and supports only plugins.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
class PLL_T15S {
|
||||
/**
|
||||
* Transient key
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const TRANSIENT_KEY_PLUGIN = 't15s-registry-plugins';
|
||||
|
||||
/**
|
||||
* Project directory slug
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $slug = '';
|
||||
|
||||
/**
|
||||
* Full GlotPress API URL for the project.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $api_url = '';
|
||||
|
||||
/**
|
||||
* Installed translations.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
private static $installed_translations;
|
||||
|
||||
/**
|
||||
* Available languages.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
private static $available_languages;
|
||||
|
||||
/**
|
||||
* Adds a new project to load translations for.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param string $slug Project directory slug.
|
||||
* @param string $api_url Full GlotPress API URL for the project.
|
||||
*/
|
||||
public function __construct( $slug, $api_url ) {
|
||||
$this->slug = $slug;
|
||||
$this->api_url = $api_url;
|
||||
|
||||
add_action( 'init', array( self::class, 'register_clean_translations_cache' ), 9999 );
|
||||
add_filter( 'translations_api', array( $this, 'translations_api' ), 10, 3 );
|
||||
add_filter( 'site_transient_update_plugins', array( $this, 'site_transient_update_plugins' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Short-circuits translations API requests for private projects.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param bool|array $result The result object. Default false.
|
||||
* @param string $requested_type The type of translations being requested.
|
||||
* @param object $args Translation API arguments.
|
||||
* @return bool|array
|
||||
*/
|
||||
public function translations_api( $result, $requested_type, $args ) {
|
||||
if ( 'plugins' === $requested_type && $this->slug === $args['slug'] ) {
|
||||
return self::get_translations( $args['slug'], $this->api_url );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the translations transients to include the private plugin or theme.
|
||||
*
|
||||
* @see wp_get_translation_updates()
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param bool|array $value The transient value.
|
||||
* @return bool|array
|
||||
*/
|
||||
public function site_transient_update_plugins( $value ) {
|
||||
if ( ! $value ) {
|
||||
$value = new stdClass();
|
||||
}
|
||||
|
||||
if ( ! isset( $value->translations ) ) {
|
||||
$value->translations = array();
|
||||
}
|
||||
|
||||
$translations = self::get_translations( $this->slug, $this->api_url );
|
||||
|
||||
if ( ! isset( $translations['translations'] ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$installed_translations = self::get_installed_translations();
|
||||
|
||||
foreach ( (array) $translations['translations'] as $translation ) {
|
||||
if ( in_array( $translation['language'], self::get_available_languages() ) ) {
|
||||
if ( isset( $installed_translations[ $this->slug ][ $translation['language'] ] ) && $translation['updated'] ) {
|
||||
$local = new DateTime( $installed_translations[ $this->slug ][ $translation['language'] ]['PO-Revision-Date'] );
|
||||
$remote = new DateTime( $translation['updated'] );
|
||||
|
||||
if ( $local >= $remote ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$translation['type'] = 'plugin';
|
||||
$translation['slug'] = $this->slug;
|
||||
|
||||
$value->translations[] = $translation;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers actions for clearing translation caches.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register_clean_translations_cache() {
|
||||
add_action( 'set_site_transient_update_plugins', array( self::class, 'clean_translations_cache' ) );
|
||||
add_action( 'delete_site_transient_update_plugins', array( self::class, 'clean_translations_cache' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears existing translation cache.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clean_translations_cache() {
|
||||
$translations = get_site_transient( self::TRANSIENT_KEY_PLUGIN );
|
||||
|
||||
if ( ! is_object( $translations ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't delete the cache if the transient gets changed multiple times
|
||||
* during a single request. Set cache lifetime to maximum 15 seconds.
|
||||
*/
|
||||
$cache_lifespan = 15;
|
||||
$time_not_changed = isset( $translations->_last_checked ) && ( time() - $translations->_last_checked ) > $cache_lifespan;
|
||||
|
||||
if ( ! $time_not_changed ) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete_site_transient( self::TRANSIENT_KEY_PLUGIN );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the translations for a given project.
|
||||
*
|
||||
* @since 2.6
|
||||
*
|
||||
* @param string $slug Project directory slug.
|
||||
* @param string $url Full GlotPress API URL for the project.
|
||||
* @return array Translation data.
|
||||
*/
|
||||
private static function get_translations( $slug, $url ) {
|
||||
$translations = get_site_transient( self::TRANSIENT_KEY_PLUGIN );
|
||||
|
||||
if ( ! is_object( $translations ) ) {
|
||||
$translations = new stdClass();
|
||||
}
|
||||
|
||||
if ( isset( $translations->{$slug} ) && is_array( $translations->{$slug} ) ) {
|
||||
return $translations->{$slug};
|
||||
}
|
||||
|
||||
$result = json_decode( wp_remote_retrieve_body( wp_remote_get( $url, array( 'timeout' => 3 ) ) ), true );
|
||||
|
||||
// Nothing found.
|
||||
if ( ! is_array( $result ) ) {
|
||||
$result = array();
|
||||
}
|
||||
|
||||
$translations->{$slug} = $result;
|
||||
$translations->_last_checked = time();
|
||||
|
||||
set_site_transient( self::TRANSIENT_KEY_PLUGIN, $translations );
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns installed translations.
|
||||
*
|
||||
* Used to cache the result of wp_get_installed_translations() as it is very expensive.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_installed_translations() {
|
||||
if ( null === self::$installed_translations ) {
|
||||
self::$installed_translations = wp_get_installed_translations( 'plugins' );
|
||||
}
|
||||
return self::$installed_translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns available languages.
|
||||
*
|
||||
* Used to cache the result of get_available_languages() as it is very expensive.
|
||||
*
|
||||
* @since 2.8
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_available_languages() {
|
||||
if ( null === self::$available_languages ) {
|
||||
self::$available_languages = get_available_languages();
|
||||
}
|
||||
return self::$available_languages;
|
||||
}
|
||||
}
|
||||
387
wp-content/plugins/polylang/src/install/upgrade.php
Normal file
387
wp-content/plugins/polylang/src/install/upgrade.php
Normal file
@@ -0,0 +1,387 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Polylang
|
||||
*/
|
||||
|
||||
use WP_Syntex\Polylang\Options\Options;
|
||||
/**
|
||||
* Manages Polylang upgrades
|
||||
*
|
||||
* @since 1.2
|
||||
*/
|
||||
class PLL_Upgrade {
|
||||
/**
|
||||
* Stores the plugin options.
|
||||
*
|
||||
* @var Options
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @since 1.2
|
||||
* @since 3.7 The `$options` parameter is an instance of `Options`.
|
||||
*
|
||||
* @param Options $options Polylang options.
|
||||
*/
|
||||
public function __construct( Options $options ) {
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if upgrade is possible otherwise die to avoid activation
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function can_activate() {
|
||||
if ( ! $this->can_upgrade() ) {
|
||||
ob_start();
|
||||
$this->admin_notices(); // FIXME the error message is displayed two times
|
||||
die( ob_get_contents() ); // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades if possible otherwise returns false to stop Polylang loading
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return bool true if upgrade is possible, false otherwise
|
||||
*/
|
||||
public function upgrade() {
|
||||
if ( ! $this->can_upgrade() ) {
|
||||
add_action( 'all_admin_notices', array( $this, 'admin_notices' ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
add_action( 'admin_init', array( $this, '_upgrade' ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if we the previous version is not too old
|
||||
* Upgrades if OK
|
||||
* /!\ never start any upgrade before admin_init as it is likely to conflict with some other plugins
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return bool true if upgrade is possible, false otherwise
|
||||
*/
|
||||
public function can_upgrade() {
|
||||
// Don't manage upgrade from version < 1.8
|
||||
return version_compare( $this->options['version'], '1.8', '>=' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a notice when upgrading from a too old version
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function admin_notices() {
|
||||
load_plugin_textdomain( 'polylang' );
|
||||
printf(
|
||||
'<div class="error"><p>%s</p><p>%s</p></div>',
|
||||
esc_html__( 'Polylang has been deactivated because you upgraded from a too old version.', 'polylang' ),
|
||||
sprintf(
|
||||
/* translators: %1$s and %2$s are Polylang version numbers */
|
||||
esc_html__( 'Before upgrading to %2$s, please upgrade to %1$s.', 'polylang' ),
|
||||
'<strong>2.9</strong>',
|
||||
POLYLANG_VERSION // phpcs:ignore WordPress.Security.EscapeOutput
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades the plugin depending on the previous version
|
||||
*
|
||||
* @since 1.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function _upgrade() {
|
||||
/*
|
||||
* Delete the transient precisely at this point (`admin_init`)
|
||||
* to ensure `Model\Languages` callbacks do their job.
|
||||
* i.e. delete the transient from the options table when external object cache is used.
|
||||
*/
|
||||
delete_transient( 'pll_languages_list' );
|
||||
|
||||
foreach ( array( '2.0.8', '2.1', '2.7', '3.4', '3.7', '3.8' ) as $version ) {
|
||||
if ( version_compare( $this->options['version'], $version, '<' ) ) {
|
||||
$method_to_call = array( $this, 'upgrade_' . str_replace( '.', '_', $version ) );
|
||||
if ( is_callable( $method_to_call ) ) {
|
||||
call_user_func( $method_to_call );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after Polylang has been upgraded and before the new version is saved in options.
|
||||
*
|
||||
* @since 3.7
|
||||
*/
|
||||
do_action( 'pll_upgrade' );
|
||||
|
||||
$this->options['previous_version'] = $this->options['version']; // Remember the previous version of Polylang since v1.7.7
|
||||
$this->options['version'] = POLYLANG_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades if the previous version is < 2.0.8
|
||||
* Changes the user meta 'user_lang' to 'locale' to match WP 4.7 choice
|
||||
*
|
||||
* @since 2.0.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function upgrade_2_0_8() {
|
||||
global $wpdb;
|
||||
$wpdb->update( $wpdb->usermeta, array( 'meta_key' => 'locale' ), array( 'meta_key' => 'user_lang' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades if the previous version is < 2.1.
|
||||
* Moves strings translations from polylang_mo post_content to post meta _pll_strings_translations.
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function upgrade_2_1() {
|
||||
$posts = get_posts(
|
||||
array(
|
||||
'post_type' => 'polylang_mo',
|
||||
'post_status' => 'any',
|
||||
'numberposts' => -1,
|
||||
'nopaging' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_array( $posts ) ) {
|
||||
foreach ( $posts as $post ) {
|
||||
$meta = get_post_meta( $post->ID, '_pll_strings_translations', true );
|
||||
|
||||
if ( empty( $meta ) ) {
|
||||
$strings = maybe_unserialize( $post->post_content );
|
||||
if ( is_array( $strings ) ) {
|
||||
update_post_meta( $post->ID, '_pll_strings_translations', $strings );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades if the previous version is < 2.7
|
||||
* Replace numeric keys by hashes in WPML registered strings
|
||||
* Dismiss the wizard notice for existing sites
|
||||
*
|
||||
* @since 2.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function upgrade_2_7() {
|
||||
$strings = get_option( 'polylang_wpml_strings' );
|
||||
if ( is_array( $strings ) ) {
|
||||
$new_strings = array();
|
||||
|
||||
foreach ( $strings as $string ) {
|
||||
$context = $string['context'];
|
||||
$name = $string['name'];
|
||||
|
||||
$key = md5( "$context | $name" );
|
||||
$new_strings[ $key ] = $string;
|
||||
}
|
||||
update_option( 'polylang_wpml_strings', $new_strings );
|
||||
}
|
||||
|
||||
PLL_Admin_Notices::dismiss( 'wizard' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades if the previous version is < 3.4.0.
|
||||
*
|
||||
* @since 3.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function upgrade_3_4() {
|
||||
$this->migrate_locale_fallback_to_language_description();
|
||||
|
||||
$this->migrate_strings_translations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades if the previous version is < 3.7.
|
||||
* Hides the "The language is set from content" option if it isn't the one selected.
|
||||
* Cleans up strings translations so we don't store translations duplicated from the source.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function upgrade_3_7() {
|
||||
$this->allow_to_hide_language_from_content();
|
||||
$this->empty_duplicated_strings_translations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades if the previous version is < 3.8.
|
||||
* Migrates language taxonomies from `polylang` option to `pll_language_taxonomies` option.
|
||||
*
|
||||
* @since 3.8
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function upgrade_3_8() {
|
||||
$options = get_option( 'polylang' );
|
||||
|
||||
$language_taxonomies = array();
|
||||
if ( is_array( $options ) && isset( $options['language_taxonomies'] ) && is_array( $options['language_taxonomies'] ) ) {
|
||||
$language_taxonomies = $options['language_taxonomies'];
|
||||
}
|
||||
|
||||
update_option( 'pll_language_taxonomies', $language_taxonomies );
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves strings translations from post meta to term meta _pll_strings_translations.
|
||||
*
|
||||
* @since 3.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function migrate_strings_translations() {
|
||||
$posts = get_posts(
|
||||
array(
|
||||
'post_type' => 'polylang_mo',
|
||||
'post_status' => 'any',
|
||||
'numberposts' => -1,
|
||||
'nopaging' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_array( $posts ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $posts as $post ) {
|
||||
$meta = get_post_meta( $post->ID, '_pll_strings_translations', true );
|
||||
|
||||
$term_id = (int) substr( $post->post_title, 12 );
|
||||
|
||||
// Do not delete post metas in case a user needs to rollback to Polylang < 3.4.
|
||||
|
||||
if ( empty( $meta ) || ! is_array( $meta ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
update_term_meta( $term_id, '_pll_strings_translations', wp_slash( $meta ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate locale fallback to language term description.
|
||||
*
|
||||
* @since 3.4
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function migrate_locale_fallback_to_language_description() {
|
||||
// Migrate locale fallbacks from term metas to language term description.
|
||||
$terms = get_terms(
|
||||
array(
|
||||
'taxonomy' => 'language',
|
||||
'hide_empty' => false,
|
||||
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
||||
array(
|
||||
'key' => 'fallback',
|
||||
'compare_key' => 'EXISTS',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_array( $terms ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$fallbacks = get_term_meta( $term->term_id, 'fallback', true );
|
||||
|
||||
delete_term_meta( $term->term_id, 'fallback' );
|
||||
|
||||
if ( empty( $fallbacks ) || ! is_array( $fallbacks ) ) {
|
||||
// Empty or invalid value, should not happen.
|
||||
continue;
|
||||
}
|
||||
|
||||
$description = maybe_unserialize( $term->description );
|
||||
$description = is_array( $description ) ? $description : array();
|
||||
|
||||
$description['fallbacks'] = $fallbacks;
|
||||
/** @var string */
|
||||
$description = maybe_serialize( $description );
|
||||
|
||||
wp_update_term( $term->term_id, 'language', array( 'description' => $description ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the language from content option if it is not the one selected.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function allow_to_hide_language_from_content() {
|
||||
update_option(
|
||||
'pll_language_from_content_available',
|
||||
0 === $this->options['force_lang'] ? 'yes' : 'no'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up strings translations so we don't store translations duplicated from the source.
|
||||
*
|
||||
* @since 3.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function empty_duplicated_strings_translations() {
|
||||
$terms = get_terms(
|
||||
array(
|
||||
'taxonomy' => 'language',
|
||||
'hide_empty' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! is_array( $terms ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$strings = get_term_meta( $term->term_id, '_pll_strings_translations', true );
|
||||
|
||||
if ( empty( $strings ) || ! is_array( $strings ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $strings as $i => $tuple ) {
|
||||
if ( $tuple[0] !== $tuple[1] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$strings[ $i ][1] = '';
|
||||
}
|
||||
|
||||
update_term_meta( $term->term_id, '_pll_strings_translations', $strings );
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user