Skip to content

Commit

Permalink
chore: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy - Zeus committed Feb 10, 2023
1 parent d7850a8 commit c0dd620
Show file tree
Hide file tree
Showing 18 changed files with 2,255 additions and 0 deletions.
52 changes: 52 additions & 0 deletions lib/Cron.php
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);
}
}
151 changes: 151 additions & 0 deletions lib/Processor.php
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);
}

}
59 changes: 59 additions & 0 deletions lib/Processors/AbstractContentType.php
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();
}
}
93 changes: 93 additions & 0 deletions lib/Processors/AbstractProcessorSteppable.php
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);
}

}
Loading

0 comments on commit c0dd620

Please sign in to comment.