first commit

This commit is contained in:
2025-02-24 22:33:42 +01:00
commit 737c037e85
18358 changed files with 5392983 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit8375818328e17ade60f265ce6346a9ae::getLoader();

View File

@@ -0,0 +1,579 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

View File

@@ -0,0 +1,359 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
if (self::$installed !== array()) {
$installed[] = self::$installed;
}
return $installed;
}
}

View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,284 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'PLL_ACF' => $baseDir . '/integrations/acf/acf.php',
'PLL_ACF_Auto_Translate' => $baseDir . '/integrations/acf/acf-auto-translate.php',
'PLL_ACF_Sync_Metas' => $baseDir . '/integrations/acf/acf-sync-metas.php',
'PLL_AS3CF' => $vendorDir . '/wpsyntex/polylang/integrations/wp-offload-media/as3cf.php',
'PLL_Abstract_Language_Switcher_Block' => $baseDir . '/modules/block-editor/abstract-language-switcher-block.php',
'PLL_Abstract_Sitemaps' => $vendorDir . '/wpsyntex/polylang/modules/sitemaps/abstract-sitemaps.php',
'PLL_Accept_Language' => $vendorDir . '/wpsyntex/polylang/frontend/accept-language.php',
'PLL_Accept_Languages_Collection' => $vendorDir . '/wpsyntex/polylang/frontend/accept-languages-collection.php',
'PLL_Active_Languages' => $baseDir . '/modules/active-languages/active-languages.php',
'PLL_Admin' => $vendorDir . '/wpsyntex/polylang/admin/admin.php',
'PLL_Admin_Advanced_Media' => $baseDir . '/modules/media/admin-advanced-media.php',
'PLL_Admin_Base' => $vendorDir . '/wpsyntex/polylang/admin/admin-base.php',
'PLL_Admin_Block_Editor' => $vendorDir . '/wpsyntex/polylang/admin/admin-block-editor.php',
'PLL_Admin_Classic_Editor' => $vendorDir . '/wpsyntex/polylang/admin/admin-classic-editor.php',
'PLL_Admin_Default_Term' => $vendorDir . '/wpsyntex/polylang/admin/admin-default-term.php',
'PLL_Admin_Filters' => $vendorDir . '/wpsyntex/polylang/admin/admin-filters.php',
'PLL_Admin_Filters_Columns' => $vendorDir . '/wpsyntex/polylang/admin/admin-filters-columns.php',
'PLL_Admin_Filters_Media' => $vendorDir . '/wpsyntex/polylang/admin/admin-filters-media.php',
'PLL_Admin_Filters_Post' => $vendorDir . '/wpsyntex/polylang/admin/admin-filters-post.php',
'PLL_Admin_Filters_Post_Base' => $vendorDir . '/wpsyntex/polylang/admin/admin-filters-post-base.php',
'PLL_Admin_Filters_Term' => $vendorDir . '/wpsyntex/polylang/admin/admin-filters-term.php',
'PLL_Admin_Filters_Widgets_Options' => $vendorDir . '/wpsyntex/polylang/admin/admin-filters-widgets-options.php',
'PLL_Admin_Links' => $vendorDir . '/wpsyntex/polylang/admin/admin-links.php',
'PLL_Admin_Loader' => $baseDir . '/services/admin-loader/admin-loader.php',
'PLL_Admin_Model' => $vendorDir . '/wpsyntex/polylang/admin/admin-model.php',
'PLL_Admin_Nav_Menu' => $vendorDir . '/wpsyntex/polylang/admin/admin-nav-menu.php',
'PLL_Admin_Notices' => $vendorDir . '/wpsyntex/polylang/admin/admin-notices.php',
'PLL_Admin_Site_Health' => $vendorDir . '/wpsyntex/polylang/modules/site-health/admin-site-health.php',
'PLL_Admin_Static_Pages' => $vendorDir . '/wpsyntex/polylang/admin/admin-static-pages.php',
'PLL_Admin_Strings' => $vendorDir . '/wpsyntex/polylang/admin/admin-strings.php',
'PLL_Admin_Sync' => $vendorDir . '/wpsyntex/polylang/modules/sync/admin-sync.php',
'PLL_Aqua_Resizer' => $vendorDir . '/wpsyntex/polylang/integrations/aqua-resizer/aqua-resizer.php',
'PLL_Base' => $vendorDir . '/wpsyntex/polylang/include/base.php',
'PLL_Block_Editor_Plugin' => $baseDir . '/modules/block-editor/block-editor-plugin.php',
'PLL_Bulk_Translate' => $baseDir . '/modules/bulk-translate/bulk-translate.php',
'PLL_Bulk_Translate_Option' => $baseDir . '/modules/bulk-translate/bulk-translate-option.php',
'PLL_CPAC' => $baseDir . '/integrations/admin-columns/cpac.php',
'PLL_CPTUI' => $baseDir . '/integrations/cptui/cptui.php',
'PLL_CRUD_Posts' => $vendorDir . '/wpsyntex/polylang/include/crud-posts.php',
'PLL_CRUD_Terms' => $vendorDir . '/wpsyntex/polylang/include/crud-terms.php',
'PLL_Cache' => $vendorDir . '/wpsyntex/polylang/include/cache.php',
'PLL_Cache_Compat' => $vendorDir . '/wpsyntex/polylang/integrations/cache/cache-compat.php',
'PLL_Canonical' => $vendorDir . '/wpsyntex/polylang/frontend/canonical.php',
'PLL_Cft' => $vendorDir . '/wpsyntex/polylang/integrations/custom-field-template/cft.php',
'PLL_Choose_Lang' => $vendorDir . '/wpsyntex/polylang/frontend/choose-lang.php',
'PLL_Choose_Lang_Content' => $vendorDir . '/wpsyntex/polylang/frontend/choose-lang-content.php',
'PLL_Choose_Lang_Domain' => $vendorDir . '/wpsyntex/polylang/frontend/choose-lang-domain.php',
'PLL_Choose_Lang_Url' => $vendorDir . '/wpsyntex/polylang/frontend/choose-lang-url.php',
'PLL_Collect_Linked_Posts' => $baseDir . '/services/collect-linked-objects/collect-linked-posts.php',
'PLL_Collect_Linked_Terms' => $baseDir . '/services/collect-linked-objects/collect-linked-terms.php',
'PLL_Content_Blocks' => $baseDir . '/integrations/content-blocks/content-blocks.php',
'PLL_Cookie' => $vendorDir . '/wpsyntex/polylang/include/cookie.php',
'PLL_DOM_Content' => $baseDir . '/services/dom/dom-content.php',
'PLL_DOM_Document' => $baseDir . '/services/dom/dom-document.php',
'PLL_DOM_Nodes_Iterator' => $baseDir . '/services/dom/dom-nodes-iterator.php',
'PLL_Data_Encoding' => $baseDir . '/services/data-encoding.php',
'PLL_Db_Tools' => $vendorDir . '/wpsyntex/polylang/include/db-tools.php',
'PLL_Divi_Builder' => $baseDir . '/integrations/divi/divi-builder.php',
'PLL_Domain_Mapping' => $vendorDir . '/wpsyntex/polylang/integrations/domain-mapping/domain-mapping.php',
'PLL_Duplicate' => $baseDir . '/modules/duplicate/duplicate.php',
'PLL_Duplicate_Action' => $baseDir . '/modules/duplicate/duplicate-action.php',
'PLL_Duplicate_Post' => $vendorDir . '/wpsyntex/polylang/integrations/duplicate-post/duplicate-post.php',
'PLL_Duplicate_REST' => $baseDir . '/modules/duplicate/duplicate-rest.php',
'PLL_Export_Bulk_Option' => $baseDir . '/modules/import-export/export/export-bulk-option.php',
'PLL_Export_Container' => $baseDir . '/services/exporter/export-container.php',
'PLL_Export_Data' => $baseDir . '/services/exporter/export-data.php',
'PLL_Export_Data_From_Posts' => $baseDir . '/services/exporter/export-data-from-posts.php',
'PLL_Export_Data_From_Strings' => $baseDir . '/services/exporter/export-data-from-strings.php',
'PLL_Export_Download' => $baseDir . '/modules/import-export/export/export-download.php',
'PLL_Export_File' => $baseDir . '/modules/import-export/export/export-file.php',
'PLL_Export_Metas' => $baseDir . '/services/exporter/export-metas.php',
'PLL_Export_Post_Metas' => $baseDir . '/services/exporter/export-post-metas.php',
'PLL_Export_Posts' => $baseDir . '/services/exporter/export-posts.php',
'PLL_Export_Strings' => $baseDir . '/services/exporter/export-strings.php',
'PLL_Export_Strings_Action' => $baseDir . '/modules/import-export/export/export-strings-action.php',
'PLL_Export_Term_Metas' => $baseDir . '/services/exporter/export-term-metas.php',
'PLL_Export_Terms' => $baseDir . '/services/exporter/export-terms.php',
'PLL_Export_Translated_Objects' => $baseDir . '/services/exporter/export-translated-objects.php',
'PLL_FLBuilder' => $baseDir . '/integrations/beaver-builder/flbuilder.php',
'PLL_FSE_Abstract_Bulk_Edit_Template_Slugs_Module' => $baseDir . '/modules/full-site-editing/fse-abstract-bulk-edit-template-slugs-module.php',
'PLL_FSE_Abstract_Module' => $baseDir . '/modules/full-site-editing/fse-abstract-module.php',
'PLL_FSE_Default_Language_Change' => $baseDir . '/modules/full-site-editing/fse-default-language-change.php',
'PLL_FSE_Filter_Block_Types' => $baseDir . '/modules/full-site-editing/fse-filter-block-types.php',
'PLL_FSE_Language' => $baseDir . '/modules/full-site-editing/fse-language.php',
'PLL_FSE_Language_Slug_Change' => $baseDir . '/modules/full-site-editing/fse-language-slug-change.php',
'PLL_FSE_Post_Deletion' => $baseDir . '/modules/full-site-editing/fse-post-deletion.php',
'PLL_FSE_Post_Types' => $baseDir . '/modules/full-site-editing/fse-post-types.php',
'PLL_FSE_Query_Filters' => $baseDir . '/modules/full-site-editing/fse-query-filters.php',
'PLL_FSE_REST_Duplicate_Template' => $baseDir . '/modules/full-site-editing/fse-rest-duplicate-template.php',
'PLL_FSE_REST_Enforce_Default_Template' => $baseDir . '/modules/full-site-editing/fse-rest-enforce-default-template.php',
'PLL_FSE_REST_Route' => $baseDir . '/modules/full-site-editing/fse-rest-route.php',
'PLL_FSE_REST_Template' => $baseDir . '/modules/full-site-editing/fse-rest-template.php',
'PLL_FSE_Recreate_Language' => $baseDir . '/modules/full-site-editing/fse-recreate-language.php',
'PLL_FSE_Template_Model' => $baseDir . '/modules/full-site-editing/fse-template-model.php',
'PLL_FSE_Template_Slug' => $baseDir . '/modules/full-site-editing/fse-template-slug.php',
'PLL_FSE_Template_Slug_Sync' => $baseDir . '/modules/full-site-editing/fse-template-slug-sync.php',
'PLL_FSE_Tools' => $baseDir . '/modules/full-site-editing/fse-tools.php',
'PLL_Featured_Content' => $vendorDir . '/wpsyntex/polylang/integrations/jetpack/featured-content.php',
'PLL_File_Format' => $baseDir . '/modules/import-export/file-format/file-format.php',
'PLL_File_Format_Factory' => $baseDir . '/modules/import-export/file-format/file-format-factory.php',
'PLL_Filter_REST_Routes' => $vendorDir . '/wpsyntex/polylang/include/filter-rest-routes.php',
'PLL_Filters' => $vendorDir . '/wpsyntex/polylang/include/filters.php',
'PLL_Filters_Links' => $vendorDir . '/wpsyntex/polylang/include/filters-links.php',
'PLL_Filters_Sanitization' => $vendorDir . '/wpsyntex/polylang/include/filters-sanitization.php',
'PLL_Filters_Widgets_Options' => $vendorDir . '/wpsyntex/polylang/include/filters-widgets-options.php',
'PLL_Format_Util' => $baseDir . '/include/format-util.php',
'PLL_Frontend' => $vendorDir . '/wpsyntex/polylang/frontend/frontend.php',
'PLL_Frontend_Auto_Translate' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-auto-translate.php',
'PLL_Frontend_Filters' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-filters.php',
'PLL_Frontend_Filters_Links' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-filters-links.php',
'PLL_Frontend_Filters_Search' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-filters-search.php',
'PLL_Frontend_Filters_Widgets' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-filters-widgets.php',
'PLL_Frontend_Filters_Widgets_Blocks' => $baseDir . '/modules/block-editor/frontend-filters-widgets-blocks.php',
'PLL_Frontend_Links' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-links.php',
'PLL_Frontend_Nav_Menu' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-nav-menu.php',
'PLL_Frontend_Static_Pages' => $vendorDir . '/wpsyntex/polylang/frontend/frontend-static-pages.php',
'PLL_Frontend_Translate_Slugs' => $baseDir . '/modules/translate-slugs/frontend-translate-slugs.php',
'PLL_Import_Action' => $baseDir . '/modules/import-export/import/import-action.php',
'PLL_Import_Export' => $baseDir . '/modules/import-export/import-export.php',
'PLL_Import_File_Interface' => $baseDir . '/modules/import-export/import/import-file-interface.php',
'PLL_Import_Object_Interface' => $baseDir . '/modules/import-export/import/import-object-interface.php',
'PLL_Import_Posts' => $baseDir . '/modules/import-export/import/import-posts.php',
'PLL_Import_Strings' => $baseDir . '/modules/import-export/import/import-strings.php',
'PLL_Import_Terms' => $baseDir . '/modules/import-export/import/import-terms.php',
'PLL_Install' => $vendorDir . '/wpsyntex/polylang/install/install.php',
'PLL_Install_Base' => $vendorDir . '/wpsyntex/polylang/install/install-base.php',
'PLL_Integrations' => $vendorDir . '/wpsyntex/polylang/integrations/integrations.php',
'PLL_Jetpack' => $vendorDir . '/wpsyntex/polylang/integrations/jetpack/jetpack.php',
'PLL_Language' => $vendorDir . '/wpsyntex/polylang/include/language.php',
'PLL_Language_Deprecated' => $vendorDir . '/wpsyntex/polylang/include/language-deprecated.php',
'PLL_Language_Factory' => $vendorDir . '/wpsyntex/polylang/include/language-factory.php',
'PLL_Language_Switcher_Block' => $baseDir . '/modules/block-editor/language-switcher-block.php',
'PLL_License' => $vendorDir . '/wpsyntex/polylang/include/license.php',
'PLL_Links' => $vendorDir . '/wpsyntex/polylang/include/links.php',
'PLL_Links_Abstract_Domain' => $vendorDir . '/wpsyntex/polylang/include/links-abstract-domain.php',
'PLL_Links_Default' => $vendorDir . '/wpsyntex/polylang/include/links-default.php',
'PLL_Links_Directory' => $vendorDir . '/wpsyntex/polylang/include/links-directory.php',
'PLL_Links_Domain' => $vendorDir . '/wpsyntex/polylang/include/links-domain.php',
'PLL_Links_Model' => $vendorDir . '/wpsyntex/polylang/include/links-model.php',
'PLL_Links_Permalinks' => $vendorDir . '/wpsyntex/polylang/include/links-permalinks.php',
'PLL_Links_Subdomain' => $vendorDir . '/wpsyntex/polylang/include/links-subdomain.php',
'PLL_Locale_Fallback' => $baseDir . '/modules/locale-fallback/locale-fallback.php',
'PLL_MO' => $vendorDir . '/wpsyntex/polylang/include/mo.php',
'PLL_Manage_User_Capabilities' => $baseDir . '/services/manage-user-capabilities.php',
'PLL_Media_Bulk_Option' => $baseDir . '/modules/media/media-bulk-option.php',
'PLL_Metabox_Button' => $baseDir . '/services/metabox-button/metabox-button.php',
'PLL_Metabox_User_Button' => $baseDir . '/services/metabox-button/metabox-user-button.php',
'PLL_Model' => $vendorDir . '/wpsyntex/polylang/include/model.php',
'PLL_Module_Interface' => $baseDir . '/modules/module-interface.php',
'PLL_Multilingual_Sitemaps_Provider' => $vendorDir . '/wpsyntex/polylang/modules/sitemaps/multilingual-sitemaps-provider.php',
'PLL_Nav_Menu' => $vendorDir . '/wpsyntex/polylang/include/nav-menu.php',
'PLL_Navigation_Language_Switcher_Block' => $baseDir . '/modules/block-editor/navigation-language-switcher-block.php',
'PLL_No_Category_Base' => $vendorDir . '/wpsyntex/polylang/integrations/no-category-base/no-category-base.php',
'PLL_OLT_Manager' => $vendorDir . '/wpsyntex/polylang/include/olt-manager.php',
'PLL_PO_Export' => $baseDir . '/modules/import-export/po/po-export.php',
'PLL_PO_Format' => $baseDir . '/modules/import-export/po/po-format.php',
'PLL_PO_Import' => $baseDir . '/modules/import-export/po/po-import.php',
'PLL_Plugin_Updater' => $vendorDir . '/wpsyntex/polylang/install/plugin-updater.php',
'PLL_Pro' => $baseDir . '/include/pro.php',
'PLL_Query' => $vendorDir . '/wpsyntex/polylang/include/query.php',
'PLL_REST_API' => $baseDir . '/modules/rest/rest-api.php',
'PLL_REST_Comment' => $baseDir . '/modules/rest/rest-comment.php',
'PLL_REST_Filtered_Object' => $baseDir . '/modules/rest/rest-filtered-object.php',
'PLL_REST_Post' => $baseDir . '/modules/rest/rest-post.php',
'PLL_REST_Request' => $vendorDir . '/wpsyntex/polylang/include/rest-request.php',
'PLL_REST_Term' => $baseDir . '/modules/rest/rest-term.php',
'PLL_REST_Translated_Object' => $baseDir . '/modules/rest/rest-translated-object.php',
'PLL_Settings' => $vendorDir . '/wpsyntex/polylang/settings/settings.php',
'PLL_Settings_Advanced_Media' => $baseDir . '/modules/media/settings-advanced-media.php',
'PLL_Settings_Browser' => $vendorDir . '/wpsyntex/polylang/settings/settings-browser.php',
'PLL_Settings_CPT' => $vendorDir . '/wpsyntex/polylang/settings/settings-cpt.php',
'PLL_Settings_Licenses' => $vendorDir . '/wpsyntex/polylang/settings/settings-licenses.php',
'PLL_Settings_Media' => $vendorDir . '/wpsyntex/polylang/settings/settings-media.php',
'PLL_Settings_Module' => $vendorDir . '/wpsyntex/polylang/settings/settings-module.php',
'PLL_Settings_Preview_Machine_Translation' => $vendorDir . '/wpsyntex/polylang/modules/machine-translation/settings-preview-machine-translation.php',
'PLL_Settings_Preview_Share_Slug' => $vendorDir . '/wpsyntex/polylang/modules/share-slug/settings-preview-share-slug.php',
'PLL_Settings_Preview_Translate_Slugs' => $vendorDir . '/wpsyntex/polylang/modules/translate-slugs/settings-preview-translate-slugs.php',
'PLL_Settings_Share_Slug' => $baseDir . '/modules/share-slug/settings-share-slug.php',
'PLL_Settings_Sync' => $vendorDir . '/wpsyntex/polylang/modules/sync/settings-sync.php',
'PLL_Settings_Translate_Slugs' => $baseDir . '/modules/translate-slugs/settings-translate-slugs.php',
'PLL_Settings_Url' => $vendorDir . '/wpsyntex/polylang/settings/settings-url.php',
'PLL_Share_Post_Slug' => $baseDir . '/modules/share-slug/share-post-slug.php',
'PLL_Share_Term_Slug' => $baseDir . '/modules/share-slug/share-term-slug.php',
'PLL_Sitemaps' => $vendorDir . '/wpsyntex/polylang/modules/sitemaps/sitemaps.php',
'PLL_Sitemaps_Domain' => $vendorDir . '/wpsyntex/polylang/modules/sitemaps/sitemaps-domain.php',
'PLL_Static_Pages' => $vendorDir . '/wpsyntex/polylang/include/static-pages.php',
'PLL_Switcher' => $vendorDir . '/wpsyntex/polylang/include/switcher.php',
'PLL_Sync' => $vendorDir . '/wpsyntex/polylang/modules/sync/sync.php',
'PLL_Sync_Content' => $baseDir . '/modules/sync/sync-content.php',
'PLL_Sync_Metas' => $vendorDir . '/wpsyntex/polylang/modules/sync/sync-metas.php',
'PLL_Sync_Navigation' => $baseDir . '/modules/sync/sync-navigation.php',
'PLL_Sync_Post' => $baseDir . '/modules/sync-post/sync-post.php',
'PLL_Sync_Post_Bulk_Option' => $baseDir . '/modules/sync-post/sync-post-bulk-option.php',
'PLL_Sync_Post_Button' => $baseDir . '/modules/sync-post/sync-post-button.php',
'PLL_Sync_Post_Metas' => $vendorDir . '/wpsyntex/polylang/modules/sync/sync-post-metas.php',
'PLL_Sync_Post_Model' => $baseDir . '/modules/sync-post/sync-post-model.php',
'PLL_Sync_Post_REST' => $baseDir . '/modules/sync-post/sync-post-rest.php',
'PLL_Sync_Tax' => $vendorDir . '/wpsyntex/polylang/modules/sync/sync-tax.php',
'PLL_Sync_Term_Metas' => $vendorDir . '/wpsyntex/polylang/modules/sync/sync-term-metas.php',
'PLL_T15S' => $vendorDir . '/wpsyntex/polylang/install/t15s.php',
'PLL_TEC' => $baseDir . '/integrations/events-calendar/tec.php',
'PLL_Table_Languages' => $vendorDir . '/wpsyntex/polylang/settings/table-languages.php',
'PLL_Table_Settings' => $vendorDir . '/wpsyntex/polylang/settings/table-settings.php',
'PLL_Table_String' => $vendorDir . '/wpsyntex/polylang/settings/table-string.php',
'PLL_Toggle_User_Meta' => $baseDir . '/services/metabox-button/toggle-user-meta.php',
'PLL_Translatable_Object' => $vendorDir . '/wpsyntex/polylang/include/translatable-object.php',
'PLL_Translatable_Object_With_Types_Interface' => $vendorDir . '/wpsyntex/polylang/include/translatable-object-with-types-interface.php',
'PLL_Translatable_Object_With_Types_Trait' => $vendorDir . '/wpsyntex/polylang/include/translatable-object-with-types-trait.php',
'PLL_Translatable_Objects' => $vendorDir . '/wpsyntex/polylang/include/translatable-objects.php',
'PLL_Translate_Option' => $vendorDir . '/wpsyntex/polylang/include/translate-option.php',
'PLL_Translate_Slugs' => $baseDir . '/modules/translate-slugs/translate-slugs.php',
'PLL_Translate_Slugs_Model' => $baseDir . '/modules/translate-slugs/translate-slugs-model.php',
'PLL_Translated_Object' => $vendorDir . '/wpsyntex/polylang/include/translated-object.php',
'PLL_Translated_Post' => $vendorDir . '/wpsyntex/polylang/include/translated-post.php',
'PLL_Translated_Term' => $vendorDir . '/wpsyntex/polylang/include/translated-term.php',
'PLL_Translation_Block_Parsing_Rules' => $baseDir . '/services/translation/translation-block-parsing-rules.php',
'PLL_Translation_Content' => $baseDir . '/services/translation/translation-content.php',
'PLL_Translation_Metas' => $baseDir . '/services/translation/translation-metas.php',
'PLL_Translation_Object_Model_Interface' => $baseDir . '/services/translation/translation-object-model-interface.php',
'PLL_Translation_Post_Metas' => $baseDir . '/services/translation/translation-post-metas.php',
'PLL_Translation_Post_Model' => $baseDir . '/services/translation/translation-post-model.php',
'PLL_Translation_Term_Metas' => $baseDir . '/services/translation/translation-term-metas.php',
'PLL_Translation_Term_Model' => $baseDir . '/services/translation/translation-term-model.php',
'PLL_Translation_Walker_Blocks' => $baseDir . '/services/translation/translation-walker-blocks.php',
'PLL_Translation_Walker_Classic' => $baseDir . '/services/translation/translation-walker-classic.php',
'PLL_Translation_Walker_Factory' => $baseDir . '/services/translation/translation-walker-factory.php',
'PLL_Translation_Walker_Interface' => $baseDir . '/services/translation/translation-walker-interface.php',
'PLL_Twenty_Seventeen' => $vendorDir . '/wpsyntex/polylang/integrations/twenty-seventeen/twenty-seven-teen.php',
'PLL_Upgrade' => $vendorDir . '/wpsyntex/polylang/install/upgrade.php',
'PLL_WPML_API' => $vendorDir . '/wpsyntex/polylang/modules/wpml/wpml-api.php',
'PLL_WPML_Compat' => $vendorDir . '/wpsyntex/polylang/modules/wpml/wpml-compat.php',
'PLL_WPML_Config' => $vendorDir . '/wpsyntex/polylang/modules/wpml/wpml-config.php',
'PLL_WPSEO' => $vendorDir . '/wpsyntex/polylang/integrations/wpseo/wpseo.php',
'PLL_WPSEO_OGP' => $vendorDir . '/wpsyntex/polylang/integrations/wpseo/wpseo-ogp.php',
'PLL_WP_Import' => $vendorDir . '/wpsyntex/polylang/integrations/wp-importer/wp-import.php',
'PLL_WP_Sweep' => $vendorDir . '/wpsyntex/polylang/integrations/wp-sweep/wp-sweep.php',
'PLL_Walker' => $vendorDir . '/wpsyntex/polylang/include/walker.php',
'PLL_Walker_Dropdown' => $vendorDir . '/wpsyntex/polylang/include/walker-dropdown.php',
'PLL_Walker_List' => $vendorDir . '/wpsyntex/polylang/include/walker-list.php',
'PLL_Widget_Calendar' => $vendorDir . '/wpsyntex/polylang/include/widget-calendar.php',
'PLL_Widget_Editor_Language_Attribute' => $baseDir . '/modules/block-editor/widget-editor-language-attribute.php',
'PLL_Widget_Languages' => $vendorDir . '/wpsyntex/polylang/include/widget-languages.php',
'PLL_Wizard' => $vendorDir . '/wpsyntex/polylang/modules/wizard/wizard.php',
'PLL_Wizard_Pro' => $baseDir . '/modules/wizard/wizard-pro.php',
'PLL_WordPress_Importer' => $vendorDir . '/wpsyntex/polylang/integrations/wp-importer/wordpress-importer.php',
'PLL_Xdata_Base' => $baseDir . '/modules/xdata/xdata-base.php',
'PLL_Xdata_Domain' => $baseDir . '/modules/xdata/xdata-domain.php',
'PLL_Xdata_Session_Manager' => $baseDir . '/modules/xdata/xdata-session-manager.php',
'PLL_Xdata_Subdomain' => $baseDir . '/modules/xdata/xdata-subdomain.php',
'PLL_Xliff_Export_12' => $baseDir . '/modules/import-export/xliff/export/xliff-export-12.php',
'PLL_Xliff_Export_20' => $baseDir . '/modules/import-export/xliff/export/xliff-export-20.php',
'PLL_Xliff_Export_21' => $baseDir . '/modules/import-export/xliff/export/xliff-export-21.php',
'PLL_Xliff_Export_Base' => $baseDir . '/modules/import-export/xliff/export/xliff-export-base.php',
'PLL_Xliff_Format' => $baseDir . '/modules/import-export/xliff/xliff-format.php',
'PLL_Xliff_Import' => $baseDir . '/modules/import-export/xliff/xliff-import.php',
'PLL_Xliff_Import_Parser_12' => $baseDir . '/modules/import-export/xliff/xliff-import-parser-12.php',
'PLL_Xliff_Import_Parser_21' => $baseDir . '/modules/import-export/xliff/xliff-import-parser-21.php',
'PLL_Xliff_Import_Parser_Base' => $baseDir . '/modules/import-export/xliff/xliff-import-parser-base.php',
'PLL_Yarpp' => $vendorDir . '/wpsyntex/polylang/integrations/yarpp/yarpp.php',
'Polylang' => $vendorDir . '/wpsyntex/polylang/include/class-polylang.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Import_Export\\Services\\Context' => $baseDir . '/services/context.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Action' => $baseDir . '/modules/Machine_Translation/Action.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Button' => $baseDir . '/modules/Machine_Translation/Button.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Button_REST' => $baseDir . '/modules/Machine_Translation/Button_REST.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Clients\\Client_Interface' => $baseDir . '/modules/Machine_Translation/Clients/Client_Interface.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Clients\\Deepl' => $baseDir . '/modules/Machine_Translation/Clients/Deepl.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Data' => $baseDir . '/modules/Machine_Translation/Data.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Factory' => $baseDir . '/modules/Machine_Translation/Factory.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Module_Settings' => $baseDir . '/modules/Machine_Translation/Module_Settings.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Processor' => $baseDir . '/modules/Machine_Translation/Processor.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Services\\Deepl' => $baseDir . '/modules/Machine_Translation/Services/Deepl.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Services\\Service_Interface' => $baseDir . '/modules/Machine_Translation/Services/Service_Interface.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Settings\\Deepl' => $baseDir . '/modules/Machine_Translation/Settings/Deepl.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Settings\\Settings_Interface' => $baseDir . '/modules/Machine_Translation/Settings/Settings_Interface.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,38 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit8375818328e17ade60f265ce6346a9ae
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit8375818328e17ade60f265ce6346a9ae', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit8375818328e17ade60f265ce6346a9ae', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit8375818328e17ade60f265ce6346a9ae::getInitializer($loader));
$loader->register(true);
return $loader;
}
}

