-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jeremy - Zeus
committed
Feb 10, 2023
1 parent
d7850a8
commit c0dd620
Showing
18 changed files
with
2,255 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
|
||
namespace WpmlToPolylangMigration; | ||
|
||
// Deny direct access | ||
if (!defined('ABSPATH')) { | ||
header("HTTP/1.0 404 Not Found"); | ||
exit(); | ||
} | ||
|
||
/** | ||
* Cron to process the actual migration since this will be very heavy and can timeout or be shutdown on ASGs. | ||
*/ | ||
class Cron { | ||
|
||
const HOOK_NAME = 'wpml_to_polylang_migration'; | ||
|
||
/** | ||
* Registers the cron hook. | ||
* MUST ALWAYS be registered for it to work. | ||
* @return void | ||
*/ | ||
public function __construct() { | ||
add_action(self::HOOK_NAME, __CLASS__ . '::process'); | ||
} | ||
|
||
/** | ||
* Callback used to trigger the cron process. | ||
* @return void | ||
*/ | ||
public static function process() { | ||
(new Processor())->run(); | ||
} | ||
|
||
/** | ||
* Schedules the cron single event. | ||
* @return void | ||
*/ | ||
public static function schedule() { | ||
if (false === \wp_next_scheduled(self::HOOK_NAME)) { | ||
\wp_schedule_single_event(time(), self::HOOK_NAME); | ||
} | ||
} | ||
|
||
/** | ||
* Clears to cron single event. | ||
* @return void | ||
*/ | ||
public static function clear() { | ||
\wp_clear_scheduled_hook(self::HOOK_NAME); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
<?php | ||
|
||
namespace WpmlToPolylangMigration; | ||
|
||
// Deny direct access | ||
if (!defined('ABSPATH')) { | ||
header('HTTP/1.0 404 Not Found'); | ||
exit(); | ||
} | ||
|
||
if (false === defined('WPML_TO_POLYLANG_SCRIPT_TIMEOUT_IN_SECONDS')) { | ||
define('WPML_TO_POLYLANG_SCRIPT_TIMEOUT_IN_SECONDS', 7200); // never use 0. | ||
} | ||
if (false === defined('WPML_TO_POLYLANG_QUERY_BATCH_SIZE')) { | ||
define('WPML_TO_POLYLANG_QUERY_BATCH_SIZE', 5000); | ||
} | ||
|
||
/** | ||
* Responsible for processing the actual import process from WPML to PolyLang. | ||
*/ | ||
class Processor { | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static array $_wpmlSettings; | ||
|
||
/** | ||
* @return \PLL_Admin_Model | ||
*/ | ||
private static object $_polylangModel; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static array $_polylangSettings; | ||
|
||
/** | ||
* Option name for WPML settings in the options table. | ||
*/ | ||
const OPTIONS_KEY_NAME_WPML = 'icl_sitepress_settings'; | ||
|
||
/** | ||
* Option name for Polylang settings in the options table. | ||
*/ | ||
const OPTIONS_KEY_NAME_POLYLANG = 'polylang'; | ||
|
||
/** | ||
* Constructor. | ||
*/ | ||
public function __construct() { | ||
self::$_wpmlSettings = \get_option(self::OPTIONS_KEY_NAME_WPML); | ||
self::$_polylangModel = PLL()->model; | ||
self::$_polylangSettings = \get_option(self::OPTIONS_KEY_NAME_POLYLANG) ?? []; | ||
|
||
// Sanity checks to ensure Polylang objects are available. | ||
if (empty(self::$_wpmlSettings) || empty(self::$_polylangModel) || !function_exists('PLL')) { | ||
Status::update(Status::STATUS_ERRORED); | ||
return; | ||
} | ||
|
||
// Flush since results will be cached as empty (since languages do not exist yet) from the user's visit on the Tools page. | ||
\wp_cache_flush(); | ||
|
||
// Disable WP cache additions (prevents caching issues with migration) | ||
\wp_suspend_cache_addition(true); | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public static function getWpmlSettings(): array { | ||
return self::$_wpmlSettings; | ||
} | ||
|
||
/** | ||
* @return \PLL_Admin_Model | ||
*/ | ||
public static function getPolylangModel(): object { | ||
return self::$_polylangModel; | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public static function getPolylangSettings(): array { | ||
return self::$_polylangSettings; | ||
} | ||
|
||
/** | ||
* @param array $polylangSettings The full settings array for Polylang. | ||
*/ | ||
public static function setPolylangSettings(array $polylangSettings): void { | ||
self::$_polylangSettings = $polylangSettings; | ||
\update_option(self::OPTIONS_KEY_NAME_POLYLANG, $polylangSettings); | ||
|
||
// IMPORTANT: Options are cached in Polylang, we need to force them to the new settings (otherwise it will process with invalid option values). | ||
Processor::getPolylangModel()->options = $polylangSettings; | ||
} | ||
|
||
/** | ||
* Dispatches the different import steps. | ||
* @return void | ||
*/ | ||
public function run() { | ||
set_time_limit(WPML_TO_POLYLANG_SCRIPT_TIMEOUT_IN_SECONDS); // never use 0. | ||
|
||
if (empty(self::getPolylangModel()->get_languages_list())) { | ||
(new Processors\Languages())->process(); // Languages are REQUIRED fist as other processors require the language term ids. | ||
} | ||
(new Processors\Options())->process(); | ||
(new Processors\PostTypes())->process(); | ||
(new Processors\Taxonomies())->process(); | ||
(new Processors\Menus())->process(); | ||
(new Processors\NoLangObjects())->process(); | ||
(new Processors\Strings())->process(); | ||
|
||
// Complete the process, | ||
$this->complete(); | ||
} | ||
|
||
/** | ||
* Completes the import process. | ||
* @return void | ||
*/ | ||
protected function complete() { | ||
// Re-enable WP cache additions | ||
\wp_suspend_cache_addition(false); | ||
|
||
// Force cache deletion. | ||
\wp_cache_flush(); | ||
|
||
// Update language counts. | ||
/* @var $lang \PLL_Language */ | ||
foreach (PLL()->model->get_languages_list() as $lang) { | ||
$lang->update_count(); // Doesn't seem to work... cache? | ||
} | ||
|
||
// Remove the Polylang wizard notice as it isn't needed or expected after import is complete. | ||
if (class_exists('PLL_Admin_Notices')) { | ||
\PLL_Admin_Notices::dismiss('wizard'); | ||
} | ||
|
||
// Flush rewrite rules (WPML doesn't use them but Polylang does). | ||
\flush_rewrite_rules(); | ||
|
||
// Flag as complete. | ||
Status::update(Status::STATUS_COMPLETED); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<?php | ||
|
||
namespace WpmlToPolylangMigration\Processors; | ||
|
||
use WpmlToPolylangMigration\Status; | ||
|
||
// Deny direct access | ||
if (!defined('ABSPATH')) { | ||
header("HTTP/1.0 404 Not Found"); | ||
exit(); | ||
} | ||
|
||
/** | ||
* Abstract Content Type Processor. | ||
*/ | ||
abstract class AbstractContentType extends AbstractProcessorSteppable { | ||
use Traits\TContentType; | ||
|
||
/** | ||
* Cache for Polylang's language term taxonomies. | ||
* @var ?array | ||
*/ | ||
protected ?array $polylangLanguages = NULL; | ||
|
||
/** | ||
* Gets the WPML term translations for this content type. | ||
* @param int[] $trids WPML translation ids. | ||
* @return array | ||
*/ | ||
abstract protected function getWPMLTranslations(array $trids): array; | ||
|
||
/** | ||
* Gets the Polylang languages taxonomy term ids for this content type. | ||
* @return array | ||
*/ | ||
abstract protected function getPolylangLanguageTaxonomyTermIds(): array; | ||
|
||
|
||
public function __construct() { | ||
if ($this->polylangLanguages === NULL) { | ||
$this->polylangLanguages = $this->getPolylangLanguageTaxonomyTermIds(); | ||
} | ||
} | ||
|
||
/** | ||
* Content type specific process. | ||
* @return void | ||
*/ | ||
public function process(): void { | ||
// Make sure we have languages. | ||
if (empty($this->polylangLanguages)) { | ||
Status::update(Status::STATUS_ERRORED); | ||
return; | ||
} | ||
|
||
// MUST call parent process. | ||
parent::process(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php | ||
|
||
namespace WpmlToPolylangMigration\Processors; | ||
|
||
// Deny direct access | ||
if (!defined('ABSPATH')) { | ||
header('HTTP/1.0 404 Not Found'); | ||
exit(); | ||
} | ||
|
||
/** | ||
* Abstract class for steppable processors. | ||
* Steppable is a paginated processor to handle processing large volumes | ||
*/ | ||
abstract class AbstractProcessorSteppable { | ||
|
||
/** | ||
* Current step being processed. | ||
* @var int | ||
*/ | ||
protected int $step = 1; | ||
|
||
/** | ||
* Cache for the total. | ||
* @var ?int | ||
*/ | ||
protected ?int $total = NULL; | ||
|
||
/** | ||
* Returns the total number of items to process. | ||
* @return int | ||
*/ | ||
abstract protected function getTotal(): int; | ||
|
||
/** | ||
* Returns the data to process. | ||
* @return array | ||
*/ | ||
abstract protected function getData(): array; | ||
|
||
/** | ||
* Processes the data. | ||
* @param array $data | ||
* @return void | ||
*/ | ||
abstract protected function processData(array $data): void; | ||
|
||
/** | ||
* Process as steppable. | ||
* @return void | ||
*/ | ||
public function process(): void { | ||
// Do the processes in pages (steps). | ||
while ($data = $this->getData()) { | ||
$this->processData($data); | ||
$this->incrementStep(); // Do this last. | ||
} | ||
} | ||
|
||
/** | ||
* @return int | ||
*/ | ||
protected function getStep(): int { | ||
return $this->step; | ||
} | ||
|
||
/** | ||
* @return void | ||
*/ | ||
protected function incrementStep(): void { | ||
$this->step++; | ||
} | ||
|
||
/** | ||
* Returns the action completion percentage. | ||
* @return int | ||
*/ | ||
protected function getPercentage(): int { | ||
$percentage = 100.00; | ||
|
||
if ($this->total === NULL) { | ||
$this->total = $this->getTotal(); | ||
} | ||
|
||
if ($this->total) { | ||
$percentage = ($this->getStep() * WPML_TO_POLYLANG_QUERY_BATCH_SIZE) / $this->total * 100; | ||
$percentage = round(floatval($percentage), 2); | ||
} | ||
|
||
return (int)min($percentage, 100.00); | ||
} | ||
|
||
} |
Oops, something went wrong.