first commit
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace WPML\ST\Upgrade\Command;
|
||||
|
||||
|
||||
use WPML\Element\API\Languages;
|
||||
use WPML\FP\Cast;
|
||||
use WPML\FP\Fns;
|
||||
use WPML\FP\Logic;
|
||||
use WPML\FP\Lst;
|
||||
use WPML\FP\Obj;
|
||||
use WPML\FP\Relation;
|
||||
use WPML\FP\Str;
|
||||
use WPML\LIB\WP\Option;
|
||||
use WPML\ST\MO\File\ManagerFactory;
|
||||
use function WPML\FP\partial;
|
||||
use function WPML\FP\pipe;
|
||||
|
||||
class MigrateMultilingualWidgets implements \IWPML_St_Upgrade_Command {
|
||||
|
||||
public function run() {
|
||||
$multiLingualWidgets = Option::getOr( 'widget_text_icl', [] );
|
||||
$multiLingualWidgets = array_filter( $multiLingualWidgets, Logic::complement( 'is_scalar' ) );
|
||||
if ( ! $multiLingualWidgets ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$textWidgets = Option::getOr( 'widget_text', [] );
|
||||
if ( $textWidgets ) {
|
||||
$theHighestTextWidgetId = max( Obj::keys( $textWidgets ) );
|
||||
} else {
|
||||
$theHighestTextWidgetId = 0;
|
||||
$textWidgets['_multiwidget'] = 1;
|
||||
}
|
||||
|
||||
$transformWidget = pipe(
|
||||
Obj::renameProp( 'icl_language', 'wpml_language' ),
|
||||
Obj::over( Obj::lensProp( 'wpml_language' ), Logic::ifElse( Relation::equals( 'multilingual' ), Fns::always( 'all' ), Fns::identity() ) )
|
||||
);
|
||||
|
||||
$oldToNewIdMap = [];
|
||||
foreach ( $multiLingualWidgets as $id => $widget ) {
|
||||
$newId = ++ $theHighestTextWidgetId;
|
||||
$oldToNewIdMap[ $id ] = $newId;
|
||||
|
||||
$textWidgets = Obj::assoc( $newId, $transformWidget( $widget ), $textWidgets );
|
||||
}
|
||||
|
||||
Option::update( 'widget_text', $textWidgets );
|
||||
Option::delete( 'widget_text_icl' );
|
||||
|
||||
$sidebars = wp_get_sidebars_widgets();
|
||||
$sidebars = $this->convertSidebarsConfig( $sidebars, $oldToNewIdMap );
|
||||
wp_set_sidebars_widgets( $sidebars );
|
||||
|
||||
$this->convertWidgetsContentStrings();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function convertSidebarsConfig( $sidebars, array $oldToNewIdMap ) {
|
||||
$isMultilingualWidget = Str::startsWith( 'text_icl' );
|
||||
$extractIdNumber = pipe( Str::split( '-' ), Lst::last(), Cast::toInt() );
|
||||
|
||||
$mapWidgetId = Logic::ifElse(
|
||||
$isMultilingualWidget,
|
||||
pipe( $extractIdNumber, Obj::prop( Fns::__, $oldToNewIdMap ), Str::concat( 'text-' ) ),
|
||||
Fns::identity()
|
||||
);
|
||||
|
||||
return Fns::map( Fns::map( $mapWidgetId ), $sidebars );
|
||||
}
|
||||
|
||||
private function convertWidgetsContentStrings() {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query("
|
||||
UPDATE {$wpdb->prefix}icl_strings
|
||||
SET `name` = CONCAT( 'widget body - ', MD5(`value`))
|
||||
WHERE `name` LIKE 'widget body - text_icl%'
|
||||
");
|
||||
|
||||
$locales = Fns::map( Languages::getWPLocale(), Languages::getSecondaries() );
|
||||
Fns::map( partial( [ ManagerFactory::create(), 'add' ], 'Widgets' ), $locales );
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
}
|
||||
|
||||
public static function get_command_id() {
|
||||
return __CLASS__;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace WPML\ST\Upgrade\Command;
|
||||
|
||||
use WPML\ST\MO\File\Manager;
|
||||
use WPML\ST\MO\Generate\Process\SingleSiteProcess;
|
||||
use WPML\ST\MO\Generate\Process\Status;
|
||||
use WPML\ST\MO\Notice\RegenerationInProgressNotice;
|
||||
use WPML_Installation;
|
||||
use function WPML\Container\make;
|
||||
|
||||
class RegenerateMoFilesWithStringNames implements \IWPML_St_Upgrade_Command {
|
||||
|
||||
const WPML_VERSION_FOR_THIS_COMMAND = '4.3.4';
|
||||
|
||||
/** @var Status $status */
|
||||
private $status;
|
||||
|
||||
/** @var SingleSiteProcess $singleProcess */
|
||||
private $singleProcess;
|
||||
|
||||
/**
|
||||
* @param Status $status
|
||||
* @param SingleSiteProcess $singleProcess We use run the single site process because
|
||||
* the migration command runs once per site.
|
||||
*/
|
||||
public function __construct( Status $status, SingleSiteProcess $singleProcess ) {
|
||||
$this->status = $status;
|
||||
$this->singleProcess = $singleProcess;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
if ( ! ( $this->hasWpmlStartedBeforeThisCommand() && Manager::hasFiles() ) ) {
|
||||
$this->status->markComplete();
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->singleProcess->runPage();
|
||||
|
||||
if ( $this->singleProcess->isCompleted() ) {
|
||||
\wpml_get_admin_notices()->remove_notice(
|
||||
RegenerationInProgressNotice::GROUP,
|
||||
RegenerationInProgressNotice::ID
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
\wpml_get_admin_notices()->add_notice( make( RegenerationInProgressNotice::class ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function hasWpmlStartedBeforeThisCommand() {
|
||||
return (bool) version_compare(
|
||||
get_option( WPML_Installation::WPML_START_VERSION_KEY, '0.0.0' ),
|
||||
self::WPML_VERSION_FOR_THIS_COMMAND,
|
||||
'<'
|
||||
);
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
|
||||
}
|
||||
|
||||
public static function get_command_id() {
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* WPML_ST_Upgrade_Command_Factory class file.
|
||||
*
|
||||
* @package wpml-string-translation
|
||||
*/
|
||||
|
||||
use function WPML\Container\make;
|
||||
use WPML\ST\Upgrade\Command\RegenerateMoFilesWithStringNames;
|
||||
use WPML\ST\Upgrade\Command\MigrateMultilingualWidgets;
|
||||
|
||||
/**
|
||||
* Class WPML_ST_Upgrade_Command_Factory
|
||||
*/
|
||||
class WPML_ST_Upgrade_Command_Factory {
|
||||
/**
|
||||
* WP db instance.
|
||||
*
|
||||
* @var wpdb wpdb
|
||||
*/
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* SitePress instance.
|
||||
*
|
||||
* @var SitePress
|
||||
*/
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* WPML_ST_Upgrade_Command_Factory constructor.
|
||||
*
|
||||
* @param wpdb $wpdb WP db instance.
|
||||
* @param SitePress $sitepress SitePress instance.
|
||||
*/
|
||||
public function __construct( wpdb $wpdb, SitePress $sitepress ) {
|
||||
$this->wpdb = $wpdb;
|
||||
$this->sitepress = $sitepress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create upgrade commands.
|
||||
*
|
||||
* @param string $class_name Name of upgrade command class.
|
||||
*
|
||||
* @throws WPML_ST_Upgrade_Command_Not_Found_Exception Exception when command not found.
|
||||
* @return IWPML_St_Upgrade_Command
|
||||
*/
|
||||
public function create( $class_name ) {
|
||||
switch ( $class_name ) {
|
||||
case 'WPML_ST_Upgrade_Migrate_Originals':
|
||||
$result = new WPML_ST_Upgrade_Migrate_Originals( $this->wpdb, $this->sitepress );
|
||||
break;
|
||||
case 'WPML_ST_Upgrade_Display_Strings_Scan_Notices':
|
||||
$themes_and_plugins_settings = new WPML_ST_Themes_And_Plugins_Settings();
|
||||
$result = new WPML_ST_Upgrade_Display_Strings_Scan_Notices( $themes_and_plugins_settings );
|
||||
break;
|
||||
case 'WPML_ST_Upgrade_DB_String_Packages':
|
||||
$result = new WPML_ST_Upgrade_DB_String_Packages( $this->wpdb );
|
||||
break;
|
||||
case 'WPML_ST_Upgrade_MO_Scanning':
|
||||
$result = new WPML_ST_Upgrade_MO_Scanning( $this->wpdb );
|
||||
break;
|
||||
case 'WPML_ST_Upgrade_DB_String_Name_Index':
|
||||
$result = new WPML_ST_Upgrade_DB_String_Name_Index( $this->wpdb );
|
||||
break;
|
||||
case 'WPML_ST_Upgrade_DB_Longtext_String_Value':
|
||||
$result = new WPML_ST_Upgrade_DB_Longtext_String_Value( $this->wpdb );
|
||||
break;
|
||||
case 'WPML_ST_Upgrade_DB_Strings_Add_Translation_Priority_Field':
|
||||
$result = new WPML_ST_Upgrade_DB_Strings_Add_Translation_Priority_Field( $this->wpdb );
|
||||
break;
|
||||
case 'WPML_ST_Upgrade_DB_String_Packages_Word_Count':
|
||||
$result = new WPML_ST_Upgrade_DB_String_Packages_Word_Count( wpml_get_upgrade_schema() );
|
||||
break;
|
||||
case '\WPML\ST\Upgrade\Command\RegenerateMoFilesWithStringNames':
|
||||
$isBackground = true;
|
||||
$result = new RegenerateMoFilesWithStringNames(
|
||||
\WPML\ST\MO\Generate\Process\ProcessFactory::createStatus( $isBackground ),
|
||||
\WPML\ST\MO\Generate\Process\ProcessFactory::createSingle( $isBackground )
|
||||
);
|
||||
break;
|
||||
case MigrateMultilingualWidgets::class:
|
||||
$result = new MigrateMultilingualWidgets();
|
||||
break;
|
||||
default:
|
||||
throw new WPML_ST_Upgrade_Command_Not_Found_Exception( $class_name );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
|
||||
class WPML_ST_Upgrade_Command_Not_Found_Exception extends InvalidArgumentException {
|
||||
/**
|
||||
* @param string $class_name
|
||||
* @param int $code
|
||||
* @param Exception $previous
|
||||
*/
|
||||
public function __construct( $class_name, $code = 0, Exception $previous = null ) {
|
||||
$msg = sprintf( 'Class %s is not valid String Translation upgrade strategy', $class_name );
|
||||
parent::__construct( $msg, $code, $previous );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* WPML_ST_Upgrade_DB_Longtext_String_Value class file.
|
||||
*
|
||||
* @package wpml-string-translation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WPML_ST_Upgrade_DB_Longtext_String_Value
|
||||
*/
|
||||
class WPML_ST_Upgrade_DB_Longtext_String_Value implements IWPML_St_Upgrade_Command {
|
||||
/**
|
||||
* WP db instance.
|
||||
*
|
||||
* @var wpdb
|
||||
*/
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* WPML_ST_Upgrade_DB_Longtext_String_Value constructor.
|
||||
*
|
||||
* @param wpdb $wpdb WP db instance.
|
||||
*/
|
||||
public function __construct( wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run upgrade.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run() {
|
||||
$result = true;
|
||||
|
||||
$table_name = $this->wpdb->prefix . 'icl_strings';
|
||||
if ( count( (array) $this->wpdb->get_results( "SHOW TABLES LIKE '{$table_name}'" ) ) ) {
|
||||
$sql = "
|
||||
ALTER TABLE {$table_name}
|
||||
MODIFY COLUMN `value` LONGTEXT NOT NULL;
|
||||
";
|
||||
|
||||
$result = false !== $this->wpdb->query( $sql );
|
||||
}
|
||||
|
||||
$table_name = $this->wpdb->prefix . 'icl_string_translations';
|
||||
if ( count( (array) $this->wpdb->get_results( "SHOW TABLES LIKE '{$table_name}'" ) ) ) {
|
||||
$sql = "
|
||||
ALTER TABLE {$table_name}
|
||||
MODIFY COLUMN `value` LONGTEXT NULL DEFAULT NULL,
|
||||
MODIFY COLUMN `mo_string` LONGTEXT NULL DEFAULT NULL;
|
||||
";
|
||||
|
||||
$result = ( false !== $this->wpdb->query( $sql ) ) && $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run upgrade in ajax.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run_ajax() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run upgrade on frontend.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function run_frontend() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get command id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_command_id() {
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Upgrade_DB_String_Name_Index implements IWPML_St_Upgrade_Command {
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$result = true;
|
||||
|
||||
$table_name = $this->wpdb->prefix . 'icl_strings';
|
||||
if ( 0 !== count( $this->wpdb->get_results( "SHOW TABLES LIKE '{$table_name}'" ) ) ) {
|
||||
$sql = "SHOW KEYS FROM {$table_name} WHERE Key_name='icl_strings_name'";
|
||||
if ( 0 === count( $this->wpdb->get_results( $sql ) ) ) {
|
||||
$sql = "
|
||||
ALTER TABLE {$this->wpdb->prefix}icl_strings
|
||||
ADD INDEX `icl_strings_name` (`name` ASC);
|
||||
";
|
||||
|
||||
$result = false !== $this->wpdb->query( $sql );
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
$this->run();
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
$this->run();
|
||||
}
|
||||
|
||||
public static function get_command_id() {
|
||||
return __CLASS__ . '_2';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Upgrade_DB_String_Packages_Word_Count implements IWPML_St_Upgrade_Command {
|
||||
|
||||
/** @var WPML_Upgrade_Schema $upgrade_schema */
|
||||
private $upgrade_schema;
|
||||
|
||||
public function __construct( WPML_Upgrade_Schema $upgrade_schema ) {
|
||||
$this->upgrade_schema = $upgrade_schema;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$table = 'icl_string_packages';
|
||||
$column = 'word_count';
|
||||
|
||||
if ( ! $this->upgrade_schema->does_table_exist( $table ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->upgrade_schema->does_column_exist( $table, $column ) ) {
|
||||
return (bool) $this->upgrade_schema->add_column( $table, $column, 'VARCHAR(2000) DEFAULT NULL' );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function get_command_id() {
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class WPML_ST_Upgrade_DB_String_Packages
|
||||
*/
|
||||
class WPML_ST_Upgrade_DB_String_Packages implements IWPML_St_Upgrade_Command {
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* WPML_ST_Upgrade_DB_String_Packages constructor.
|
||||
*
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$sql_get_st_package_table_name = "SHOW TABLES LIKE '{$this->wpdb->prefix}icl_string_packages'";
|
||||
|
||||
$st_packages_table_exist = $this->wpdb->get_var( $sql_get_st_package_table_name ) === "{$this->wpdb->prefix}icl_string_packages";
|
||||
|
||||
if ( ! $st_packages_table_exist ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql_get_post_id_column_from_st_package = "SHOW COLUMNS FROM {$this->wpdb->prefix}icl_string_packages LIKE 'post_id'";
|
||||
$post_id_column_exists = $st_packages_table_exist
|
||||
? $this->wpdb->get_var( $sql_get_post_id_column_from_st_package ) === 'post_id'
|
||||
: false;
|
||||
|
||||
if ( ! $post_id_column_exists ) {
|
||||
$sql = "ALTER TABLE {$this->wpdb->prefix}icl_string_packages ADD COLUMN `post_id` INTEGER";
|
||||
return (bool) $this->wpdb->query( $sql );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public static function get_command_id() {
|
||||
return __CLASS__ . '_2.4.2';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Upgrade_DB_Strings_Add_Translation_Priority_Field implements IWPML_St_Upgrade_Command {
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$result = null;
|
||||
|
||||
$table_name = $this->wpdb->prefix . 'icl_strings';
|
||||
if ( 0 !== count( $this->wpdb->get_results( "SHOW TABLES LIKE '{$table_name}'" ) ) ) {
|
||||
$sql = "SHOW FIELDS FROM {$table_name} WHERE FIELD = 'translation_priority'";
|
||||
if ( 0 === count( $this->wpdb->get_results( $sql ) ) ) {
|
||||
$sql = "ALTER TABLE {$this->wpdb->prefix}icl_strings
|
||||
ADD COLUMN `translation_priority` varchar(160) NOT NULL";
|
||||
|
||||
$result = false !== $this->wpdb->query( $sql );
|
||||
}
|
||||
|
||||
if ( false !== $result ) {
|
||||
$sql = "SHOW KEYS FROM {$table_name} WHERE Key_name='icl_strings_translation_priority'";
|
||||
if ( 0 === count( $this->wpdb->get_results( $sql ) ) ) {
|
||||
$sql = "
|
||||
ALTER TABLE {$this->wpdb->prefix}icl_strings
|
||||
ADD INDEX `icl_strings_translation_priority` ( `translation_priority` ASC )
|
||||
";
|
||||
|
||||
$result = false !== $this->wpdb->query( $sql );
|
||||
} else {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public static function get_command_id() {
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Upgrade_Display_Strings_Scan_Notices implements IWPML_St_Upgrade_Command {
|
||||
/** @var WPML_ST_Themes_And_Plugins_Settings */
|
||||
private $settings;
|
||||
|
||||
/**
|
||||
* WPML_ST_Upgrade_Display_Strings_Scan_Notices constructor.
|
||||
*
|
||||
* @param WPML_ST_Themes_And_Plugins_Settings $settings
|
||||
*/
|
||||
public function __construct( WPML_ST_Themes_And_Plugins_Settings $settings ) {
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public static function get_command_id() {
|
||||
return __CLASS__;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$this->maybe_add_missing_setting();
|
||||
|
||||
return ! $this->settings->display_notices_setting_is_missing();
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private function maybe_add_missing_setting() {
|
||||
if ( $this->settings->display_notices_setting_is_missing() ) {
|
||||
$this->settings->create_display_notices_setting();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Upgrade_Migrate_Originals implements IWPML_St_Upgrade_Command {
|
||||
|
||||
/** @var wpdb $wpdb */
|
||||
private $wpdb;
|
||||
|
||||
/** @var SitePress sitepress */
|
||||
private $sitepress;
|
||||
|
||||
private $translations = array();
|
||||
private $not_translated = array();
|
||||
private $active_languages;
|
||||
|
||||
public function __construct( wpdb $wpdb, SitePress $sitepress ) {
|
||||
$this->wpdb = $wpdb;
|
||||
$this->sitepress = $sitepress;
|
||||
$active_languages = $this->sitepress->get_active_languages();
|
||||
foreach ( $active_languages as $lang ) {
|
||||
$this->active_languages[] = $lang['code'];
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_command_id() {
|
||||
return __CLASS__;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
if ( $this->is_migration_required() ) {
|
||||
if ( current_user_can( 'manage_options' ) ) {
|
||||
$this->sitepress->get_wp_api()->add_action( 'admin_notices', array( $this, 'update_message' ) );
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function update_message() {
|
||||
?>
|
||||
<div id="wpml-st-upgrade-migrate-originals" class="update-nag notice notice-info" style="display:block">
|
||||
<p>
|
||||
<?php esc_html_e( "WPML needs to update the database. This update will help improve WPML's performance when fetching translated strings.", 'wpml-string-translation' ); ?>
|
||||
<br /><br />
|
||||
<button class="wpml-st-upgrade-migrate-originals"><?php esc_html_e( 'Update Now', 'wpml-string-translation' ); ?></button> <span class="spinner" style="float: none"></span>
|
||||
</p>
|
||||
<?php wp_nonce_field( 'wpml-st-upgrade-migrate-originals-nonce', 'wpml-st-upgrade-migrate-originals-nonce' ); ?>
|
||||
</div>
|
||||
<div id="wpml-st-upgrade-migrate-originals-complete" class="update-nag notice notice-info" style="display:none">
|
||||
<p>
|
||||
<?php esc_html_e( 'The database has been updated.', 'wpml-string-translation' ); ?>
|
||||
<br /><br />
|
||||
<button class="wpml-st-upgrade-migrate-originals-close"><?php esc_html_e( 'Close', 'wpml-string-translation' ); ?></button>
|
||||
</p>
|
||||
<?php wp_nonce_field( 'wpml-st-upgrade-migrate-originals-nonce', 'wpml-st-upgrade-migrate-originals-nonce' ); ?>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
jQuery( function( $ ) {
|
||||
jQuery( '.wpml-st-upgrade-migrate-originals' ).click( function() {
|
||||
jQuery( this ).prop( 'disabled', true );
|
||||
jQuery( this ).parent().find( '.spinner' ).css( 'visibility', 'visible' );
|
||||
jQuery.ajax({
|
||||
url: ajaxurl,
|
||||
type: "POST",
|
||||
data: {
|
||||
action: 'wpml-st-upgrade-migrate-originals',
|
||||
nonce: jQuery( '#wpml-st-upgrade-migrate-originals-nonce' ).val()
|
||||
},
|
||||
success: function ( response ) {
|
||||
jQuery( '#wpml-st-upgrade-migrate-originals' ).hide();
|
||||
jQuery( '#wpml-st-upgrade-migrate-originals-complete' ).css( 'display', 'block' );
|
||||
}
|
||||
});
|
||||
});
|
||||
jQuery( '.wpml-st-upgrade-migrate-originals-close' ).click( function() {
|
||||
jQuery( '#wpml-st-upgrade-migrate-originals-complete' ).hide();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
|
||||
if ( $this->is_migration_required() ) {
|
||||
$this->get_strings_without_translations();
|
||||
$this->get_originals_with_translations();
|
||||
$this->migrate_translations();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function run_frontend() {}
|
||||
|
||||
|
||||
private function is_migration_required() {
|
||||
$query = "
|
||||
SELECT id
|
||||
FROM {$this->wpdb->prefix}icl_strings
|
||||
WHERE context LIKE 'plugin %' OR context LIKE 'theme %'
|
||||
LIMIT 1";
|
||||
$found = $this->wpdb->get_var( $query );
|
||||
return $found > 0;
|
||||
}
|
||||
|
||||
private function get_strings_without_translations() {
|
||||
|
||||
foreach ( $this->active_languages as $lang ) {
|
||||
$res_args = array( $lang, $lang );
|
||||
|
||||
$res_query = "
|
||||
SELECT
|
||||
s.value,
|
||||
s.id
|
||||
FROM {$this->wpdb->prefix}icl_strings s
|
||||
WHERE s.id NOT IN (
|
||||
SELECT st.string_id FROM {$this->wpdb->prefix}icl_string_translations st
|
||||
WHERE st.language=%s
|
||||
)
|
||||
AND s.language!=%s
|
||||
";
|
||||
$res_prepare = $this->wpdb->prepare( $res_query, $res_args );
|
||||
$this->not_translated[ $lang ] = $this->wpdb->get_results( $res_prepare, ARRAY_A );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function get_originals_with_translations() {
|
||||
|
||||
foreach ( $this->active_languages as $lang ) {
|
||||
$res_args = array( ICL_TM_COMPLETE, $lang );
|
||||
|
||||
$res_query = "
|
||||
SELECT
|
||||
st.value AS tra,
|
||||
s.value AS org
|
||||
FROM {$this->wpdb->prefix}icl_strings s
|
||||
LEFT JOIN {$this->wpdb->prefix}icl_string_translations st
|
||||
ON s.id=st.string_id
|
||||
WHERE st.status=%d AND st.language=%s
|
||||
";
|
||||
$res_prepare = $this->wpdb->prepare( $res_query, $res_args );
|
||||
$result = $this->wpdb->get_results( $res_prepare, ARRAY_A );
|
||||
$strings = array();
|
||||
foreach ( $result as $string ) {
|
||||
$strings[ $string['org'] ] = $string['tra'];
|
||||
}
|
||||
$this->translations[ $lang ] = $strings;
|
||||
}
|
||||
}
|
||||
|
||||
private function migrate_translations() {
|
||||
|
||||
foreach ( $this->active_languages as $lang ) {
|
||||
foreach ( $this->not_translated[ $lang ] as $not_translated ) {
|
||||
|
||||
if ( isset( $this->translations[ $lang ][ $not_translated['value'] ] ) ) {
|
||||
icl_add_string_translation( $not_translated['id'], $lang, $this->translations[ $lang ][ $not_translated['value'] ], ICL_TM_COMPLETE );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Upgrade_MO_Scanning implements IWPML_St_Upgrade_Command {
|
||||
/** @var wpdb $wpdb */
|
||||
private $wpdb;
|
||||
|
||||
private static $sql = "
|
||||
CREATE TABLE `PREFIXicl_mo_files_domains` (
|
||||
`id` int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
|
||||
`file_path` varchar(250) NOT NULL,
|
||||
`file_path_md5` varchar(32) NOT NULL,
|
||||
`domain` varchar(160) NOT NULL,
|
||||
`status` varchar(20) NOT NULL DEFAULT %s,
|
||||
`num_of_strings` int(11) NOT NULL DEFAULT '0',
|
||||
`last_modified` int(11) NOT NULL,
|
||||
`component_type` enum('plugin','theme','other') NOT NULL DEFAULT 'other',
|
||||
`component_id` varchar(100) DEFAULT NULL,
|
||||
UNIQUE KEY `file_path_md5_UNIQUE` (`file_path_md5`)
|
||||
)
|
||||
";
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
|
||||
public function run() {
|
||||
return $this->create_table() && $this->add_mo_value_field_if_does_not_exist();
|
||||
}
|
||||
|
||||
private function create_table() {
|
||||
$table_name = $this->wpdb->prefix . 'icl_mo_files_domains';
|
||||
$this->wpdb->query( "DROP TABLE IF EXISTS `{$table_name}`" );
|
||||
|
||||
$sql = str_replace( 'PREFIX', $this->wpdb->prefix, self::$sql );
|
||||
$sql = $this->wpdb->prepare(
|
||||
$sql,
|
||||
array( WPML_ST_Translations_File_Entry::NOT_IMPORTED )
|
||||
);
|
||||
|
||||
$sql .= $this->get_charset_collate();
|
||||
|
||||
return false !== $this->wpdb->query( $sql );
|
||||
}
|
||||
|
||||
private function add_mo_value_field_if_does_not_exist() {
|
||||
$result = true;
|
||||
|
||||
$table_name = $this->wpdb->prefix . 'icl_string_translations';
|
||||
if ( 0 === count( $this->wpdb->get_results( "SHOW COLUMNS FROM `{$table_name}` LIKE 'mo_string'" ) ) ) {
|
||||
$sql = "
|
||||
ALTER TABLE {$table_name}
|
||||
ADD COLUMN `mo_string` TEXT NULL DEFAULT NULL AFTER `value`;
|
||||
";
|
||||
|
||||
$result = false !== $this->wpdb->query( $sql );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function run_ajax() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public function run_frontend() {
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public static function get_command_id() {
|
||||
return __CLASS__ . '_4' ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_charset_collate() {
|
||||
$charset_collate = '';
|
||||
if ( method_exists( $this->wpdb, 'has_cap' ) && $this->wpdb->has_cap( 'collation' ) ) {
|
||||
$charset_collate = $this->wpdb->get_charset_collate();
|
||||
}
|
||||
|
||||
return $charset_collate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Upgrade_String_Index {
|
||||
/** @var wpdb */
|
||||
private $wpdb;
|
||||
|
||||
const OPTION_NAME = 'wpml_string_table_ok_for_mo_import';
|
||||
|
||||
/**
|
||||
* @param wpdb $wpdb
|
||||
*/
|
||||
public function __construct( wpdb $wpdb ) {
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
public function is_uc_domain_name_context_index_unique() {
|
||||
$key_exists = get_option( self::OPTION_NAME );
|
||||
if ( ! $key_exists ) {
|
||||
$sql = "SHOW KEYS FROM {$this->wpdb->prefix}icl_strings WHERE Key_name='uc_domain_name_context_md5' AND Non_unique = 0";
|
||||
|
||||
$key_exists = 0 < count( $this->wpdb->get_results( $sql ) ) ? 'yes' : 'no';
|
||||
update_option( self::OPTION_NAME, $key_exists, true );
|
||||
}
|
||||
|
||||
return 'yes' === $key_exists;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
/**
|
||||
* WPML_ST_Upgrade class file.
|
||||
*
|
||||
* @package wpml-string-translation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WPML_ST_Upgrade
|
||||
*/
|
||||
class WPML_ST_Upgrade {
|
||||
|
||||
const TRANSIENT_UPGRADE_IN_PROGRESS = 'wpml_st_upgrade_in_progress';
|
||||
|
||||
/**
|
||||
* SitePress instance.
|
||||
*
|
||||
* @var SitePress $sitepress
|
||||
*/
|
||||
private $sitepress;
|
||||
|
||||
/**
|
||||
* Upgrade Command Factory instance.
|
||||
*
|
||||
* @var WPML_ST_Upgrade_Command_Factory
|
||||
*/
|
||||
private $command_factory;
|
||||
|
||||
/**
|
||||
* Upgrade in progress flag.
|
||||
*
|
||||
* @var bool $upgrade_in_progress
|
||||
*/
|
||||
private $upgrade_in_progress;
|
||||
|
||||
/**
|
||||
* WPML_ST_Upgrade constructor.
|
||||
*
|
||||
* @param SitePress $sitepress SitePress instance.
|
||||
* @param WPML_ST_Upgrade_Command_Factory|null $command_factory Upgrade Command Factory instance.
|
||||
*/
|
||||
public function __construct( SitePress $sitepress, WPML_ST_Upgrade_Command_Factory $command_factory = null ) {
|
||||
$this->sitepress = $sitepress;
|
||||
$this->command_factory = $command_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run upgrade.
|
||||
*/
|
||||
public function run() {
|
||||
if ( get_transient( self::TRANSIENT_UPGRADE_IN_PROGRESS ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $this->sitepress->get_wp_api()->is_admin() ) {
|
||||
if ( $this->sitepress->get_wp_api()->constant( 'DOING_AJAX' ) ) {
|
||||
$this->run_ajax();
|
||||
} else {
|
||||
$this->run_admin();
|
||||
}
|
||||
} else {
|
||||
$this->run_front_end();
|
||||
}
|
||||
|
||||
$this->set_upgrade_completed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run admin.
|
||||
*/
|
||||
private function run_admin() {
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_Migrate_Originals' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_Display_Strings_Scan_Notices' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_DB_String_Packages' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_MO_Scanning' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_DB_String_Name_Index' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_DB_Longtext_String_Value' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_DB_Strings_Add_Translation_Priority_Field' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_DB_String_Packages_Word_Count' );
|
||||
$this->maybe_run( '\WPML\ST\Upgrade\Command\RegenerateMoFilesWithStringNames' );
|
||||
$this->maybe_run( \WPML\ST\Upgrade\Command\MigrateMultilingualWidgets::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run ajax.
|
||||
*/
|
||||
private function run_ajax() {
|
||||
$this->maybe_run_ajax( 'WPML_ST_Upgrade_Migrate_Originals' );
|
||||
|
||||
// It has to be maybe_run.
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_MO_Scanning' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_DB_String_Packages_Word_Count' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run on frontend.
|
||||
*/
|
||||
private function run_front_end() {
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_MO_Scanning' );
|
||||
$this->maybe_run( 'WPML_ST_Upgrade_DB_String_Packages_Word_Count' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe run command.
|
||||
*
|
||||
* @param string $class Command class name.
|
||||
*/
|
||||
private function maybe_run( $class ) {
|
||||
if ( ! $this->has_command_been_executed( $class ) ) {
|
||||
$this->set_upgrade_in_progress();
|
||||
$upgrade = $this->command_factory->create( $class );
|
||||
if ( $upgrade->run() ) {
|
||||
$this->mark_command_as_executed( $class );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe run command in ajax.
|
||||
*
|
||||
* @param string $class Command class name.
|
||||
*/
|
||||
private function maybe_run_ajax( $class ) {
|
||||
if ( ! $this->has_command_been_executed( $class ) ) {
|
||||
$this->run_ajax_command( $class );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run command in ajax.
|
||||
*
|
||||
* @param string $class Command class name.
|
||||
*/
|
||||
private function run_ajax_command( $class ) {
|
||||
if ( $this->nonce_ok( $class ) ) {
|
||||
$upgrade = $this->command_factory->create( $class );
|
||||
if ( $upgrade->run_ajax() ) {
|
||||
$this->mark_command_as_executed( $class );
|
||||
$this->sitepress->get_wp_api()->wp_send_json_success( '' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check nonce.
|
||||
*
|
||||
* @param string $class Command class name.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function nonce_ok( $class ) {
|
||||
$ok = false;
|
||||
|
||||
$class = strtolower( $class );
|
||||
$class = str_replace( '_', '-', $class );
|
||||
if ( isset( $_POST['action'] ) && $_POST['action'] === $class ) {
|
||||
$nonce = $this->filter_nonce_parameter();
|
||||
if ( $this->sitepress->get_wp_api()->wp_verify_nonce( $nonce, $class . '-nonce' ) ) {
|
||||
$ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if command was executed.
|
||||
*
|
||||
* @param string $class Command class name.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_command_been_executed( $class ) {
|
||||
$id = call_user_func( [ $class, 'get_command_id' ] );
|
||||
$settings = $this->sitepress->get_setting( 'st', [] );
|
||||
|
||||
return isset( $settings[ $id . '_has_run' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark command as executed.
|
||||
*
|
||||
* @param string $class Command class name.
|
||||
*/
|
||||
public function mark_command_as_executed( $class ) {
|
||||
$id = call_user_func( [ $class, 'get_command_id' ] );
|
||||
$settings = $this->sitepress->get_setting( 'st', [] );
|
||||
$settings[ $id . '_has_run' ] = true;
|
||||
$this->sitepress->set_setting( 'st', $settings, true );
|
||||
wp_cache_flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter nonce.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function filter_nonce_parameter() {
|
||||
return filter_input( INPUT_POST, 'nonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flag that upgrade is in process.
|
||||
*/
|
||||
private function set_upgrade_in_progress() {
|
||||
if ( ! $this->upgrade_in_progress ) {
|
||||
$this->upgrade_in_progress = true;
|
||||
set_transient( self::TRANSIENT_UPGRADE_IN_PROGRESS, true, MINUTE_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark upgrade as completed.
|
||||
*/
|
||||
private function set_upgrade_completed() {
|
||||
if ( $this->upgrade_in_progress ) {
|
||||
$this->upgrade_in_progress = false;
|
||||
delete_transient( self::TRANSIENT_UPGRADE_IN_PROGRESS );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
interface IWPML_St_Upgrade_Command {
|
||||
|
||||
public function run();
|
||||
|
||||
public function run_ajax();
|
||||
|
||||
public function run_frontend();
|
||||
|
||||
public static function get_command_id();
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
class WPML_ST_Repair_Strings_Schema {
|
||||
|
||||
const OPTION_HAS_RUN = 'wpml_st_repair_string_schema_has_run';
|
||||
|
||||
/** @var IWPML_St_Upgrade_Command $upgrade_command */
|
||||
private $upgrade_command;
|
||||
|
||||
/** @var WPML_Notices $notices */
|
||||
private $notices;
|
||||
|
||||
/** @var array $args */
|
||||
private $args;
|
||||
|
||||
/** @var string $db_error */
|
||||
private $db_error;
|
||||
|
||||
/** @var array $has_run */
|
||||
private $has_run = array();
|
||||
|
||||
public function __construct( WPML_Notices $notices, array $args, $db_error ) {
|
||||
$this->notices = $notices;
|
||||
$this->args = $args;
|
||||
$this->db_error = $db_error;
|
||||
}
|
||||
|
||||
public function set_command( IWPML_St_Upgrade_Command $upgrade_command ) {
|
||||
$this->upgrade_command = $upgrade_command;
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function run() {
|
||||
$this->has_run = get_option( self::OPTION_HAS_RUN, array() );
|
||||
|
||||
if ( ! $this->upgrade_command || array_key_exists( $this->get_command_id(), $this->has_run ) ) {
|
||||
$this->add_notice();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->run_upgrade_command() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->add_notice();
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
private function run_upgrade_command() {
|
||||
if ( ! $this->acquire_lock() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = $this->upgrade_command->run();
|
||||
$this->has_run[ $this->get_command_id() ] = true;
|
||||
update_option( self::OPTION_HAS_RUN, $this->has_run, false );
|
||||
|
||||
$this->release_lock();
|
||||
|
||||
return (bool) $success;
|
||||
}
|
||||
|
||||
private function get_command_id() {
|
||||
return get_class( $this->upgrade_command );
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
private function acquire_lock() {
|
||||
if ( get_transient( WPML_ST_Upgrade::TRANSIENT_UPGRADE_IN_PROGRESS ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_transient( WPML_ST_Upgrade::TRANSIENT_UPGRADE_IN_PROGRESS, true, MINUTE_IN_SECONDS );
|
||||
return true;
|
||||
}
|
||||
|
||||
private function release_lock() {
|
||||
delete_transient( WPML_ST_Upgrade::TRANSIENT_UPGRADE_IN_PROGRESS );
|
||||
}
|
||||
|
||||
private function add_notice() {
|
||||
$text = '<p>' . sprintf(
|
||||
esc_html__( 'We have detected a problem with some tables in the database. Please contact %1$sWPML support%2$s to get this fixed.', 'wpml-string-translation' ),
|
||||
'<a href="https://wpml.org/forums/forum/english-support/" class="otgs-external-link" rel="noopener" target="_blank">',
|
||||
'</a>'
|
||||
) . '</p>';
|
||||
|
||||
$text .= '<pre>' . $this->db_error . '</pre>';
|
||||
|
||||
if ( $this->upgrade_command ) {
|
||||
$notice_id = $this->get_command_id();
|
||||
} else {
|
||||
$notice_id = 'default';
|
||||
$text .= '<pre>' . print_r( $this->args, true ) . '</pre>';
|
||||
}
|
||||
|
||||
$notice = $this->notices->create_notice( $notice_id, $text, __CLASS__ );
|
||||
$notice->set_hideable( true );
|
||||
$notice->set_css_class_types( array( 'notice-error' ) );
|
||||
$this->notices->add_notice( $notice );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user