View File

@@ -0,0 +1,294 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit8375818328e17ade60f265ce6346a9ae
{
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'PLL_ACF' => __DIR__ . '/../..' . '/integrations/acf/acf.php',
'PLL_ACF_Auto_Translate' => __DIR__ . '/../..' . '/integrations/acf/acf-auto-translate.php',
'PLL_ACF_Sync_Metas' => __DIR__ . '/../..' . '/integrations/acf/acf-sync-metas.php',
'PLL_AS3CF' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/wp-offload-media/as3cf.php',
'PLL_Abstract_Language_Switcher_Block' => __DIR__ . '/../..' . '/modules/block-editor/abstract-language-switcher-block.php',
'PLL_Abstract_Sitemaps' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sitemaps/abstract-sitemaps.php',
'PLL_Accept_Language' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/accept-language.php',
'PLL_Accept_Languages_Collection' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/accept-languages-collection.php',
'PLL_Active_Languages' => __DIR__ . '/../..' . '/modules/active-languages/active-languages.php',
'PLL_Admin' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin.php',
'PLL_Admin_Advanced_Media' => __DIR__ . '/../..' . '/modules/media/admin-advanced-media.php',
'PLL_Admin_Base' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-base.php',
'PLL_Admin_Block_Editor' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-block-editor.php',
'PLL_Admin_Classic_Editor' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-classic-editor.php',
'PLL_Admin_Default_Term' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-default-term.php',
'PLL_Admin_Filters' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-filters.php',
'PLL_Admin_Filters_Columns' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-filters-columns.php',
'PLL_Admin_Filters_Media' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-filters-media.php',
'PLL_Admin_Filters_Post' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-filters-post.php',
'PLL_Admin_Filters_Post_Base' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-filters-post-base.php',
'PLL_Admin_Filters_Term' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-filters-term.php',
'PLL_Admin_Filters_Widgets_Options' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-filters-widgets-options.php',
'PLL_Admin_Links' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-links.php',
'PLL_Admin_Loader' => __DIR__ . '/../..' . '/services/admin-loader/admin-loader.php',
'PLL_Admin_Model' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-model.php',
'PLL_Admin_Nav_Menu' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-nav-menu.php',
'PLL_Admin_Notices' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-notices.php',
'PLL_Admin_Site_Health' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/site-health/admin-site-health.php',
'PLL_Admin_Static_Pages' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-static-pages.php',
'PLL_Admin_Strings' => __DIR__ . '/..' . '/wpsyntex/polylang/admin/admin-strings.php',
'PLL_Admin_Sync' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sync/admin-sync.php',
'PLL_Aqua_Resizer' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/aqua-resizer/aqua-resizer.php',
'PLL_Base' => __DIR__ . '/..' . '/wpsyntex/polylang/include/base.php',
'PLL_Block_Editor_Plugin' => __DIR__ . '/../..' . '/modules/block-editor/block-editor-plugin.php',
'PLL_Bulk_Translate' => __DIR__ . '/../..' . '/modules/bulk-translate/bulk-translate.php',
'PLL_Bulk_Translate_Option' => __DIR__ . '/../..' . '/modules/bulk-translate/bulk-translate-option.php',
'PLL_CPAC' => __DIR__ . '/../..' . '/integrations/admin-columns/cpac.php',
'PLL_CPTUI' => __DIR__ . '/../..' . '/integrations/cptui/cptui.php',
'PLL_CRUD_Posts' => __DIR__ . '/..' . '/wpsyntex/polylang/include/crud-posts.php',
'PLL_CRUD_Terms' => __DIR__ . '/..' . '/wpsyntex/polylang/include/crud-terms.php',
'PLL_Cache' => __DIR__ . '/..' . '/wpsyntex/polylang/include/cache.php',
'PLL_Cache_Compat' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/cache/cache-compat.php',
'PLL_Canonical' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/canonical.php',
'PLL_Cft' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/custom-field-template/cft.php',
'PLL_Choose_Lang' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/choose-lang.php',
'PLL_Choose_Lang_Content' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/choose-lang-content.php',
'PLL_Choose_Lang_Domain' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/choose-lang-domain.php',
'PLL_Choose_Lang_Url' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/choose-lang-url.php',
'PLL_Collect_Linked_Posts' => __DIR__ . '/../..' . '/services/collect-linked-objects/collect-linked-posts.php',
'PLL_Collect_Linked_Terms' => __DIR__ . '/../..' . '/services/collect-linked-objects/collect-linked-terms.php',
'PLL_Content_Blocks' => __DIR__ . '/../..' . '/integrations/content-blocks/content-blocks.php',
'PLL_Cookie' => __DIR__ . '/..' . '/wpsyntex/polylang/include/cookie.php',
'PLL_DOM_Content' => __DIR__ . '/../..' . '/services/dom/dom-content.php',
'PLL_DOM_Document' => __DIR__ . '/../..' . '/services/dom/dom-document.php',
'PLL_DOM_Nodes_Iterator' => __DIR__ . '/../..' . '/services/dom/dom-nodes-iterator.php',
'PLL_Data_Encoding' => __DIR__ . '/../..' . '/services/data-encoding.php',
'PLL_Db_Tools' => __DIR__ . '/..' . '/wpsyntex/polylang/include/db-tools.php',
'PLL_Divi_Builder' => __DIR__ . '/../..' . '/integrations/divi/divi-builder.php',
'PLL_Domain_Mapping' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/domain-mapping/domain-mapping.php',
'PLL_Duplicate' => __DIR__ . '/../..' . '/modules/duplicate/duplicate.php',
'PLL_Duplicate_Action' => __DIR__ . '/../..' . '/modules/duplicate/duplicate-action.php',
'PLL_Duplicate_Post' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/duplicate-post/duplicate-post.php',
'PLL_Duplicate_REST' => __DIR__ . '/../..' . '/modules/duplicate/duplicate-rest.php',
'PLL_Export_Bulk_Option' => __DIR__ . '/../..' . '/modules/import-export/export/export-bulk-option.php',
'PLL_Export_Container' => __DIR__ . '/../..' . '/services/exporter/export-container.php',
'PLL_Export_Data' => __DIR__ . '/../..' . '/services/exporter/export-data.php',
'PLL_Export_Data_From_Posts' => __DIR__ . '/../..' . '/services/exporter/export-data-from-posts.php',
'PLL_Export_Data_From_Strings' => __DIR__ . '/../..' . '/services/exporter/export-data-from-strings.php',
'PLL_Export_Download' => __DIR__ . '/../..' . '/modules/import-export/export/export-download.php',
'PLL_Export_File' => __DIR__ . '/../..' . '/modules/import-export/export/export-file.php',
'PLL_Export_Metas' => __DIR__ . '/../..' . '/services/exporter/export-metas.php',
'PLL_Export_Post_Metas' => __DIR__ . '/../..' . '/services/exporter/export-post-metas.php',
'PLL_Export_Posts' => __DIR__ . '/../..' . '/services/exporter/export-posts.php',
'PLL_Export_Strings' => __DIR__ . '/../..' . '/services/exporter/export-strings.php',
'PLL_Export_Strings_Action' => __DIR__ . '/../..' . '/modules/import-export/export/export-strings-action.php',
'PLL_Export_Term_Metas' => __DIR__ . '/../..' . '/services/exporter/export-term-metas.php',
'PLL_Export_Terms' => __DIR__ . '/../..' . '/services/exporter/export-terms.php',
'PLL_Export_Translated_Objects' => __DIR__ . '/../..' . '/services/exporter/export-translated-objects.php',
'PLL_FLBuilder' => __DIR__ . '/../..' . '/integrations/beaver-builder/flbuilder.php',
'PLL_FSE_Abstract_Bulk_Edit_Template_Slugs_Module' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-abstract-bulk-edit-template-slugs-module.php',
'PLL_FSE_Abstract_Module' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-abstract-module.php',
'PLL_FSE_Default_Language_Change' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-default-language-change.php',
'PLL_FSE_Filter_Block_Types' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-filter-block-types.php',
'PLL_FSE_Language' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-language.php',
'PLL_FSE_Language_Slug_Change' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-language-slug-change.php',
'PLL_FSE_Post_Deletion' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-post-deletion.php',
'PLL_FSE_Post_Types' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-post-types.php',
'PLL_FSE_Query_Filters' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-query-filters.php',
'PLL_FSE_REST_Duplicate_Template' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-rest-duplicate-template.php',
'PLL_FSE_REST_Enforce_Default_Template' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-rest-enforce-default-template.php',
'PLL_FSE_REST_Route' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-rest-route.php',
'PLL_FSE_REST_Template' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-rest-template.php',
'PLL_FSE_Recreate_Language' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-recreate-language.php',
'PLL_FSE_Template_Model' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-template-model.php',
'PLL_FSE_Template_Slug' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-template-slug.php',
'PLL_FSE_Template_Slug_Sync' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-template-slug-sync.php',
'PLL_FSE_Tools' => __DIR__ . '/../..' . '/modules/full-site-editing/fse-tools.php',
'PLL_Featured_Content' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/jetpack/featured-content.php',
'PLL_File_Format' => __DIR__ . '/../..' . '/modules/import-export/file-format/file-format.php',
'PLL_File_Format_Factory' => __DIR__ . '/../..' . '/modules/import-export/file-format/file-format-factory.php',
'PLL_Filter_REST_Routes' => __DIR__ . '/..' . '/wpsyntex/polylang/include/filter-rest-routes.php',
'PLL_Filters' => __DIR__ . '/..' . '/wpsyntex/polylang/include/filters.php',
'PLL_Filters_Links' => __DIR__ . '/..' . '/wpsyntex/polylang/include/filters-links.php',
'PLL_Filters_Sanitization' => __DIR__ . '/..' . '/wpsyntex/polylang/include/filters-sanitization.php',
'PLL_Filters_Widgets_Options' => __DIR__ . '/..' . '/wpsyntex/polylang/include/filters-widgets-options.php',
'PLL_Format_Util' => __DIR__ . '/../..' . '/include/format-util.php',
'PLL_Frontend' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend.php',
'PLL_Frontend_Auto_Translate' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-auto-translate.php',
'PLL_Frontend_Filters' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-filters.php',
'PLL_Frontend_Filters_Links' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-filters-links.php',
'PLL_Frontend_Filters_Search' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-filters-search.php',
'PLL_Frontend_Filters_Widgets' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-filters-widgets.php',
'PLL_Frontend_Filters_Widgets_Blocks' => __DIR__ . '/../..' . '/modules/block-editor/frontend-filters-widgets-blocks.php',
'PLL_Frontend_Links' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-links.php',
'PLL_Frontend_Nav_Menu' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-nav-menu.php',
'PLL_Frontend_Static_Pages' => __DIR__ . '/..' . '/wpsyntex/polylang/frontend/frontend-static-pages.php',
'PLL_Frontend_Translate_Slugs' => __DIR__ . '/../..' . '/modules/translate-slugs/frontend-translate-slugs.php',
'PLL_Import_Action' => __DIR__ . '/../..' . '/modules/import-export/import/import-action.php',
'PLL_Import_Export' => __DIR__ . '/../..' . '/modules/import-export/import-export.php',
'PLL_Import_File_Interface' => __DIR__ . '/../..' . '/modules/import-export/import/import-file-interface.php',
'PLL_Import_Object_Interface' => __DIR__ . '/../..' . '/modules/import-export/import/import-object-interface.php',
'PLL_Import_Posts' => __DIR__ . '/../..' . '/modules/import-export/import/import-posts.php',
'PLL_Import_Strings' => __DIR__ . '/../..' . '/modules/import-export/import/import-strings.php',
'PLL_Import_Terms' => __DIR__ . '/../..' . '/modules/import-export/import/import-terms.php',
'PLL_Install' => __DIR__ . '/..' . '/wpsyntex/polylang/install/install.php',
'PLL_Install_Base' => __DIR__ . '/..' . '/wpsyntex/polylang/install/install-base.php',
'PLL_Integrations' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/integrations.php',
'PLL_Jetpack' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/jetpack/jetpack.php',
'PLL_Language' => __DIR__ . '/..' . '/wpsyntex/polylang/include/language.php',
'PLL_Language_Deprecated' => __DIR__ . '/..' . '/wpsyntex/polylang/include/language-deprecated.php',
'PLL_Language_Factory' => __DIR__ . '/..' . '/wpsyntex/polylang/include/language-factory.php',
'PLL_Language_Switcher_Block' => __DIR__ . '/../..' . '/modules/block-editor/language-switcher-block.php',
'PLL_License' => __DIR__ . '/..' . '/wpsyntex/polylang/include/license.php',
'PLL_Links' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links.php',
'PLL_Links_Abstract_Domain' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links-abstract-domain.php',
'PLL_Links_Default' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links-default.php',
'PLL_Links_Directory' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links-directory.php',
'PLL_Links_Domain' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links-domain.php',
'PLL_Links_Model' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links-model.php',
'PLL_Links_Permalinks' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links-permalinks.php',
'PLL_Links_Subdomain' => __DIR__ . '/..' . '/wpsyntex/polylang/include/links-subdomain.php',
'PLL_Locale_Fallback' => __DIR__ . '/../..' . '/modules/locale-fallback/locale-fallback.php',
'PLL_MO' => __DIR__ . '/..' . '/wpsyntex/polylang/include/mo.php',
'PLL_Manage_User_Capabilities' => __DIR__ . '/../..' . '/services/manage-user-capabilities.php',
'PLL_Media_Bulk_Option' => __DIR__ . '/../..' . '/modules/media/media-bulk-option.php',
'PLL_Metabox_Button' => __DIR__ . '/../..' . '/services/metabox-button/metabox-button.php',
'PLL_Metabox_User_Button' => __DIR__ . '/../..' . '/services/metabox-button/metabox-user-button.php',
'PLL_Model' => __DIR__ . '/..' . '/wpsyntex/polylang/include/model.php',
'PLL_Module_Interface' => __DIR__ . '/../..' . '/modules/module-interface.php',
'PLL_Multilingual_Sitemaps_Provider' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sitemaps/multilingual-sitemaps-provider.php',
'PLL_Nav_Menu' => __DIR__ . '/..' . '/wpsyntex/polylang/include/nav-menu.php',
'PLL_Navigation_Language_Switcher_Block' => __DIR__ . '/../..' . '/modules/block-editor/navigation-language-switcher-block.php',
'PLL_No_Category_Base' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/no-category-base/no-category-base.php',
'PLL_OLT_Manager' => __DIR__ . '/..' . '/wpsyntex/polylang/include/olt-manager.php',
'PLL_PO_Export' => __DIR__ . '/../..' . '/modules/import-export/po/po-export.php',
'PLL_PO_Format' => __DIR__ . '/../..' . '/modules/import-export/po/po-format.php',
'PLL_PO_Import' => __DIR__ . '/../..' . '/modules/import-export/po/po-import.php',
'PLL_Plugin_Updater' => __DIR__ . '/..' . '/wpsyntex/polylang/install/plugin-updater.php',
'PLL_Pro' => __DIR__ . '/../..' . '/include/pro.php',
'PLL_Query' => __DIR__ . '/..' . '/wpsyntex/polylang/include/query.php',
'PLL_REST_API' => __DIR__ . '/../..' . '/modules/rest/rest-api.php',
'PLL_REST_Comment' => __DIR__ . '/../..' . '/modules/rest/rest-comment.php',
'PLL_REST_Filtered_Object' => __DIR__ . '/../..' . '/modules/rest/rest-filtered-object.php',
'PLL_REST_Post' => __DIR__ . '/../..' . '/modules/rest/rest-post.php',
'PLL_REST_Request' => __DIR__ . '/..' . '/wpsyntex/polylang/include/rest-request.php',
'PLL_REST_Term' => __DIR__ . '/../..' . '/modules/rest/rest-term.php',
'PLL_REST_Translated_Object' => __DIR__ . '/../..' . '/modules/rest/rest-translated-object.php',
'PLL_Settings' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/settings.php',
'PLL_Settings_Advanced_Media' => __DIR__ . '/../..' . '/modules/media/settings-advanced-media.php',
'PLL_Settings_Browser' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/settings-browser.php',
'PLL_Settings_CPT' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/settings-cpt.php',
'PLL_Settings_Licenses' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/settings-licenses.php',
'PLL_Settings_Media' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/settings-media.php',
'PLL_Settings_Module' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/settings-module.php',
'PLL_Settings_Preview_Machine_Translation' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/machine-translation/settings-preview-machine-translation.php',
'PLL_Settings_Preview_Share_Slug' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/share-slug/settings-preview-share-slug.php',
'PLL_Settings_Preview_Translate_Slugs' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/translate-slugs/settings-preview-translate-slugs.php',
'PLL_Settings_Share_Slug' => __DIR__ . '/../..' . '/modules/share-slug/settings-share-slug.php',
'PLL_Settings_Sync' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sync/settings-sync.php',
'PLL_Settings_Translate_Slugs' => __DIR__ . '/../..' . '/modules/translate-slugs/settings-translate-slugs.php',
'PLL_Settings_Url' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/settings-url.php',
'PLL_Share_Post_Slug' => __DIR__ . '/../..' . '/modules/share-slug/share-post-slug.php',
'PLL_Share_Term_Slug' => __DIR__ . '/../..' . '/modules/share-slug/share-term-slug.php',
'PLL_Sitemaps' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sitemaps/sitemaps.php',
'PLL_Sitemaps_Domain' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sitemaps/sitemaps-domain.php',
'PLL_Static_Pages' => __DIR__ . '/..' . '/wpsyntex/polylang/include/static-pages.php',
'PLL_Switcher' => __DIR__ . '/..' . '/wpsyntex/polylang/include/switcher.php',
'PLL_Sync' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sync/sync.php',
'PLL_Sync_Content' => __DIR__ . '/../..' . '/modules/sync/sync-content.php',
'PLL_Sync_Metas' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sync/sync-metas.php',
'PLL_Sync_Navigation' => __DIR__ . '/../..' . '/modules/sync/sync-navigation.php',
'PLL_Sync_Post' => __DIR__ . '/../..' . '/modules/sync-post/sync-post.php',
'PLL_Sync_Post_Bulk_Option' => __DIR__ . '/../..' . '/modules/sync-post/sync-post-bulk-option.php',
'PLL_Sync_Post_Button' => __DIR__ . '/../..' . '/modules/sync-post/sync-post-button.php',
'PLL_Sync_Post_Metas' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sync/sync-post-metas.php',
'PLL_Sync_Post_Model' => __DIR__ . '/../..' . '/modules/sync-post/sync-post-model.php',
'PLL_Sync_Post_REST' => __DIR__ . '/../..' . '/modules/sync-post/sync-post-rest.php',
'PLL_Sync_Tax' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sync/sync-tax.php',
'PLL_Sync_Term_Metas' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/sync/sync-term-metas.php',
'PLL_T15S' => __DIR__ . '/..' . '/wpsyntex/polylang/install/t15s.php',
'PLL_TEC' => __DIR__ . '/../..' . '/integrations/events-calendar/tec.php',
'PLL_Table_Languages' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/table-languages.php',
'PLL_Table_Settings' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/table-settings.php',
'PLL_Table_String' => __DIR__ . '/..' . '/wpsyntex/polylang/settings/table-string.php',
'PLL_Toggle_User_Meta' => __DIR__ . '/../..' . '/services/metabox-button/toggle-user-meta.php',
'PLL_Translatable_Object' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translatable-object.php',
'PLL_Translatable_Object_With_Types_Interface' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translatable-object-with-types-interface.php',
'PLL_Translatable_Object_With_Types_Trait' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translatable-object-with-types-trait.php',
'PLL_Translatable_Objects' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translatable-objects.php',
'PLL_Translate_Option' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translate-option.php',
'PLL_Translate_Slugs' => __DIR__ . '/../..' . '/modules/translate-slugs/translate-slugs.php',
'PLL_Translate_Slugs_Model' => __DIR__ . '/../..' . '/modules/translate-slugs/translate-slugs-model.php',
'PLL_Translated_Object' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translated-object.php',
'PLL_Translated_Post' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translated-post.php',
'PLL_Translated_Term' => __DIR__ . '/..' . '/wpsyntex/polylang/include/translated-term.php',
'PLL_Translation_Block_Parsing_Rules' => __DIR__ . '/../..' . '/services/translation/translation-block-parsing-rules.php',
'PLL_Translation_Content' => __DIR__ . '/../..' . '/services/translation/translation-content.php',
'PLL_Translation_Metas' => __DIR__ . '/../..' . '/services/translation/translation-metas.php',
'PLL_Translation_Object_Model_Interface' => __DIR__ . '/../..' . '/services/translation/translation-object-model-interface.php',
'PLL_Translation_Post_Metas' => __DIR__ . '/../..' . '/services/translation/translation-post-metas.php',
'PLL_Translation_Post_Model' => __DIR__ . '/../..' . '/services/translation/translation-post-model.php',
'PLL_Translation_Term_Metas' => __DIR__ . '/../..' . '/services/translation/translation-term-metas.php',
'PLL_Translation_Term_Model' => __DIR__ . '/../..' . '/services/translation/translation-term-model.php',
'PLL_Translation_Walker_Blocks' => __DIR__ . '/../..' . '/services/translation/translation-walker-blocks.php',
'PLL_Translation_Walker_Classic' => __DIR__ . '/../..' . '/services/translation/translation-walker-classic.php',
'PLL_Translation_Walker_Factory' => __DIR__ . '/../..' . '/services/translation/translation-walker-factory.php',
'PLL_Translation_Walker_Interface' => __DIR__ . '/../..' . '/services/translation/translation-walker-interface.php',
'PLL_Twenty_Seventeen' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/twenty-seventeen/twenty-seven-teen.php',
'PLL_Upgrade' => __DIR__ . '/..' . '/wpsyntex/polylang/install/upgrade.php',
'PLL_WPML_API' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/wpml/wpml-api.php',
'PLL_WPML_Compat' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/wpml/wpml-compat.php',
'PLL_WPML_Config' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/wpml/wpml-config.php',
'PLL_WPSEO' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/wpseo/wpseo.php',
'PLL_WPSEO_OGP' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/wpseo/wpseo-ogp.php',
'PLL_WP_Import' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/wp-importer/wp-import.php',
'PLL_WP_Sweep' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/wp-sweep/wp-sweep.php',
'PLL_Walker' => __DIR__ . '/..' . '/wpsyntex/polylang/include/walker.php',
'PLL_Walker_Dropdown' => __DIR__ . '/..' . '/wpsyntex/polylang/include/walker-dropdown.php',
'PLL_Walker_List' => __DIR__ . '/..' . '/wpsyntex/polylang/include/walker-list.php',
'PLL_Widget_Calendar' => __DIR__ . '/..' . '/wpsyntex/polylang/include/widget-calendar.php',
'PLL_Widget_Editor_Language_Attribute' => __DIR__ . '/../..' . '/modules/block-editor/widget-editor-language-attribute.php',
'PLL_Widget_Languages' => __DIR__ . '/..' . '/wpsyntex/polylang/include/widget-languages.php',
'PLL_Wizard' => __DIR__ . '/..' . '/wpsyntex/polylang/modules/wizard/wizard.php',
'PLL_Wizard_Pro' => __DIR__ . '/../..' . '/modules/wizard/wizard-pro.php',
'PLL_WordPress_Importer' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/wp-importer/wordpress-importer.php',
'PLL_Xdata_Base' => __DIR__ . '/../..' . '/modules/xdata/xdata-base.php',
'PLL_Xdata_Domain' => __DIR__ . '/../..' . '/modules/xdata/xdata-domain.php',
'PLL_Xdata_Session_Manager' => __DIR__ . '/../..' . '/modules/xdata/xdata-session-manager.php',
'PLL_Xdata_Subdomain' => __DIR__ . '/../..' . '/modules/xdata/xdata-subdomain.php',
'PLL_Xliff_Export_12' => __DIR__ . '/../..' . '/modules/import-export/xliff/export/xliff-export-12.php',
'PLL_Xliff_Export_20' => __DIR__ . '/../..' . '/modules/import-export/xliff/export/xliff-export-20.php',
'PLL_Xliff_Export_21' => __DIR__ . '/../..' . '/modules/import-export/xliff/export/xliff-export-21.php',
'PLL_Xliff_Export_Base' => __DIR__ . '/../..' . '/modules/import-export/xliff/export/xliff-export-base.php',
'PLL_Xliff_Format' => __DIR__ . '/../..' . '/modules/import-export/xliff/xliff-format.php',
'PLL_Xliff_Import' => __DIR__ . '/../..' . '/modules/import-export/xliff/xliff-import.php',
'PLL_Xliff_Import_Parser_12' => __DIR__ . '/../..' . '/modules/import-export/xliff/xliff-import-parser-12.php',
'PLL_Xliff_Import_Parser_21' => __DIR__ . '/../..' . '/modules/import-export/xliff/xliff-import-parser-21.php',
'PLL_Xliff_Import_Parser_Base' => __DIR__ . '/../..' . '/modules/import-export/xliff/xliff-import-parser-base.php',
'PLL_Yarpp' => __DIR__ . '/..' . '/wpsyntex/polylang/integrations/yarpp/yarpp.php',
'Polylang' => __DIR__ . '/..' . '/wpsyntex/polylang/include/class-polylang.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Import_Export\\Services\\Context' => __DIR__ . '/../..' . '/services/context.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Action' => __DIR__ . '/../..' . '/modules/Machine_Translation/Action.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Button' => __DIR__ . '/../..' . '/modules/Machine_Translation/Button.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Button_REST' => __DIR__ . '/../..' . '/modules/Machine_Translation/Button_REST.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Clients\\Client_Interface' => __DIR__ . '/../..' . '/modules/Machine_Translation/Clients/Client_Interface.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Clients\\Deepl' => __DIR__ . '/../..' . '/modules/Machine_Translation/Clients/Deepl.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Data' => __DIR__ . '/../..' . '/modules/Machine_Translation/Data.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Factory' => __DIR__ . '/../..' . '/modules/Machine_Translation/Factory.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Module_Settings' => __DIR__ . '/../..' . '/modules/Machine_Translation/Module_Settings.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Processor' => __DIR__ . '/../..' . '/modules/Machine_Translation/Processor.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Services\\Deepl' => __DIR__ . '/../..' . '/modules/Machine_Translation/Services/Deepl.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Services\\Service_Interface' => __DIR__ . '/../..' . '/modules/Machine_Translation/Services/Service_Interface.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Settings\\Deepl' => __DIR__ . '/../..' . '/modules/Machine_Translation/Settings/Deepl.php',
'WP_Syntex\\Polylang_Pro\\Modules\\Machine_Translation\\Settings\\Settings_Interface' => __DIR__ . '/../..' . '/modules/Machine_Translation/Settings/Settings_Interface.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->classMap = ComposerStaticInit8375818328e17ade60f265ce6346a9ae::$classMap;
}, null, ClassLoader::class);
}
}

