'wowm-plugin', 'utm_medium' => $medium, 'utm_campaign' => 'winonweb-migrate'], self::SITE) . ($anchor ? '#' . $anchor : '');
}
const ICONS = [
'database' => '',
'uploads' => '',
'themes' => '',
'plugins' => '',
'muplugins' => '',
'other' => '',
'export' => '',
'import' => '',
'backups' => '',
'schedule' => '',
'download' => '',
'restore' => '',
'trash' => '',
'check' => '',
'x' => '',
'arrow' => '',
'warning' => '',
'info' => '',
'error' => '',
'plus' => '',
'globe' => '',
'shield' => '',
'file' => '',
'chevron' => '',
'login' => '',
'help' => '',
'tools' => '',
'search' => '',
'undo' => '',
'layers' => '',
];
public static function init(): void {
add_action('admin_menu', [self::class, 'menu']);
add_action('admin_enqueue_scripts', [self::class, 'assets']);
add_filter('plugin_action_links_' . Plugin::basename(), function ($links) {
array_unshift($links, '' . esc_html__('Open', 'winonweb-migrate') . '');
return $links;
});
add_filter(
'plugin_row_meta',
function ($meta, $file) {
if ($file === Plugin::basename()) {
$meta[] = '' . esc_html__('Guide', 'winonweb-migrate') . '';
$meta[] = '' . esc_html__('Migration help from WinOnWeb', 'winonweb-migrate') . '';
}
return $meta;
},
10,
2,
);
}
public static function icon(string $name, int $size = 20): string {
return sprintf('', $size, self::ICONS[$name] ?? '');
}
public static function the_icon(string $name, int $size = 20): void {
$shape = ['d' => true, 'cx' => true, 'cy' => true, 'r' => true, 'rx' => true, 'ry' => true, 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'points' => true, 'x1' => true, 'x2' => true, 'y1' => true, 'y2' => true];
echo wp_kses(self::icon($name, $size), [
'svg' => ['class' => true, 'width' => true, 'height' => true, 'viewbox' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'aria-hidden' => true],
'path' => $shape,
'circle' => $shape,
'ellipse' => $shape,
'rect' => $shape,
'line' => $shape,
'polyline' => $shape,
]);
}
public static function menu(): void {
$icon = 'data:image/svg+xml;base64,' . base64_encode('');
add_menu_page(__('WinOnWeb Migrate', 'winonweb-migrate'), __('Migrate', 'winonweb-migrate'), 'manage_options', self::PAGE, [self::class, 'render'], $icon, 80);
}
public static function assets(string $hook): void {
if ($hook !== 'toplevel_page_' . self::PAGE) {
return;
}
// Before the page data is built, so an expired undo is not offered.
Rollback::expire();
$css = WOWM_DIR . 'assets/admin.css';
$js = WOWM_DIR . 'assets/admin.js';
wp_enqueue_style('wowm-admin', WOWM_URL . 'assets/admin.css', [], WOWM_VERSION . '.' . filemtime($css));
wp_enqueue_script('wowm-admin', WOWM_URL . 'assets/admin.js', [], WOWM_VERSION . '.' . filemtime($js), true);
$max_upload = wp_max_upload_size();
wp_localize_script('wowm-admin', 'WOWM', [
'ajax' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce(Ajax::NONCE),
'chunk' => (int) max(256 * KB_IN_BYTES, min(8 * MB_IN_BYTES, $max_upload * 0.8)),
'home' => untrailingslashit((string) get_option('home')),
'login' => wp_login_url(admin_url('admin.php?page=' . self::PAGE)),
'backups' => Ajax::backups_payload(),
'schedule' => ['settings' => Scheduler::settings(), 'next' => Scheduler::next_run()],
'rollback' => self::rollback_payload(),
'lastImport' => Rollback::last_import(),
'activeJob' => Scheduler::active(),
'now' => time(),
'icons' => array_map(function ($svg) {
return '';
}, self::ICONS),
'i18n' => [
'calculating' => __('Calculating…', 'winonweb-migrate'),
'convertTitle' => /* translators: %s: plugin the archive was made with, e.g. Duplicator */ __('Converting the %s archive', 'winonweb-migrate'),
'checksTitle' => __('Server check', 'winonweb-migrate'),
'rollbackTitle' => /* translators: %s: relative time, e.g. "2 hours ago" */ __('Import finished %s', 'winonweb-migrate'),
'rollbackText' => __('The previous database is kept for 7 days, so this import can be undone. Is the site working as expected?', 'winonweb-migrate'),
'rollbackKeep' => __('Everything works, remove the old database', 'winonweb-migrate'),
'rollbackUndo' => __('Undo import', 'winonweb-migrate'),
'confirmUndo' => __('Put the previous database back? The imported database is discarded. Files stay as they are (use the safety backup to restore them).', 'winonweb-migrate'),
'undone' => __('The previous database is back. Log in again.', 'winonweb-migrate'),
'kept' => __('Old database removed', 'winonweb-migrate'),
'undoNote' => __('Not happy with the result? "Undo import" at the top of this page brings the previous database back.', 'winonweb-migrate'),
'healthTitle' => __('Site check after import', 'winonweb-migrate'),
'healthOk' => __('The site answers normally.', 'winonweb-migrate'),
'healthBroken' => __('The site shows an error after this import.', 'winonweb-migrate'),
/* translators: %s: countdown like 9:41 */
'autoUndoIn' => __('The previous database comes back automatically in %s unless you keep the import.', 'winonweb-migrate'),
'autoUndoOff' => __('Automatic undo is off: use "Undo import" or "Keep" below.', 'winonweb-migrate'),
'keepAnyway' => __('Keep the import anyway', 'winonweb-migrate'),
'undoNow' => __('Undo import now', 'winonweb-migrate'),
'recheck' => __('Check again', 'winonweb-migrate'),
'recoveryTitle' => __('If wp-admin stops opening', 'winonweb-migrate'),
'recoveryText' => __('When a plugin or theme causes a critical error, WordPress emails the site administrator a recovery-mode link. Log in through it and click "Undo import". From the command line:', 'winonweb-migrate'),
'recoveryCli' => 'wp wowm undo',
'undoneTitle' => __('Import undone', 'winonweb-migrate'),
'undoneText' => __('The login page showed a critical error right after the import, so the previous database was put back automatically. Fix the cause shown below (usually the PHP version or a plugin) and import again. Files from the archive stayed in place; the backup made before the import restores them.', 'winonweb-migrate'),
'deactivatedTitle' => __('Deactivated after import', 'winonweb-migrate'),
'deactivatedText' => __('Their files are not on this server or they belong to the previous host. Reinstall or re-enable them from Plugins if you need them.', 'winonweb-migrate'),
'autoUndo' => __('If the site shows a critical error after the import, undo it automatically: at once when wp-admin cannot open, otherwise after 10 minutes (recommended)', 'winonweb-migrate'),
'resumeTitle' => __('Continue the interrupted upload?', 'winonweb-migrate'),
/* translators: 1: file name, 2: size already uploaded */
'resumeText' => __('%1$s: %2$s already on the server. Pick the same file to continue from there.', 'winonweb-migrate'),
'resumeDiscard' => __('Start over', 'winonweb-migrate'),
/* translators: %s: size already uploaded */
'resuming' => __('Resuming from %s…', 'winonweb-migrate'),
'reattachTitle' => __('Still running in the background', 'winonweb-migrate'),
'reattachText' => __('This job kept going while the page was closed. Reconnected to its progress.', 'winonweb-migrate'),
'backgroundNote' => __('You can close this tab: the job continues on the server and this page reconnects when you come back.', 'winonweb-migrate'),
'differential' => __('Changes only', 'winonweb-migrate'),
'basedOn' => /* translators: %s: date of the full backup */ __('on top of the backup from %s', 'winonweb-migrate'),
'confirmDeleteChain' => /* translators: %d: number of changes-only backups */ __('Delete this full backup? Its %d changes-only backups are deleted too, they cannot be restored without it.', 'winonweb-migrate'),
'baseNone' => __('Everything (full backup)', 'winonweb-migrate'),
'baseChanges' => /* translators: %s: date of the full backup */ __('Only files changed since %s', 'winonweb-migrate'),
'scanNeedle' => __('Old address, e.g. old-domain.com', 'winonweb-migrate'),
'scanning' => /* translators: %s: progress in percent */ __('Scanning… %s%%', 'winonweb-migrate'),
'noHits' => __('Nothing found. The database is clean.', 'winonweb-migrate'),
'hitsTitle' => /* translators: %s: number of rows */ __('Found in %s places', 'winonweb-migrate'),
'table' => __('Table', 'winonweb-migrate'),
'column' => __('Column', 'winonweb-migrate'),
'matches' => __('Rows', 'winonweb-migrate'),
'replaceTitle' => __('Replacing in the database', 'winonweb-migrate'),
'replaceDone' => __('Replacement finished', 'winonweb-migrate'),
'replaceResult' => /* translators: %s: number of rows */ __('%s rows updated.', 'winonweb-migrate'),
'confirmReplace' => /* translators: 1: text to find, 2: replacement */ __('Replace "%1$s" with "%2$s" in the whole database? A database backup is made first.', 'winonweb-migrate'),
'replaceYes' => __('Replace', 'winonweb-migrate'),
'dbBackupTitle' => __('Backing up the database first', 'winonweb-migrate'),
'changed' => __('Changed', 'winonweb-migrate'),
'counting' => __('counting…', 'winonweb-migrate'),
'excludeFolder' => __('Click to leave this out of the archive', 'winonweb-migrate'),
'includeFolder' => __('Excluded. Click to include it again', 'winonweb-migrate'),
'exportTitle' => __('Exporting your site', 'winonweb-migrate'),
'importTitle' => __('Importing archive', 'winonweb-migrate'),
'preBackupTitle' => __('Backing up this site first', 'winonweb-migrate'),
'restoreTitle' => __('Restoring backup', 'winonweb-migrate'),
'exportDone' => __('Your archive is ready', 'winonweb-migrate'),
'importDone' => __('Import complete', 'winonweb-migrate'),
'failed' => __('Something went wrong', 'winonweb-migrate'),
'cancelled' => __('Cancelled', 'winonweb-migrate'),
'cancel' => __('Cancel', 'winonweb-migrate'),
'cancelling' => __('Cancelling…', 'winonweb-migrate'),
'close' => __('Close', 'winonweb-migrate'),
'download' => __('Download', 'winonweb-migrate'),
'restore' => __('Restore', 'winonweb-migrate'),
'delete' => __('Delete', 'winonweb-migrate'),
'login' => __('Log in again', 'winonweb-migrate'),
'viewSite' => __('View site', 'winonweb-migrate'),
'loginNote' => __('Users and passwords now come from the imported site. Log in with an account from that site.', 'winonweb-migrate'),
'elapsed' => __('Elapsed', 'winonweb-migrate'),
'tables' => __('Tables', 'winonweb-migrate'),
'rows' => __('Rows', 'winonweb-migrate'),
'files' => __('Files', 'winonweb-migrate'),
'packed' => __('Packed', 'winonweb-migrate'),
'unpacked' => __('Unpacked', 'winonweb-migrate'),
'archive' => __('Archive', 'winonweb-migrate'),
'uploading' => /* translators: %s: file name */ __('Uploading %s', 'winonweb-migrate'),
'checking' => __('Checking archive…', 'winonweb-migrate'),
'badFile' => __('Choose a .wowm, All-in-One .wpress or Duplicator .zip file.', 'winonweb-migrate'),
'badResponse' => __('The server returned an unexpected response.', 'winonweb-migrate'),
'retrying' => __('Connection hiccup, retrying…', 'winonweb-migrate'),
'timedOut' => __('The server did not answer in time.', 'winonweb-migrate'),
/* translators: 1: error message, 2: attempt number, 3: number of attempts */
'uploadRetry' => __('%1$s Retrying (%2$s of %3$s)…', 'winonweb-migrate'),
'bigFileNote' => __('Large file: this can take a long time on shared hosting, and the bar may pause for a minute between pieces. It is still working, keep this tab open. For files of a few GB it is faster to SFTP the archive into wp-content/wowm-backups and use Restore from Backups.', 'winonweb-migrate'),
/* translators: %s: piece size, e.g. 2 MB */
'chunkNote' => __('This host limits the upload size, so the file is sent in %s pieces. It is slower but reliable: keep this tab open and wait. For very large files, SFTP into wp-content/wowm-backups and use Restore from Backups instead.', 'winonweb-migrate'),
'confirmDelete' => __('Delete this backup permanently?', 'winonweb-migrate'),
'deleted' => __('Backup deleted', 'winonweb-migrate'),
'saved' => __('Schedule saved', 'winonweb-migrate'),
'noBackups' => __('No backups yet. Create your first export, it takes a minute.', 'winonweb-migrate'),
'nextRun' => /* translators: %s: date and time */ __('Next backup: %s', 'winonweb-migrate'),
'scheduleOff' => __('Automatic backups are off.', 'winonweb-migrate'),
'free' => /* translators: %s: size, e.g. 12 GB */ __('%s free on disk', 'winonweb-migrate'),
'origin' => [
'manual' => __('Manual', 'winonweb-migrate'),
'scheduled' => __('Scheduled', 'winonweb-migrate'),
'pre-import' => __('Before import', 'winonweb-migrate'),
'uploaded' => __('Uploaded', 'winonweb-migrate'),
'cli' => __('WP-CLI', 'winonweb-migrate'),
],
'components' => [
'database' => __('Database', 'winonweb-migrate'),
'uploads' => __('Media', 'winonweb-migrate'),
'themes' => __('Themes', 'winonweb-migrate'),
'plugins' => __('Plugins', 'winonweb-migrate'),
'muplugins' => __('MU plugins', 'winonweb-migrate'),
'other' => __('Other', 'winonweb-migrate'),
],
'from' => __('From', 'winonweb-migrate'),
'to' => __('To', 'winonweb-migrate'),
'created' => __('Created', 'winonweb-migrate'),
'size' => __('Size', 'winonweb-migrate'),
'wordpress' => __('WordPress', 'winonweb-migrate'),
'php' => __('PHP', 'winonweb-migrate'),
'theme' => __('Theme', 'winonweb-migrate'),
'plugins' => __('Active plugins', 'winonweb-migrate'),
'nothingSelected' => __('Select at least one thing to export.', 'winonweb-migrate'),
'findPlaceholder' => __('Find, e.g. old-domain.com', 'winonweb-migrate'),
'replacePlaceholder' => __('Replace with', 'winonweb-migrate'),
'remove' => __('Remove', 'winonweb-migrate'),
'readyToImport' => __('Ready to import', 'winonweb-migrate'),
'understand' => /* translators: %s: site address */ __('I understand this replaces the content of %s', 'winonweb-migrate'),
'importNow' => __('Import now', 'winonweb-migrate'),
'importDb' => __('Import the database', 'winonweb-migrate'),
'importFiles' => __('Import files', 'winonweb-migrate'),
'keepDropins' => __('Keep this host\'s drop-ins (recommended)', 'winonweb-migrate'),
'preBackup' => /* translators: %s: list of parts, e.g. Database, Themes */ __('First back up what gets replaced: %s (recommended)', 'winonweb-migrate'),
'preBackupLabel' => /* translators: %s: address of the site the archive comes from */ __('Before importing %s', 'winonweb-migrate'),
'extraReplace' => __('Extra find & replace', 'winonweb-migrate'),
'addRule' => __('Add rule', 'winonweb-migrate'),
'filesOnlyNote' => __('Files were imported. The database was not changed.', 'winonweb-migrate'),
],
]);
}
/**
* Rollback state for the screen.
*/
public static function rollback_payload(): ?array {
$data = Rollback::get();
if (!$data) {
return null;
}
return $data;
}
public static function render(): void {
Storage::cleanup();
require WOWM_DIR . 'views/admin.php';
}
}