View File

@@ -0,0 +1,59 @@
{
"packages": [
{
"name": "wpsyntex/polylang",
"version": "3.6.x-dev",
"version_normalized": "3.6.9999999.9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/polylang/polylang.git",
"reference": "803654e02a6f2e018f832efd4ef660b433d6249a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/polylang/polylang/zipball/803654e02a6f2e018f832efd4ef660b433d6249a",
"reference": "803654e02a6f2e018f832efd4ef660b433d6249a",
"shasum": ""
},
"require": {
"php": ">=7.0"
},
"require-dev": {
"automattic/vipwpcs": "*",
"behat/behat": "^3.7|^3.8",
"dealerdirect/phpcodesniffer-composer-installer": "*",
"phpcompatibility/phpcompatibility-wp": "*",
"wp-coding-standards/wpcs": "*",
"wpsyntex/polylang-phpstan": "^1.0",
"yoast/wp-test-utils": "^1.0.0"
},
"time": "2025-01-13T08:53:41+00:00",
"type": "wordpress-plugin",
"installation-source": "dist",
"autoload": {
"classmap": [
"admin/",
"frontend/",
"include/",
"integrations/",
"install/",
"modules/",
"settings/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-3.0-or-later"
],
"description": "Making WordPress multilingual.",
"homepage": "https://polylang.pro",
"support": {
"issues": "https://github.com/polylang/polylang/issues",
"source": "https://github.com/polylang/polylang/tree/3.6.x"
},
"install-path": "../wpsyntex/polylang"
}
],
"dev": false,
"dev-package-names": []
}

View File

@@ -0,0 +1,32 @@
<?php return array(
'root' => array(
'name' => 'wpsyntex/polylang-pro',
'pretty_version' => '3.6.x-dev',
'version' => '3.6.9999999.9999999-dev',
'reference' => '12dc783720ba8cadc49c7146db46a12ef4517855',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => false,
),
'versions' => array(
'wpsyntex/polylang' => array(
'pretty_version' => '3.6.x-dev',
'version' => '3.6.9999999.9999999-dev',
'reference' => '803654e02a6f2e018f832efd4ef660b433d6249a',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../wpsyntex/polylang',
'aliases' => array(),
'dev_requirement' => false,
),
'wpsyntex/polylang-pro' => array(
'pretty_version' => '3.6.x-dev',
'version' => '3.6.9999999.9999999-dev',
'reference' => '12dc783720ba8cadc49c7146db46a12ef4517855',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
),
);

View File

@@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 70000)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.0.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View File

@@ -0,0 +1,575 @@
<?php
/**
* @package Polylang
*/
/**
* Setup features available on all admin pages.
*
* @since 1.8
*/
abstract class PLL_Admin_Base extends PLL_Base {
/**
* 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' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'admin_print_footer_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 the link to the languages panel in the WordPress admin menu
*
* @since 0.1
*
* @return void
*/
public function add_menus() {
global $admin_page_hooks;
// Prepare the list of tabs
$tabs = array( 'lang' => __( 'Languages', 'polylang' ) );
// Only if at least one language has been created
if ( $this->model->has_languages() ) {
$tabs['strings'] = __( 'Translations', 'polylang' );
}
$tabs['settings'] = __( 'Settings', 'polylang' );
/**
* Filter the list of tabs in Polylang settings
*
* @since 1.5.1
*
* @param array $tabs list of tab names
*/
$tabs = apply_filters( 'pll_settings_tabs', $tabs );
$parent = '';
foreach ( $tabs as $tab => $title ) {
$page = 'lang' === $tab ? 'mlang' : "mlang_$tab";
if ( empty( $parent ) ) {
$parent = $page;
add_menu_page( $title, __( 'Languages', 'polylang' ), 'manage_options', $page, '__return_null', 'dashicons-translation' );
$admin_page_hooks[ $page ] = 'languages'; // Hack to avoid the localization of the hook name. See: https://core.trac.wordpress.org/ticket/18857
}
add_submenu_page( $parent, $title, $title, 'manage_options', $page, array( $this, 'languages_page' ) );
}
}
/**
* Setup js scripts & css styles ( only on the relevant pages )
*
* @since 0.6
*
* @return void
*/
public function admin_enqueue_scripts() {
$screen = get_current_screen();
if ( empty( $screen ) ) {
return;
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
/*
* 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(
'user' => array( array( 'profile', 'user-edit' ), array( 'jquery' ), false, false ),
'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', 'upload' ), 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->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' ),
)
);
}
}
/**
* Sets pll_ajax_backend on all backend ajax request
* 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 1.4
*
* @return void
*/
public function admin_print_footer_scripts() {
global $post_ID, $tag_ID;
$params = array( 'pll_ajax_backend' => 1 );
if ( ! empty( $post_ID ) ) {
$params = array_merge( $params, array( 'pll_post_id' => (int) $post_ID ) );
}
if ( ! empty( $tag_ID ) ) {
$params = array_merge( $params, array( 'pll_term_id' => (int) $tag_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.
*/
$params = apply_filters( 'pll_admin_ajax_params', $params );
$str = http_build_query( $params );
$arr = wp_json_encode( $params );
?>
<script>
if (typeof jQuery != 'undefined') {
jQuery(
function( $ ){
$.ajaxPrefilter( function ( options, originalOptions, jqXHR ) {
if ( -1 != options.url.indexOf( ajaxurl ) || -1 != ajaxurl.indexOf( options.url ) ) {
function addPolylangParametersAsString() {
if ( 'undefined' === typeof options.data || '' === options.data.trim() ) {
// Only Polylang data need to be send. So it could be as a simple query string.
options.data = '<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>';
} else {
/*
* In some cases data could be a JSON string like in third party plugins.
* So we need not to break their process by adding polylang parameters as valid JSON data.
*/
try {
options.data = JSON.stringify( Object.assign( JSON.parse( options.data ), <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?> ) );
} catch( exception ) {
// Add Polylang data to the existing query string.
options.data = options.data + '&<?php echo $str; // phpcs:ignore WordPress.Security.EscapeOutput ?>';
}
}
}
/*
* options.processData set to true is the default jQuery process where the data is converted in a query string by using jQuery.param().
* This step is done before applying filters. Thus here the options.data is already a string in this case.
* @See https://github.com/jquery/jquery/blob/3.5.1/src/ajax.js#L563-L569 jQuery ajax function.
* It is the most case WordPress send ajax request this way however third party plugins or themes could be send JSON string.
* Use JSON format is recommended in jQuery.param() documentation to be able to send complex data structures.
* @See https://api.jquery.com/jquery.param/ jQuery param function.
*/
if ( options.processData ) {
addPolylangParametersAsString();
} else {
/*
* If options.processData is set to false data could be undefined or pass as a string.
* So data as to be processed as if options.processData is set to true.
*/
if ( 'undefined' === typeof options.data || 'string' === typeof options.data ) {
addPolylangParametersAsString();
} else {
// Otherwise options.data is probably an object.
options.data = Object.assign( options.data || {} , <?php echo $arr; // phpcs:ignore WordPress.Security.EscapeOutput ?> );
}
}
}
});
}
);
}
</script>
<?php
}
/**
* 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 frontend/choose-lang.php */
do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
} else {
/** This action is documented in include/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() {
// Language for admin language filter: may be empty
// $_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'] ) ) && current_user_can( 'edit_user', $user_id = get_current_user_id() ) ) { // phpcs:ignore WordPress.Security.NonceVerification
update_user_meta( $user_id, 'pll_filter_content', ( $lang = $this->model->get_language( sanitize_key( $_GET['lang'] ) ) ) ? $lang->slug : '' ); // phpcs:ignore WordPress.Security.NonceVerification
}
$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 )
);
/**
* Filters the admin languages filter submenu items
*
* @since 2.6
*
* @param array $items The admin languages filter submenu items.
*/
$items = apply_filters( 'pll_admin_languages_filter', array_merge( array( $all_item ), $this->model->get_languages_list() ) );
$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' ),
),
);
if ( 'all' !== $selected->slug ) {
$menu['meta']['class'] = 'pll-filtered-languages';
}
if ( ! empty( $items ) ) {
$wp_admin_bar->add_menu( $menu );
}
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] );
}
}
}
}
}

View File

@@ -0,0 +1,92 @@
<?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;
}
// Backward compatibility with WP < 6.0 where `WP_Block_Editor_Context::$name` doesn't exist yet.
if (
( property_exists( $context, 'name' ) && '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 );
}
}
}

View File

@@ -0,0 +1,371 @@
<?php
/**
* @package Polylang
*/
/**
* 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|null
*/
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
*
* @return void
*/
public function post_language() {
global $post_ID;
$post_type = get_post_type( $post_ID );
// phpcs:ignore WordPress.Security.NonceVerification, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$from_post_id = isset( $_GET['from_post'] ) ? (int) $_GET['from_post'] : 0;
$lang = ( $lg = $this->model->post->get_language( $post_ID ) ) ? $lg :
( isset( $_GET['new_lang'] ) ? $this->model->get_language( sanitize_key( $_GET['new_lang'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification
$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->get_languages_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
*/
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'];
$lang_slug = sanitize_key( $_POST['lang'] );
$lang = $this->model->get_language( $lang_slug );
$post_type = sanitize_key( $_POST['post_type'] );
if ( empty( $lang ) ) {
wp_die( esc_html( "{$lang_slug} is not a valid language code." ) );
}
$post_type_object = get_post_type_object( $post_type );
if ( empty( $post_type_object ) ) {
wp_die( esc_html( "{$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_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' => '&mdash; ' . $taxonomy->labels->parent_item . ' &mdash;',
'echo' => 0,
)
);
$x->Add( array( 'what' => 'taxonomy', 'data' => $taxonomy->name, 'supplemental' => $supplemental ) );
}
}
}
// Parent dropdown list ( only for hierarchical post types )
if ( in_array( $post_type, get_post_types( array( 'hierarchical' => true ) ) ) ) {
$post = get_post( $post_ID );
if ( ! empty( $post ) ) {
// 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->edit_post_translation_link( $post->ID ),
);
}
// 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->edit_post_translation_link( $post_id ),
)
);
}
}
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
}
}
}

View File

@@ -0,0 +1,264 @@
<?php
/**
* @package Polylang
*/
/**
* Manages filters and actions related to default terms.
*
* @since 3.1
*/
class PLL_Admin_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 $pref_lang;
/**
* 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->pref_lang = &$polylang->pref_lang;
$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 );
// Adds the language column in the 'Terms' table.
add_filter( 'manage_' . $taxonomy . '_custom_column', array( $this, 'term_column' ), 10, 3 );
}
}
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 in note below the term list table and in settings->writing dropdown
*
* @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->pref_lang ) && $tr = $this->model->term->get( $taxonomy_term_id, $this->pref_lang ) ) {
$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;
}
$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 );
}
}
}
/**
* Create a default term for a language
*
* @since 1.2
*
* @param object|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 = isset( $cat->error_data['term_exists'] ) ? $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 PLL_Admin_Model::add_language().
* @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 );
}
}
}
}
/**
* Identify the default term in the terms list table to disable the language dropdown in js.
*
* @since 3.1
*
* @param string $out The output.
* @param string $column The custom column's name.
* @param int $term_id The term id.
* @return string The HTML string.
*/
public function term_column( $out, $column, $term_id ) {
if ( $column === $this->get_first_language_column() && $this->is_default_term( $term_id ) ) {
$out .= sprintf( '<div class="hidden" id="default_cat_%1$d">%1$d</div>', intval( $term_id ) );
}
return $out;
}
/**
* Returns the first language column in the posts, pages and media library tables
*
* @since 0.9
*
* @return string first language column name
*/
protected function get_first_language_column() {
$columns = array();
foreach ( $this->model->get_languages_list() as $language ) {
$columns[] = 'language_' . $language->slug;
}
return empty( $columns ) ? '' : reset( $columns );
}
/**
* 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;
}
/**
* Check 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 ] );
}
}
}
}
}

View File

@@ -0,0 +1,451 @@
<?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;
/**
* @var PLL_Admin_Links|null
*/
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( $columns, $before ) {
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->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 the posts, pages and media library tables
*
* @since 0.9
*
* @return string first language column name
*/
protected function get_first_language_column() {
$columns = array();
foreach ( $this->model->get_languages_list() as $language ) {
$columns[] = 'language_' . $language->slug;
}
return empty( $columns ) ? '' : reset( $columns );
}
/**
* 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( $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 ) {
$inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
$lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->post->get_language( $post_id ); // phpcs:ignore WordPress.Security.NonceVerification
if ( false === strpos( $column, 'language_' ) || ! $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>', intval( $post_id ), esc_html( $lang->slug ) );
}
// Link to edit post ( or a translation )
if ( $id = $this->model->post->get( $post_id, $language ) ) {
// get_edit_post_link returns nothing if the user cannot edit the post
// Thanks to Solinx. See http://wordpress.org/support/topic/feature-request-incl-code-check-for-capabilities-in-admin-screens
if ( $link = get_edit_post_link( $id ) ) {
$flag = '';
if ( $id === $post_id ) {
$flag = $this->get_flag_html( $language );
$class = 'pll_column_flag';
/* translators: accessibility text, %s is a native language name */
$s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
} else {
$class = esc_attr( 'pll_icon_edit translation_' . $id );
/* translators: accessibility text, %s is a native language name */
$s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
}
$post = get_post( $id );
if ( ! empty( $post ) ) {
printf(
'<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
esc_attr( $class ),
esc_attr( $post->post_title ),
esc_url( $link ),
esc_html( $s ),
$flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
} elseif ( $id === $post_id ) {
printf(
'<span class="pll_column_flag" style=""><span class="screen-reader-text">%1$s</span>%2$s</span>',
/* translators: accessibility text, %s is a native language name */
esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) ),
$this->get_flag_html( $language ) // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
}
// Link to add a new translation
else {
echo $this->links->new_post_translation_link( $post_id, $language ); // phpcs:ignore WordPress.Security.EscapeOutput
}
}
/**
* 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() ) {
$elements = $this->model->get_languages_list();
if ( current_filter() == 'bulk_edit_custom_box' ) {
array_unshift( $elements, (object) array( 'slug' => -1, 'name' => __( '&mdash; No Change &mdash;', 'polylang' ) ) );
}
$dropdown = new PLL_Walker_Dropdown();
// The hidden field 'old_lang' allows to pass the old language to ajax request
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( $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 ) {
$inline = wp_doing_ajax() && isset( $_REQUEST['action'], $_POST['inline_lang_choice'] ) && 'inline-save-tax' === $_REQUEST['action']; // phpcs:ignore WordPress.Security.NonceVerification
if ( false === strpos( $column, 'language_' ) || ! ( $lang = $inline ? $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) ) : $this->model->term->get_language( $term_id ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
return $out;
}
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( $_REQUEST['taxonomy'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
$taxonomy = sanitize_key( $_REQUEST['taxonomy'] ); // phpcs:ignore WordPress.Security.NonceVerification
}
if ( isset( $GLOBALS['taxonomy'] ) ) {
$taxonomy = $GLOBALS['taxonomy'];
}
if ( ! isset( $taxonomy, $post_type ) || ! post_type_exists( $post_type ) || ! taxonomy_exists( $taxonomy ) ) {
return $out;
}
$term_id = (int) $term_id;
$language = $this->model->get_language( substr( $column, 9 ) );
if ( empty( $language ) ) {
return $out;
}
if ( $column == $this->get_first_language_column() ) {
$out .= sprintf( '<div class="hidden" id="lang_%d">%s</div>', intval( $term_id ), esc_html( $lang->slug ) );
}
// Link to edit term ( or a translation )
if ( ( $id = $this->model->term->get( $term_id, $language ) ) && $term = get_term( $id, $taxonomy ) ) {
if ( $term instanceof WP_Term && $link = get_edit_term_link( $id, $taxonomy, $post_type ) ) {
$flag = '';
if ( $id === $term_id ) {
$flag = $this->get_flag_html( $language );
$class = 'pll_column_flag';
/* translators: accessibility text, %s is a native language name */
$s = sprintf( __( 'Edit this item in %s', 'polylang' ), $language->name );
} else {
$class = esc_attr( 'pll_icon_edit translation_' . $id );
/* translators: accessibility text, %s is a native language name */
$s = sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name );
}
$out .= sprintf(
'<a class="%1$s" title="%2$s" href="%3$s"><span class="screen-reader-text">%4$s</span>%5$s</a>',
$class,
esc_attr( $term->name ),
esc_url( $link ),
esc_html( $s ),
$flag // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
);
} elseif ( $id === $term_id ) {
$out .= sprintf(
'<span class="pll_column_flag"><span class="screen-reader-text">%1$s</span>%2$s</span>',
/* translators: accessibility text, %s is a native language name */
esc_html( sprintf( __( 'This item is in %s', 'polylang' ), $language->name ) ),
$this->get_flag_html( $language ) // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
}
// Link to add a new translation
else {
$out .= $this->links->new_term_translation_link( $term_id, $taxonomy, $post_type, $language );
}
return $out;
}
/**
* Update rows of translated posts when the language is modified in quick edit
*
* @since 1.7
*
* @return void
*/
public function ajax_update_post_rows() {
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 = 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
*/
public function ajax_update_term_rows() {
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 = ob_get_clean();
$x->add( array( 'what' => 'row', 'data' => $data, 'supplemental' => array( 'term_id' => $term_id ) ) );
}
$x->send();
}
/**
* Returns the language flag or the language slug if there is no flag.
*
* @since 2.8
*
* @param PLL_Language $language PLL_Language object.
* @return string
*/
protected function get_flag_html( $language ) {
return $language->flag ? $language->flag : sprintf( '<abbr>%s</abbr>', esc_html( $language->slug ) );
}
}

View File

@@ -0,0 +1,132 @@
<?php
/**
* @package Polylang
*/
/**
* 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 );
// Creates a media translation
if ( isset( $_GET['action'], $_GET['new_lang'], $_GET['from_media'] ) && 'translate_media' === $_GET['action'] ) { // phpcs:ignore WordPress.Security.NonceVerification
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 for WP 3.5+ since we have the metabox
}
$post_id = $post->ID;
$lang = $this->model->post->get_language( $post_id );
$dropdown = new PLL_Walker_Dropdown();
$fields['language'] = array(
'label' => __( 'Language', 'polylang' ),
'input' => 'html',
'html' => $dropdown->walk(
$this->model->get_languages_list(),
-1,
array(
'name' => sprintf( 'attachments[%d][language]', $post_id ),
'class' => 'media_lang_choice',
'selected' => $lang ? $lang->slug : '',
)
),
);
return $fields;
}
/**
* Creates a media translation
*
* @since 0.9
*
* @return void
*/
public function translate_media() {
if ( isset( $_GET['from_media'], $_GET['new_lang'] ) ) {
// Security check
check_admin_referer( 'translate_media' );
$post_id = (int) $_GET['from_media'];
// Bails if the translations already exists
// See https://wordpress.org/support/topic/edit-translation-in-media-attachments?#post-7322303
// Or if the source media does not exist
if ( $this->model->post->get_translation( $post_id, sanitize_key( $_GET['new_lang'] ) ) || ! get_post( $post_id ) ) {
wp_safe_redirect( wp_get_referer() );
exit;
}
$tr_id = $this->posts->create_media_translation( $post_id, sanitize_key( $_GET['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;
}
$this->model->post->set_language( $post['ID'], $language );
return $post;
}
}

View File

@@ -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_Links|null
*/
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;
}
}

View File

@@ -0,0 +1,223 @@
<?php
/**
* @package Polylang
*/
/**
* 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'] ) && $post_id = (int) $_POST['post_ID'] ) { // phpcs:ignore WordPress.Security.NonceVerification
check_admin_referer( 'pll_language', '_pll_nonce' );
$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;
}
if ( ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) {
return;
}
$language = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) );
if ( empty( $language ) ) {
return;
}
$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 a post
*
* @since 2.3
*
* @return void
*/
public function bulk_edit_posts() {
if ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'], $_REQUEST['post'] ) && -1 !== $_GET['inline_lang_choice'] ) { // phpcs:ignore WordPress.Security.NonceVerification
check_admin_referer( 'bulk-posts' );
if ( $lang = $this->model->get_language( sanitize_key( $_GET['inline_lang_choice'] ) ) ) {
$post_ids = array_map( 'intval', (array) $_REQUEST['post'] );
foreach ( $post_ids as $post_id ) {
if ( current_user_can( 'edit_post', $post_id ) ) {
$this->model->post->set_language( $post_id, $lang );
}
}
}
}
}
/**
* Save language when inline editing a post
*
* @since 2.3
*
* @return void
*/
public function inline_edit_post() {
check_admin_referer( 'inlineeditnonce', '_inline_edit' );
if ( isset( $_POST['post_ID'], $_POST['inline_lang_choice'] ) ) {
$post_id = (int) $_POST['post_ID'];
$lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) );
if ( $post_id && $lang && current_user_can( 'edit_post', $post_id ) ) {
$this->model->post->set_language( $post_id, $lang );
}
}
}
/**
* 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;
}
}

View File

@@ -0,0 +1,716 @@
<?php
/**
* @package Polylang
*/
/**
* 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|null
*/
public $links;
/**
* Language selected in the admin language filter.
*
* @var PLL_Language|null
*/
public $filter_lang;
/**
* Preferred language to assign to the new terms.
*
* @var PLL_Language|null
*/
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->get_languages_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 $tag The term being edited.
* @return void
*/
public function edit_term_form( $tag ) {
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;
}
$term_id = $tag->term_id;
$taxonomy = $tag->taxonomy; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$lang = $this->model->term->get_language( $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_id );
$dropdown = new PLL_Walker_Dropdown();
$dropdown_html = $dropdown->walk(
$this->model->get_languages_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 Term ID.
* @param string $taxonomy Taxonomy name.
* @return void
*/
protected function save_language( $term_id, $taxonomy ) {
global $wpdb;
// Security checks are necessary to accept language modifications
// as 'wp_update_term' can be called from outside WP admin
// Edit tags
if ( isset( $_POST['term_lang_choice'] ) ) {
if ( isset( $_POST['action'] ) && sanitize_key( $_POST['action'] ) === 'add-' . $taxonomy ) { // phpcs:ignore WordPress.Security.NonceVerification
check_ajax_referer( 'add-' . $taxonomy, '_ajax_nonce-add-' . $taxonomy ); // Category metabox
} else {
check_admin_referer( 'pll_language', '_pll_nonce' ); // Edit tags or tags metabox
}
$language = $this->model->get_language( sanitize_key( $_POST['term_lang_choice'] ) );
if ( ! empty( $language ) ) {
$this->model->term->set_language( $term_id, $language );
}
}
// *Post* bulk edit, in case a new term is created
elseif ( isset( $_GET['bulk_edit'], $_GET['inline_lang_choice'] ) ) {
check_admin_referer( '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;
}
$this->model->term->set_language( $term_id, $language );
$term = get_term( $term_id, $taxonomy );
$terms = array();
// Get all terms with the same name
// FIXME backward compatibility WP < 4.2
// No WP function to get all terms with the exact same name so let's use a custom query
// $terms = get_terms( $taxonomy, array( 'name' => $term->name, 'hide_empty' => false, 'fields' => 'ids' ) ); should be OK in 4.2
// I may need to rework the loop below
if ( $term instanceof WP_Term ) {
$terms = $wpdb->get_results(
$wpdb->prepare(
"SELECT t.term_id FROM $wpdb->terms AS t
INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id
WHERE tt.taxonomy = %s AND t.name = %s",
$taxonomy,
$term->name
)
);
}
// If we have several terms with the same name, they are translations of each other
if ( count( $terms ) > 1 ) {
$translations = array();
foreach ( $terms as $term ) {
$translations[ $this->model->term->get_language( $term->term_id )->slug ] = $term->term_id;
}
$this->model->term->save_translations( $term_id, $translations );
}
}
elseif ( current_user_can( 'edit_term', $term_id ) ) {
$this->model->term->set_language( $term_id, $this->model->get_language( sanitize_key( $_GET['inline_lang_choice'] ) ) );
}
}
// Quick edit
elseif ( isset( $_POST['inline_lang_choice'] ) ) {
check_ajax_referer(
isset( $_POST['action'] ) && 'inline-save' == $_POST['action'] ? 'inlineeditnonce' : 'taxinlineeditnonce', // Post quick edit or tag quick edit ?
'_inline_edit'
);
$lang = $this->model->get_language( sanitize_key( $_POST['inline_lang_choice'] ) );
$this->model->term->set_language( $term_id, $lang );
}
// Edit post
elseif ( isset( $_POST['post_lang_choice'] ) ) { // FIXME should be useless now
check_admin_referer( 'pll_language', '_pll_nonce' );
$language = $this->model->get_language( sanitize_key( $_POST['post_lang_choice'] ) );
if ( ! empty( $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'] ) );
$term_id = isset( $_POST['term_id'] ) ? (int) $_POST['term_id'] : null; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$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 );
}
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->edit_term_translation_link( $term->term_id, $term->taxonomy, $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;
}
}

View File

@@ -0,0 +1,34 @@
<?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
*/
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 );
}
}
}

View File

@@ -0,0 +1,127 @@
<?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 object $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, 'personal_options' ) );
// 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 );
}
}
/**
* Outputs hidden information to modify the biography form with js.
*
* @since 0.4
*
* @param WP_User $profileuser The current WP_User object.
* @return void
*/
public function personal_options( $profileuser ) {
foreach ( $this->model->get_languages_list() as $lang ) {
$meta = $lang->is_default ? 'description' : 'description_' . $lang->slug;
$description = get_user_meta( $profileuser->ID, $meta, true );
printf(
'<input type="hidden" class="biography" name="%s___%s" value="%s" />',
esc_attr( $lang->slug ),
esc_attr( $lang->name ),
sanitize_user_field( 'description', $description, $profileuser->ID, 'edit' )
);
}
}
/**
* 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;
}
}

View File

@@ -0,0 +1,226 @@
<?php
/**
* @package Polylang
*/
/**
* Manages links related functions.
*
* @since 1.8
*/
class PLL_Admin_Links extends PLL_Links {
/**
* 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( $link, $language ) {
$str = '';
if ( $link ) {
/* translators: accessibility text, %s is a native language name */
$hint = sprintf( __( 'Add a translation in %s', 'polylang' ), $language->name );
$str = 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 )
);
}
return $str;
}
/**
* Returns the html markup for a translation link.
*
* @since 2.6
*
* @param string $link The translation link.
* @param PLL_Language $language The language of the translation.
* @return string
*/
public function edit_translation_link( $link, $language ) {
return $link ? sprintf(
'<a href="%1$s" class="pll_icon_edit"><span class="screen-reader-text">%2$s</span></a>',
esc_url( $link ),
/* translators: accessibility text, %s is a native language name */
esc_html( sprintf( __( 'Edit the translation in %s', 'polylang' ), $language->name ) )
) : '';
}
/**
* Get the link to create a new post translation.
*
* @since 1.5
*
* @param int $post_id The source post id.
* @param PLL_Language $language The language of the new translation.
* @param string $context Optional. Defaults to 'display' which encodes '&' to '&amp;'.
* Otherwise, preserves '&'.
* @return string
*/
public function get_new_post_translation_link( $post_id, $language, $context = 'display' ) {
$post_type = get_post_type( $post_id );
$post_type_object = get_post_type_object( get_post_type( $post_id ) );
if ( empty( $post_type_object ) || ! current_user_can( $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 && ! current_user_can( 'manage_privacy_options' ) ) {
$privacy_page = get_option( 'wp_page_for_privacy_policy' );
if ( $privacy_page && in_array( $post_id, $this->model->post->get_translations( $privacy_page ) ) ) {
return '';
}
}
if ( 'attachment' === $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_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 post translation link.
*
* @since 1.8
*
* @param int $post_id The source post id.
* @param PLL_Language $language The language of the new translation.
* @return string
*/
public function new_post_translation_link( $post_id, $language ) {
$link = $this->get_new_post_translation_link( $post_id, $language );
return $this->new_translation_link( $link, $language );
}
/**
* Returns the html markup for a post translation link.
*
* @since 1.4
*
* @param int $post_id The translation post id.
* @return string
*/
public function edit_post_translation_link( $post_id ) {
$link = get_edit_post_link( $post_id );
$language = $this->model->post->get_language( $post_id );
return $this->edit_translation_link( $link, $language );
}
/**
* Get the link to create a new term translation.
*
* @since 1.5
*
* @param int $term_id Source term id.
* @param string $taxonomy Taxonomy name.
* @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( $term_id, $taxonomy, $post_type, $language ) {
$tax = get_taxonomy( $taxonomy );
if ( ! $tax || ! current_user_can( $tax->cap->edit_terms ) ) {
return '';
}
$args = array(
'taxonomy' => $taxonomy,
'post_type' => $post_type,
'from_tag' => $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_id, $taxonomy, $post_type );
}
/**
* Returns the html markup for a new term translation.
*
* @since 1.8
*
* @param int $term_id Source term id.
* @param string $taxonomy Taxonomy name.
* @param string $post_type Post type name.
* @param PLL_Language $language The language of the new translation.
* @return string
*/
public function new_term_translation_link( $term_id, $taxonomy, $post_type, $language ) {
$link = $this->get_new_term_translation_link( $term_id, $taxonomy, $post_type, $language );
return $this->new_translation_link( $link, $language );
}
/**
* Returns the html markup for a term translation link.
*
* @since 1.4
*
* @param int $term_id Translation term id.
* @param string $taxonomy Taxonomy name.
* @param string $post_type Post type name.
* @return string
*/
public function edit_term_translation_link( $term_id, $taxonomy, $post_type ) {
$link = get_edit_term_link( $term_id, $taxonomy, $post_type );
$language = $this->model->term->get_language( $term_id );
return $this->edit_translation_link( $link, $language );
}
}

View File

@@ -0,0 +1,540 @@
<?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 {
/**
* Adds a new language
* and creates a default category for this language.
*
* @since 1.2
*
* @param array $args {
* @type string $name Language name (used only for display).
* @type string $slug Language code (ideally 2-letters ISO 639-1 language code).
* @type string $locale WordPress locale. If something wrong is used for the locale, the .mo files will
* not be loaded...
* @type int $rtl 1 if rtl language, 0 otherwise.
* @type int $term_group Language order when displayed.
* @type string $no_default_cat Optional, if set, no default category will be created for this language.
* @type string $flag Optional, country code, {@see settings/flags.php}.
* }
* @return WP_Error|true true if success / WP_Error if failed.
*/
public function add_language( $args ) {
$errors = $this->validate_lang( $args );
if ( $errors->has_errors() ) {
return $errors;
}
// First the language taxonomy
$r = wp_insert_term(
$args['name'],
'language',
array(
'slug' => $args['slug'],
'description' => $this->build_language_metas( $args ),
)
);
if ( is_wp_error( $r ) ) {
// Avoid an ugly fatal error if something went wrong ( reported once in the forum )
return new WP_Error( 'pll_add_language', __( 'Impossible to add the language.', 'polylang' ) );
}
wp_update_term( (int) $r['term_id'], 'language', array( 'term_group' => (int) $args['term_group'] ) ); // can't set the term group directly in wp_insert_term
// The other language taxonomies.
$this->update_secondary_language_terms( $args['slug'], $args['name'] );
if ( ! isset( $this->options['default_lang'] ) ) {
// If this is the first language created, set it as default language
$this->options['default_lang'] = $args['slug'];
update_option( 'polylang', $this->options );
}
// Refresh languages.
$this->clean_languages_cache();
$this->get_languages_list();
flush_rewrite_rules(); // Refresh rewrite rules.
/**
* Fires when a language is added.
*
* @since 1.9
*
* @param array $args Arguments used to create the language. @see PLL_Admin_Model::add_language().
*/
do_action( 'pll_add_language', $args );
return true;
}
/**
* Delete a language.
*
* @since 1.2
*
* @param int $lang_id Language term_id.
* @return bool
*/
public function delete_language( $lang_id ) {
$lang = $this->get_language( (int) $lang_id );
if ( empty( $lang ) ) {
return false;
}
// Oops ! we are deleting the default language...
// Need to do this before loosing the information for default category translations
if ( $lang->is_default ) {
$slugs = $this->get_languages_list( array( 'fields' => 'slug' ) );
$slugs = array_diff( $slugs, array( $lang->slug ) );
if ( ! empty( $slugs ) ) {
$this->update_default_lang( reset( $slugs ) ); // Arbitrary choice...
} else {
unset( $this->options['default_lang'] );
}
}
// Delete the translations
$this->update_translations( $lang->slug );
// Delete language option in widgets
foreach ( $GLOBALS['wp_registered_widgets'] as $widget ) {
if ( ! empty( $widget['callback'][0] ) && ! empty( $widget['params'][0]['number'] ) ) {
$obj = $widget['callback'][0];
$number = $widget['params'][0]['number'];
if ( is_object( $obj ) && method_exists( $obj, 'get_settings' ) && method_exists( $obj, 'save_settings' ) ) {
$settings = $obj->get_settings();
if ( isset( $settings[ $number ]['pll_lang'] ) && $settings[ $number ]['pll_lang'] == $lang->slug ) {
unset( $settings[ $number ]['pll_lang'] );
$obj->save_settings( $settings );
}
}
}
}
// Delete menus locations
if ( ! empty( $this->options['nav_menus'] ) ) {
foreach ( $this->options['nav_menus'] as $theme => $locations ) {
foreach ( array_keys( $locations ) as $location ) {
unset( $this->options['nav_menus'][ $theme ][ $location ][ $lang->slug ] );
}
}
}
// Delete users options
delete_metadata( 'user', 0, 'pll_filter_content', '', true );
delete_metadata( 'user', 0, "description_{$lang->slug}", '', true );
// Delete domain
unset( $this->options['domains'][ $lang->slug ] );
/*
* Delete the language itself.
*
* Reverses the language taxonomies order is required to make sure 'language' is deleted in last.
*
* The initial order with the 'language' taxonomy at the beginning of 'PLL_Language::term_props' property
* is done by {@see PLL_Model::filter_language_terms_orderby()}
*/
foreach ( array_reverse( $lang->get_tax_props( 'term_id' ) ) as $taxonomy_name => $term_id ) {
wp_delete_term( $term_id, $taxonomy_name );
}
// Refresh languages.
$this->clean_languages_cache();
$this->get_languages_list();
update_option( 'polylang', $this->options );
flush_rewrite_rules(); // refresh rewrite rules
return true;
}
/**
* Updates language properties.
*
* @since 1.2
*
* @param array $args {
* @type int $lang_id Id of the language to modify.
* @type string $name Language name ( used only for display ).
* @type string $slug Language code ( ideally 2-letters ISO 639-1 language code ).
* @type string $locale WordPress locale. If something wrong is used for the locale, the .mo files will not be loaded...
* @type int $rtl 1 if rtl language, 0 otherwise.
* @type int $term_group Language order when displayed.
* @type string $flag Optional, country code, @see flags.php.
* }
* @return WP_Error|true true if success / WP_Error if failed.
*/
public function update_language( $args ) {
$lang = $this->get_language( (int) $args['lang_id'] );
if ( empty( $lang ) ) {
return new WP_Error( 'pll_invalid_language_id', __( 'The language does not seem to exist.', 'polylang' ) );
}
$errors = $this->validate_lang( $args, $lang );
if ( $errors->get_error_code() ) { // Using has_errors() would be more meaningful but is available only since WP 5.0
return $errors;
}
// Update links to this language in posts and terms in case the slug has been modified
$slug = $args['slug'];
$old_slug = $lang->slug;
if ( $old_slug != $slug ) {
// Update the language slug in translations
$this->update_translations( $old_slug, $slug );
// Update language option in widgets
foreach ( $GLOBALS['wp_registered_widgets'] as $widget ) {
if ( ! empty( $widget['callback'][0] ) && ! empty( $widget['params'][0]['number'] ) ) {
$obj = $widget['callback'][0];
$number = $widget['params'][0]['number'];
if ( is_object( $obj ) && method_exists( $obj, 'get_settings' ) && method_exists( $obj, 'save_settings' ) ) {
$settings = $obj->get_settings();
if ( isset( $settings[ $number ]['pll_lang'] ) && $settings[ $number ]['pll_lang'] == $old_slug ) {
$settings[ $number ]['pll_lang'] = $slug;
$obj->save_settings( $settings );
}
}
}
}
// Update menus locations
if ( ! empty( $this->options['nav_menus'] ) ) {
foreach ( $this->options['nav_menus'] as $theme => $locations ) {
foreach ( array_keys( $locations ) as $location ) {
if ( ! empty( $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ] ) ) {
$this->options['nav_menus'][ $theme ][ $location ][ $slug ] = $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ];
unset( $this->options['nav_menus'][ $theme ][ $location ][ $old_slug ] );
}
}
}
}
// Update domains
if ( ! empty( $this->options['domains'][ $old_slug ] ) ) {
$this->options['domains'][ $slug ] = $this->options['domains'][ $old_slug ];
unset( $this->options['domains'][ $old_slug ] );
}
// Update the default language option if necessary
if ( $lang->is_default ) {
$this->options['default_lang'] = $slug;
}
}
update_option( 'polylang', $this->options );
// And finally update the language itself.
$this->update_secondary_language_terms( $args['slug'], $args['name'], $lang );
$description = $this->build_language_metas( $args );
wp_update_term( $lang->get_tax_prop( 'language', 'term_id' ), 'language', array( 'slug' => $slug, 'name' => $args['name'], 'description' => $description, 'term_group' => (int) $args['term_group'] ) );
// Refresh languages.
$this->clean_languages_cache();
$this->get_languages_list();
// Refresh rewrite rules.
flush_rewrite_rules();
/**
* Fires after a language is updated.
*
* @since 1.9
* @since 3.2 Added $lang parameter.
*
* @param array $args {
* Arguments used to modify the language. @see PLL_Admin_Model::update_language().
*
* @type string $name Language name (used only for display).
* @type string $slug Language code (ideally 2-letters ISO 639-1 language code).
* @type string $locale WordPress locale.
* @type int $rtl 1 if rtl language, 0 otherwise.
* @type int $term_group Language order when displayed.
* @type string $no_default_cat Optional, if set, no default category has been created for this language.
* @type string $flag Optional, country code, @see flags.php.
* }
* @param PLL_Language $lang Previous value of the language being edited.
*/
do_action( 'pll_update_language', $args, $lang );
return true;
}
/**
* Builds the language metas into an array and serializes it, to be stored in the term description.
*
* @since 3.4
*
* @param array $args {
* @type string $name Language name (used only for display).
* @type string $slug Language code (ideally 2-letters ISO 639-1 language code).
* @type string $locale WordPress locale. If something wrong is used for the locale, the .mo files will not be
* loaded...
* @type int $rtl 1 if rtl language, 0 otherwise.
* @type int $term_group Language order when displayed.
* @type int $lang_id Optional, ID of the language to modify. An empty value means the language is being
* created.
* @type string $flag Optional, country code, {@see settings/flags.php}.
* }
* @return string The serialized description array updated.
*/
protected function build_language_metas( array $args ) {
if ( ! empty( $args['lang_id'] ) ) {
$language_term = get_term( (int) $args['lang_id'] );
if ( $language_term instanceof WP_Term ) {
$old_data = maybe_unserialize( $language_term->description );
}
}
if ( empty( $old_data ) || ! is_array( $old_data ) ) {
$old_data = array();
}
$new_data = array(
'locale' => $args['locale'],
'rtl' => ! empty( $args['rtl'] ) ? 1 : 0,
'flag_code' => empty( $args['flag'] ) ? '' : $args['flag'],
);
/**
* Allow to add data to store for a language.
* `$locale`, `$rtl`, and `$flag_code` cannot be overwritten.
*
* @since 3.4
*
* @param mixed[] $add_data Data to add.
* @param mixed[] $args {
* Arguments used to create the language.
*
* @type string $name Language name (used only for display).
* @type string $slug Language code (ideally 2-letters ISO 639-1 language code).
* @type string $locale WordPress locale. If something wrong is used for the locale, the .mo files will
* not be loaded...
* @type int $rtl 1 if rtl language, 0 otherwise.
* @type int $term_group Language order when displayed.
* @type int $lang_id Optional, ID of the language to modify. An empty value means the language is
* being created.
* @type string $flag Optional, country code, {@see settings/flags.php}.
* }
* @param mixed[] $new_data New data.
* @param mixed[] $old_data {
* Original data. Contains at least the following:
*
* @type string $locale WordPress locale.
* @type int $rtl 1 if rtl language, 0 otherwise.
* @type string $flag_code Country code.
* }
*/
$add_data = apply_filters( 'pll_language_metas', array(), $args, $new_data, $old_data );
// Don't allow to overwrite `$locale`, `$rtl`, and `$flag_code`.
$new_data = array_merge( $old_data, $add_data, $new_data );
/** @var non-empty-string $serialized maybe_serialize() cannot return anything else than a string when fed by an array. */
$serialized = maybe_serialize( $new_data );
return $serialized;
}
/**
* Validates data entered when creating or updating a language.
*
* @see PLL_Admin_Model::add_language().
*
* @since 0.4
*
* @param array $args Parameters of {@see PLL_Admin_Model::add_language() or @see PLL_Admin_Model::update_language()}.
* @param PLL_Language|null $lang Optional the language currently updated, the language is created if not set.
* @return WP_Error
*/
protected function validate_lang( $args, $lang = null ) {
$errors = new WP_Error();
// Validate locale with the same pattern as WP 4.3. See #28303
if ( ! preg_match( '#^[a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?$#', $args['locale'], $matches ) ) {
$errors->add( 'pll_invalid_locale', __( 'Enter a valid WordPress locale', 'polylang' ) );
}
// Validate slug characters
if ( ! preg_match( '#^[a-z_-]+$#', $args['slug'] ) ) {
$errors->add( 'pll_invalid_slug', __( 'The language code contains invalid characters', 'polylang' ) );
}
// Validate slug is unique
foreach ( $this->get_languages_list() as $language ) {
if ( $language->slug === $args['slug'] && ( null === $lang || $lang->term_id !== $language->term_id ) ) {
$errors->add( 'pll_non_unique_slug', __( 'The language code must be unique', 'polylang' ) );
}
}
// Validate name
// No need to sanitize it as wp_insert_term will do it for us
if ( empty( $args['name'] ) ) {
$errors->add( 'pll_invalid_name', __( 'The language must have a name', 'polylang' ) );
}
// Validate flag
if ( ! empty( $args['flag'] ) && ! is_readable( POLYLANG_DIR . '/flags/' . $args['flag'] . '.png' ) ) {
$flag = PLL_Language::get_flag_informations( $args['flag'] );
if ( ! empty( $flag['url'] ) ) {
$response = function_exists( 'vip_safe_wp_remote_get' ) ? vip_safe_wp_remote_get( esc_url_raw( $flag['url'] ) ) : wp_remote_get( esc_url_raw( $flag['url'] ) );
}
if ( empty( $response ) || is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
$errors->add( 'pll_invalid_flag', __( 'The flag does not exist', 'polylang' ) );
}
}
return $errors;
}
/**
* Updates the translations when a language slug has been modified in settings
* or deletes them when a language is removed.
*
* @since 0.5
*
* @param string $old_slug The old language slug.
* @param string $new_slug Optional, the new language slug, if not set it means that the language has been deleted.
* @return void
*/
public function update_translations( $old_slug, $new_slug = '' ) {
global $wpdb;
$term_ids = array();
$dr = array();
$dt = array();
$ut = array();
$taxonomies = $this->translatable_objects->get_taxonomy_names( array( 'translations' ) );
$terms = get_terms( array( 'taxonomy' => $taxonomies ) );
if ( is_array( $terms ) ) {
foreach ( $terms as $term ) {
$term_ids[ $term->taxonomy ][] = $term->term_id;
$tr = maybe_unserialize( $term->description );
/**
* Filters the unserialized translation group description before it is
* updated when a language is deleted or a language slug is changed.
*
* @since 3.2
*
* @param (int|string[])[] $tr {
* List of translations with lang codes as array keys and IDs as array values.
* Also in this array:
*
* @type string[] $sync List of synchronized translations with lang codes as array keys and array values.
* }
* @param string $old_slug The old language slug.
* @param string $new_slug The new language slug.
* @param WP_Term $term The term containing the post or term translation group.
*/
$tr = apply_filters( 'update_translation_group', $tr, $old_slug, $new_slug, $term );
if ( ! empty( $tr[ $old_slug ] ) ) {
if ( $new_slug ) {
$tr[ $new_slug ] = $tr[ $old_slug ]; // Suppress this for delete
} else {
$dr['id'][] = (int) $tr[ $old_slug ];
$dr['tt'][] = (int) $term->term_taxonomy_id;
}
unset( $tr[ $old_slug ] );
if ( empty( $tr ) || 1 == count( $tr ) ) {
$dt['t'][] = (int) $term->term_id;
$dt['tt'][] = (int) $term->term_taxonomy_id;
} else {
$ut['case'][] = $wpdb->prepare( 'WHEN %d THEN %s', $term->term_id, maybe_serialize( $tr ) );
$ut['in'][] = (int) $term->term_id;
}
}
}
}
// Delete relationships
if ( ! empty( $dr ) ) {
// PHPCS:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
"DELETE FROM $wpdb->term_relationships
WHERE object_id IN ( " . implode( ',', $dr['id'] ) . ' )
AND term_taxonomy_id IN ( ' . implode( ',', $dr['tt'] ) . ' )'
);
// PHPCS:enable
}
// Delete terms
if ( ! empty( $dt ) ) {
$wpdb->query( "DELETE FROM $wpdb->terms WHERE term_id IN ( " . implode( ',', $dt['t'] ) . ' )' ); // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query( "DELETE FROM $wpdb->term_taxonomy WHERE term_taxonomy_id IN ( " . implode( ',', $dt['tt'] ) . ' )' ); // PHPCS:ignore WordPress.DB.PreparedSQL.NotPrepared
}
// Update terms
if ( ! empty( $ut ) ) {
// PHPCS:disable WordPress.DB.PreparedSQL.NotPrepared
$wpdb->query(
"UPDATE $wpdb->term_taxonomy
SET description = ( CASE term_id " . implode( ' ', $ut['case'] ) . ' END )
WHERE term_id IN ( ' . implode( ',', $ut['in'] ) . ' )'
);
// PHPCS:enable
}
if ( ! empty( $term_ids ) ) {
foreach ( $term_ids as $taxonomy => $ids ) {
clean_term_cache( $ids, $taxonomy );
}
}
}
/**
* Updates the default language
* taking care to update the default category & the nav menu locations.
*
* @since 1.8
*
* @param string $slug New language slug.
* @return void
*/
public function update_default_lang( $slug ) {
// The nav menus stored in theme locations should be in the default language
$theme = get_stylesheet();
if ( ! empty( $this->options['nav_menus'][ $theme ] ) ) {
$menus = array();
foreach ( $this->options['nav_menus'][ $theme ] as $key => $loc ) {
$menus[ $key ] = empty( $loc[ $slug ] ) ? 0 : $loc[ $slug ];
}
set_theme_mod( 'nav_menu_locations', $menus );
}
/**
* Fires when a default language is updated.
*
* @since 3.1
*
* @param string $slug Slug.
*/
do_action( 'pll_update_default_lang', $slug );
// Update options
$this->options['default_lang'] = $slug;
update_option( 'polylang', $this->options );
$this->clean_languages_cache();
flush_rewrite_rules();
}
}

View File

@@ -0,0 +1,280 @@
<?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 object $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
}
/**
* Setups filters and terms
* adds the language switcher metabox 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' ) );
// FIXME is it possible to choose the order ( after theme locations in WP3.5 and older ) ?
// 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' );
$this->create_nav_menu_locations();
}
/**
* 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( 'jquery' ), 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 ( empty( $_POST['menu-item-url'][ $menu_item_db_id ] ) || '#pll_switcher' !== $_POST['menu-item-url'][ $menu_item_db_id ] ) { // phpcs:ignore WordPress.Security.NonceVerification
return;
}
// Security check as 'wp_update_nav_menu_item' can be called from outside WP admin
if ( current_user_can( 'edit_theme_options' ) ) {
check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
$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
}
}
}
/**
* Assign 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
foreach ( $locations as $loc => $menu ) {
$infos = $this->explode_location( $loc );
$this->options['nav_menus'][ $this->theme ][ $infos['location'] ][ $infos['lang'] ] = $menu;
if ( $this->options['default_lang'] != $infos['lang'] ) {
unset( $locations[ $loc ] ); // Remove temporary locations before database update
}
}
update_option( 'polylang', $this->options );
return $locations;
}
/**
* Assign 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 ) {
if ( current_user_can( 'edit_theme_options' ) && is_array( $mods ) && isset( $mods['nav_menu_locations'] ) ) {
// Manage Locations tab in Appearance -> Menus
if ( isset( $_GET['action'] ) && 'locations' === $_GET['action'] ) { // phpcs:ignore WordPress.Security.NonceVerification
check_admin_referer( 'save-menu-locations' );
$this->options['nav_menus'][ $this->theme ] = array();
}
// Edit Menus tab in Appearance -> Menus
// Add the test of $_POST['update-nav-menu-nonce'] to avoid conflict with Vantage theme
elseif ( isset( $_POST['action'], $_POST['update-nav-menu-nonce'] ) && 'update' === $_POST['action'] ) {
check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
$this->options['nav_menus'][ $this->theme ] = array();
}
// Customizer
// Don't reset locations in this case.
// see http://wordpress.org/support/topic/menus-doesnt-show-and-not-saved-in-theme-settings-multilingual-site
elseif ( isset( $_POST['action'] ) && 'customize_save' == $_POST['action'] ) {
check_ajax_referer( 'save-customize_' . $GLOBALS['wp_customize']->get_stylesheet(), 'nonce' );
}
else {
return $mods; // No modification for nav menu locations
}
$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 ) {
if ( isset( $this->options['nav_menus'] ) ) {
foreach ( $this->options['nav_menus'] as $theme => $locations ) {
foreach ( $locations as $loc => $languages ) {
foreach ( $languages as $lang => $menu_id ) {
if ( $menu_id === $term_id ) {
unset( $this->options['nav_menus'][ $theme ][ $loc ][ $lang ] );
}
}
}
}
update_option( 'polylang', $this->options );
}
}
}

View File

@@ -0,0 +1,270 @@
<?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 array
*/
protected $options;
/**
* Stores custom notices.
*
* @var string[]
*/
private static $notices = array();
/**
* Constructor
* Setup actions
*
* @since 2.3.9
*
* @param object $polylang The Polylang object.
*/
public function __construct( $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.
* @return bool
*/
protected function can_display_notice( $notice ) {
$screen = get_current_screen();
if ( empty( $screen ) ) {
return false;
}
$screen_id = sanitize_title( __( 'Languages', 'polylang' ) );
/**
* Filter 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,
array(
'dashboard',
'plugins',
'toplevel_page_mlang',
$screen_id . '_page_mlang_strings',
$screen_id . '_page_mlang_settings',
)
),
$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' ) && ! $this->is_dismissed( 'pllwc' ) ) {
$this->pllwc_notice();
}
if ( ! defined( 'POLYLANG_PRO' ) && $this->can_display_notice( 'review' ) && ! $this->is_dismissed( 'review' ) && ! empty( $this->options['first_activation'] ) && time() > $this->options['first_activation'] + 15 * DAY_IN_SECONDS ) {
$this->review_notice();
}
// Custom notices
foreach ( $this->get_notices() as $notice => $html ) {
if ( $this->can_display_notice( $notice ) && ! $this->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/downloads/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
}
}

View File

@@ -0,0 +1,152 @@
<?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 ) && ( 'toplevel_page_mlang' === $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() {
$message = '';
if ( $this->page_on_front ) {
$untranslated = array();
foreach ( $this->model->get_languages_list() as $language ) {
if ( ! $this->model->post->get( $this->page_on_front, $language ) ) {
$untranslated[] = sprintf(
'<a href="%s">%s</a>',
esc_url( $this->links->get_new_post_translation_link( $this->page_on_front, $language ) ),
esc_html( $language->name )
);
}
}
if ( ! empty( $untranslated ) ) {
$message = 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
);
}
}
return $message;
}
}

View File

@@ -0,0 +1,136 @@
<?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( __CLASS__, 'sanitize_string_translation' ), 10, 2 );
}
/**
* 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' ),
);
// Widgets titles
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] ) || ! is_object( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! method_exists( $wp_registered_widgets[ $widget ]['callback'][0], 'get_settings' ) ) {
continue;
}
$widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->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'] ) ) {
if ( isset( $widget_settings[ $number ]['title'] ) && $title = $widget_settings[ $number ]['title'] ) {
self::register_string( self::$default_strings['widget_title'], $title, 'Widget' );
}
if ( isset( $widget_settings[ $number ]['text'] ) && $text = $widget_settings[ $number ]['text'] ) {
self::register_string( self::$default_strings['widget_text'], $text, '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 translation to sanitize
* @param string $name unique name for the string
* @return string
*/
public static function sanitize_string_translation( $translation, $name ) {
if ( $name == self::$default_strings['widget_title'] ) {
$translation = sanitize_text_field( $translation );
}
if ( $name == self::$default_strings['widget_text'] && ! current_user_can( 'unfiltered_html' ) ) {
$translation = wp_kses_post( $translation );
}
return $translation;
}
}

View 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;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* Displays the translations fields for media
* Needs WP 3.5+
*
* @package Polylang
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly
}
?>
<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;
}
?>
<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_id = $this->model->post->get_translation( $post_ID, $language ) ) && $translation_id !== $post_ID ) {
// The translation exists
printf(
'<input type="hidden" name="media_tr_lang[%s]" value="%d" />',
esc_attr( $language->slug ),
esc_attr( $translation_id )
);
echo $this->links->edit_post_translation_link( $translation_id ); // phpcs:ignore WordPress.Security.EscapeOutput
} else {
// No translation
echo $this->links->new_post_translation_link( $post_ID, $language ); // phpcs:ignore WordPress.Security.EscapeOutput
}
?>
</td>
</tr>
<?php
} // End foreach
?>
</table>

View File

@@ -0,0 +1,69 @@
<?php
/**
* Displays the translations fields for posts
*
* @package Polylang
*/
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->new_post_translation_link( $post_ID, $language );
$link = $add_link;
$translation = null;
if ( $translation_id ) {
$translation = get_post( $translation_id );
$link = $this->links->edit_post_translation_link( $translation_id );
}
?>
<tr>
<th class = "pll-language-column"><?php echo $language->flag ? $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
*/
do_action( 'pll_before_post_translation_' . $language->slug );
?>
<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( $translation->ID ) ),
( empty( $translation ) ? '' : esc_attr( $translation->post_title ) ),
esc_attr( $language->get_locale( 'display' ) ),
( $language->is_rtl ? 'rtl' : 'ltr' )
);
?>
</td>
</tr>
<?php
}
?>
</table>

View File

@@ -0,0 +1,98 @@
<?php
/**
* Displays the translations fields for terms
*
* @package Polylang
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Don't access directly
}
if ( isset( $term_id ) ) {
// 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 isset( $term_id ) ? '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 = 0;
if ( isset( $term_id ) && ( $translation_id = $this->model->term->get_translation( $term_id, $language ) ) && $translation_id != $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 );
}
if ( isset( $term_id ) ) { // Do not display the add new link in add term form ( $term_id not set !!! )
$link = $add_link = $this->links->new_term_translation_link( $term_id, $taxonomy, $post_type, $language );
}
if ( $translation ) {
$link = $this->links->edit_term_translation_link( $translation->term_id, $taxonomy, $post_type );
}
?>
<tr>
<th class = "pll-language-column">
<span class = "pll-translation-flag"><?php echo $language->flag ? $language->flag : esc_html( $language->slug ); // phpcs:ignore WordPress.Security.EscapeOutput ?></span>
<?php
printf(
'<span class="pll-language-name%1$s">%2$s</span>',
isset( $term_id ) ? '' : ' screen-reader-text',
esc_html( $language->name )
);
?>
</th>
<?php
if ( isset( $term_id ) ) {
?>
<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$s" />
<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' ),
( empty( $translation ) ? 0 : esc_attr( $translation->term_id ) ),
( empty( $translation ) ? '' : 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 ( isset( $term_id ) ) {
// Edit term form
?>
</td>
<?php
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,459 @@
/* languages admin panel */
#add-lang select {
width: 95%;
}
#add-lang label {
margin: 0.35em 0 0.5em;
}
.pll-legend {
display: block;
padding: 2px 0;
color: #1d2327;
font-weight: 400;
text-shadow: none;
margin: 0.35em 0 0.5em;
}
.column-locale,
.languages .column-slug {
width : 15%
}
.column-default_lang {
width : 5%;
}
.column-term_group,
.column-flag, .column-count {
width : 10%;
}
td.column-default_lang .icon-default-lang:before,
.pll-wizard-content .icon-default-lang:before {
font-family: 'dashicons';
content: "\f155";
}
.pll-icon:before{
display: inline-block;
text-align: left;
width: 15px;
}
.pll-circle:before{
content: "\25cf";
}
/* about Polylang metabox */
#pll-about-box p,
#pll-recommended p {
text-align: justify;
}
#pll-about-box input {
margin: 0;
padding: 0;
float: right;
}
/* strings translation table */
.stringstranslations .column-name,
.stringstranslations .column-context {
width: 10%;
}
.stringstranslations .column-string {
width: 33%;
}
.translation label {
display: inline-block;
width: 23%;
vertical-align: top;
}
.translation {
display: flex; /* fix #691 to remove default margin bottom */
}
@media screen and (max-width: 782px) { /* reset default display property for small device */
.translation{
display: block;
}
}
.translation textarea{
display: block; /* fix #691 to remove default margin bottom */
}
.translation input,
.translation textarea {
width: 72%;
box-sizing: border-box; /* to be sure field don't overrun outside their wrapper */
margin-bottom: 4px; /* fix #691 set the same margin bottom for both textarea and input tags */
}
/* settings */
.pll-settings {
margin-top: 20px;
}
.pll-settings .plugin-title {
width: 25%;
}
#wpbody-content .pll-settings .pll-configure tr {
display: table-row;
}
#wpbody-content .pll-settings .pll-configure td {
display: table-cell;
}
#wpbody-content .pll-settings .pll-configure > td {
padding: 20px 20px 20px 40px;
}
.pll-configure legend {
font-size: 14px;
font-weight: 600;
margin-bottom: 0.5em;
}
.pll-configure td .description {
margin-top: 2px;
margin-bottom: 0.5em;
}
.pll-configure p.submit {
margin-top: 20px;
}
.pll-configure .button {
margin-right: 20px;
}
.pll-configure fieldset {
margin-bottom: 1.5em;
}
.pll-inline-block-list {
margin: 0;
}
.pll-inline-block-list li {
display: inline-block;
margin: 0;
width: 250px;
}
/* settings URL modifications */
#pll-domains-table td {
padding: 2px 2px 2px 1.5em;
-webkit-box-shadow: none;
box-shadow: none;
border: none;
}
.pll-settings-url-col {
display: inline-block;
width: 49%;
vertical-align: top;
}
/* settings Activation keys */
.pll-table-top td {
vertical-align: top;
}
#pll-licenses-table label {
font-size: 1em;
font-weight: 600;
}
.pll-configure .pll-deactivate-license {
margin: 0 0 0 20px;
}
/* language columns in edit.php and edit-tags.php */
.wp-list-table th[class*='column-language_'],
.wp-list-table td[class*='column-language_'] {
width: 1.5em;
box-sizing: content-box; /* Override ACF 5.9.0 styles */
}
/* Text direction in post.php and edit-tags.php */
.pll-dir-rtl textarea,
.pll-dir-rtl input[type="text"] {
direction: rtl;
}
.pll-dir-ltr textarea,
.pll-dir-ltr input[type="text"] {
direction: ltr;
}
.pll-dir-ltr .tr_lang,
.pll-dir-rtl .tr_lang {
direction: inherit;
}
/* languages metabox in post.php */
#ml_box p {
margin-top: 1em;
}
#post-translations p {
float: left;
margin-top: 1em;
}
.rtl #post-translations p {
float: right;
}
#post-translations table {
table-layout: fixed;
width: 100%;
clear: both;
}
#post-translations a {
text-decoration: none;
}
#post-translations .pll-language-column,
#post-translations .pll-column-icon {
width: 20px;
}
#post-translations .tr_lang {
width: 100%;
}
#post-translations td {
padding: 2px;
}
#post-translations .spinner,
#term-translations .spinner {
float: none;
margin: 0;
background-position: center;
width: auto;
}
#select-post-language .pll-select-flag {
padding: 4px;
margin-right: 10px;
}
.rtl #select-post-language .pll-select-flag {
padding: 4px;
margin-right: 0px;
margin-left: 10px;
}
/* specific cases for media */
#select-media-language .pll-select-flag {
padding: 4px;
margin-right: 10px;
}
.pll-media-edit-column {
float: right;
}
/* language and translations in edit-tags.php */
.pll-translation-flag { /* also for media */
margin-right: 14px;
}
#select-add-term-language .pll-select-flag {
padding: 11px;
margin-right: 13px;
}
#select-edit-term-language .pll-select-flag {
padding: 11px;
margin-right: 4px;
}
#term-translations p {
/* same style as label */
font-weight: 400;
font-style: normal;
padding: 2px;
color: #23282d;
}
#add-term-translations,
#edit-term-translations {
width: 95%;
}
#term-translations .pll-language-column {
line-height: 28px;
width: 20%;
}
#term-translations .pll-edit-column,
#add-term-translations .pll-language-column {
width: 20px;
}
#edit-term-translations .pll-language-column {
padding: 15px 10px;
font-weight: normal;
}
/* icon fonts */
.pll_icon_add:before {
content: "\f132";
}
.pll_icon_edit:before {
content: "\f464";
}
[class^="pll_icon_"] {
font: 20px/1 'dashicons';
vertical-align: middle;
}
/* admin bar */
#wpadminbar #wp-admin-bar-languages .ab-item img {
margin: 0 8px 0 2px;
}
#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon {
float: none;
top: 4px;
}
#wpadminbar #wp-admin-bar-languages .ab-icon:before {
content: "\f326";
top: 1px;
}
#wp-admin-bar-languages.pll-filtered-languages {
background: #a03f3f;
}
#wpadminbar #wp-admin-bar-languages.pll-filtered-languages span.ab-label{ /* Enforce white color for WordPress admin light theme. */
color: #fff;
}
/* Notices */
.pll-notice.notice {
padding-right: 38px;
position: relative;
}
.pll-notice a.notice-dismiss {
text-decoration: none;
}
.pll-notice .button {
margin-right: 10px;
}
@media screen and ( max-width: 782px ) {
/* settings */
#wpbody-content .pll-settings .pll-configure > td {
padding: 20px;
}
#wpbody-content .pll-settings #cb {
padding: 20px 9px;
}
/* settings URL modifications */
.pll-inline-block {
width: auto;
}
.pll-settings-url-col {
display: block;
width: 100%;
}
/* settings licenses */
#wpbody-content .pll-settings #pll-licenses-table td {
display: block;
}
.pll-configure .pll-deactivate-license {
margin: 10px 0 5px;
}
/* strings translations table */
.translation label {
display: block;
width: 95%;
padding-left: 0;
}
.translation input,
.translation textarea {
width: 95%;
}
/* hide selected language flag and translations language name */
#select-add-term-language .pll-select-flag,
#select-edit-term-language .pll-select-flag,
#edit-term-translations .pll-language-name {
display: none;
}
#edit-term-translations {
width: 100%;
}
#add-term-translations .pll-language-column {
line-height: 38px;
}
#edit-term-translations td {
padding: 8px 10px;
}
#edit-term-translations .pll-language-column,
#edit-term-translations .pll-edit-column {
width: 20px;
}
/* translations tables should be kept as table */
.term-translations .pll-language-column,
.term-translations .pll-edit-column,
.term-translations .pll-translation-column {
display: table-cell;
}
.term-translations .hidden {
display: none;
}
/* admin bar */
#wpadminbar #wp-admin-bar-languages {
display: block; /*shows our menu on mobile devices */
}
#wpadminbar #wp-admin-bar-languages > .ab-item {
width: 50px;
text-align: center;
}
#wpadminbar #wp-admin-bar-languages > .ab-item .ab-icon:before {
font: 32px/1 'dashicons';
top: -1px;
}
#wpadminbar #wp-admin-bar-languages > .ab-item img {
margin: 19px 0;
}
#wpadminbar #wp-admin-bar-languages #wp-admin-bar-all .ab-item .ab-icon {
margin-right: 6px;
font-size: 20px !important;
line-height: 20px !important;
}
}

View File

@@ -0,0 +1,85 @@
/* By default Polylang dialog box use WordPress jQuery UI dialog styles.
However WooCommerce loads its own jQuery UI dialog styles and we need to override them by ours
to revert to the default WordPress ones.
*/
.pll-confirmation-modal.ui-widget,
.pll-confirmation-modal.ui-widget .ui-widget,
.pll-confirmation-modal .ui-widget {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
font-size: 13px;
}
.pll-confirmation-modal.ui-dialog {
padding: 0;
z-index: 100102;
background: #fff;
border: 0;
color: #444;
border-radius: 0; /* Override WooCommerce dialog styles - jQuery UI 1.11.4 - WP < 5.6 */
}
.ui-dialog.pll-confirmation-modal .ui-dialog-titlebar {
background: #fcfcfc;
border-radius: 0;
border: 0;
border-bottom: 1px solid #dfdfdf;
height: 36px;
font-size: 18px;
font-weight: 600;
line-height: 2;
padding: 0 36px 0 16px;
color: #444;
position: static;
}
.ui-dialog.pll-confirmation-modal .ui-dialog-title {
float: none;
width: auto;
margin: 0;
}
.pll-confirmation-modal .ui-widget-header .ui-icon {
background: none;
position: static;
}
.pll-confirmation-modal .ui-button.ui-dialog-titlebar-close {
padding: 0;
margin: 0;
top: 0;
right: 0;
width: 36px;
height: 36px;
border: 0; /* Override WooCommerce dialog styles - jQuery UI 1.11.4 - WP < 5.6 */
background: none; /* Override WooCommerce dialog styles - jQuery UI 1.11.4 - WP < 5.6 */
}
.ui-dialog.pll-confirmation-modal .ui-dialog-content {
border: 0;
padding: 16px;
color: #444;
position: static;
box-sizing: border-box;
}
.ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane{
margin: 0;
padding: 16px;
border: 0;
background: #fcfcfc;
border-top: 1px solid #dfdfdf;
}
.ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane .ui-button{
margin: 0 0 0 16px;
padding: 0 10px 1px;
background: #f7f7f7;
border: 1px solid #cccccc;
border-radius: 3px;
position: static;
line-height: 2;
vertical-align: top;
}
.ui-dialog.pll-confirmation-modal .ui-button:hover,
.ui-dialog.pll-confirmation-modal .ui-button:focus {
background: #fafafa;
border-color: #999;
color: #23282d;
}
.pll-confirmation-modal + .ui-widget-overlay {
background: #000;
opacity: 0.7;
z-index: 100101;
}

View File

@@ -0,0 +1,239 @@
/* Greatly modified version of the jquery-ui.css */
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ui-menu {
list-style: none;
padding: 0;
margin: 0;
display: block;
outline: none;
}
.ui-menu .ui-menu {
position: absolute;
}
.ui-menu .ui-menu-item {
position: relative;
margin: 0;
padding: 3px 1em 3px .4em;
cursor: pointer;
min-height: 0; /* support: IE7 */
/* support: IE10, see #8844 */
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
/* for jQuery UI 1.12 which introduces a wrapper */
.ui-menu .ui-menu-item:not([role]) {
padding: 0;
}
.ui-menu-item-wrapper {
padding: 3px 1em 3px 2em;
}
.rtl .ui-menu .ui-menu-item {
text-align: right;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item[role] {
padding-left: 2em;
}
.rtl .ui-menu-item-wrapper, /* for jQuery UI 1.12 which introduces a wrapper */
.rtl .ui-menu-icons .ui-menu-item[role] {
padding-left: 1em;
padding-right: 2em;
}
/* left-aligned */
.ui-selectmenu-text .ui-icon,
.ui-menu .ui-icon {
position: absolute;
top: 0;
bottom: 0;
left: .3em;
margin: auto 0;
}
.rtl .ui-selectmenu-text .ui-icon,
.rtl .ui-menu .ui-icon {
right: .3em;
left: auto;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
left: auto;
right: 0;
}
.ui-selectmenu-menu {
padding: 0;
margin: 0;
position: absolute;
top: 0;
left: 0;
display: none;
}
.ui-selectmenu-menu .ui-menu {
overflow: auto;
/* Support: IE7 */
overflow-x: hidden;
padding-bottom: 1px;
}
.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
font-size: 1em;
font-weight: bold;
line-height: 23px;
padding: 2px 0.4em;
margin: 0.5em 0 0 0;
height: auto;
border: 0;
}
.ui-selectmenu-open {
display: block;
}
.ui-selectmenu-button, /* jQuery UI 1.11.4 - WP < 5.6 */
.ui-selectmenu-button.ui-button {
display: inline-block;
overflow: hidden;
position: relative;
text-decoration: none;
box-sizing: border-box; /* To keep width calculation in percent since WP 5.6 */
text-align: left;
white-space: nowrap;
vertical-align: top;
padding: 0;
line-height: normal; /* Override WC Bookings styles with WP < 5.6 */
height: 28px; /* Override WC Bookings styles with WP < 5.6 */
}
.ui-selectmenu-button span.ui-icon {
right: 0.5em;
left: auto;
position: absolute;
top: 26%;
width: 16px;
height: 16px;
text-indent: 0; /* due to text-indent for jquery ui-dialog in wizard */
background: none;
}
.rtl .ui-selectmenu-button span.ui-icon {
left: 0.5em;
right: auto;
}
.ui-selectmenu-button.ui-widget span.ui-selectmenu-text, /* Override WC Bookings styles with WP < 5.6 */
.ui-selectmenu-button span.ui-selectmenu-text {
text-align: left;
padding: 0.1em 2.1em 0.2em 2em;
display: block;
line-height: 23px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin: 0;
}
.rtl .ui-selectmenu-button span.ui-selectmenu-text {
text-align: right;
padding: 0.2em 2em 0.2em 2.1em;
}
.ui-widget-content,
.ui-state-default,
.ui-selectmenu-button.ui-state-default, /* Override WC Bookings styles with WP < 5.6 */
.ui-button.ui-selectmenu-button-closed, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
.ui-button.ui-selectmenu-button-open, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default {
background: #fff;
border: 1px solid #ddd;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07) inset;
color: #32373c;
}
/* Override to have same styles as WP form styles since WordPress 5.4 */
.toplevel_page_mlang .ui-selectmenu-button.ui-state-default,
.toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-closed, /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
.toplevel_page_mlang .ui-selectmenu-button.ui-selectmenu-button-open{ /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
box-shadow: 0 0 0 transparent;
border-radius: 4px;
border: 1px solid #7e8993;
}
/* From this line and below: override WooCommerce bookings plugin styles which overrides default WordPress styles */
.pll-selectmenu-menu .ui-widget,
.pll-selectmenu-button.ui-widget {
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
}
.toplevel_page_mlang .ui-button.ui-selectmenu-button:focus{
color: #016087; /* Same color as WordPress focused select HTML tag */
border-color: #007cba;
box-shadow: 0 0 0 1px #007cba;
outline: 2px solid transparent;
background: #fff; /* Override bookings plugin styles which overrides default WordPress styles */
}
.toplevel_page_mlang .ui-menu-item,
.toplevel_page_mlang .ui-widget-content .ui-state-hover,
.toplevel_page_mlang .ui-widget-content .ui-state-focus,
.toplevel_page_mlang .ui-widget-content .ui-state-active {
color: #016087; /* Same color as option in a WordPress focused select HTML tag */
margin: 0;
}
.ui-selectmenu-open .ui-widget-content .ui-state-hover, /* Override WC Bookings styles with WP < 5.6 */
.ui-selectmenu-open .ui-widget-content .ui-state-focus, /* Override WC Bookings styles with WP < 5.6 */
.ui-selectmenu-open .ui-widget-content .ui-state-active, /* Override WC Bookings styles with WP < 5.6 */
.pll-selectmenu-menu .ui-widget-content .ui-state-hover,
.pll-selectmenu-menu .ui-widget-content .ui-state-focus,
.pll-selectmenu-menu .ui-widget-content .ui-state-active { /* To be compatible jQuery UI 1.12.1 since WordPress 5.6 */
background: #d5d5d5;
border: 0;
}
.ui-selectmenu-button.ui-state-focus {
border: 1px solid #5b9dd9;
box-shadow: 0 0 2px rgba(30, 140, 190, 0.8);
}
.ui-icon-triangle-1-s:before {
content: "";
background: #fff url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat right 0px top 55%;
background-size: 16px 16px;
box-sizing: border-box;
position: absolute;
width: 16px;
height: 16px;
}
.pll-selectmenu-button.ui-button:hover,
.pll-wizard .ui-button:hover,
.pll-wizard .ui-button:focus {
background: #fff; /* To override jQuery ui-dialog styles provided by WordPress */
}
.ui-widget-content {
max-height: 231px;
box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Some files were not shown because too many files have changed in this diff Show More