SL Migrations
WordPress backup, migration and restore plugin for internal use.
Version: 1.8.10 License: GPLv2 or later Author: Steven Lam - https://phatdev.net
Features
- Full-site backup (files + database) packaged into a portable archive (ZIP or DAF) + installer.
- One-click restore on any compatible WordPress host.
- Scheduled recurring backups.
- Remote storage via SFTP / FTP.
- Disaster Recovery Point - mark one package as a quick restore point; generate a recovery URL with one click, automatically protected from the cron retention purge and from manual deletion.
- Self-contained: no external license server, no telemetry, no phone-home.
Requirements
- WordPress 4.9+ (tested up to 7.0)
- PHP 7.4+ (PHP 8.2 recommended)
- Single-site activation only (
Network: falsein the plugin header): activate it on each site; it cannot be network-activated
Storage backends
- Local - packages stored in
wp-content/backups-slm/. - FTP / SFTP - upload to a remote SSH/FTP server.
- Cloud storage (Amazon S3, Dropbox, Google Drive, OneDrive and similar) is not included: those SDKs are removed from this build.
Branding
Visual style follows the TanDoanh Plugins design system:
- Primary:
#1b64f2(blue) - Accent dark:
#1349ba - Surface:
#ffffffon#eef2f7background - Typography: Plus Jakarta Sans / Manrope / system fallback
Activation
wp plugin activate sl-migrations
Or via WP Admin → Plugins → Activate.
Backup output
- Folder:
wp-content/backups-slm/ - Per package: an archive (
*_archive.zipor*_archive.daf, depending on the archive engine) of the full site + a*_installer.php(self-contained restorer).
Restore
Upload the archive (*_archive.zip or *_archive.daf) + *_installer.php to the destination host, then open installer.php in a browser and follow the wizard.
Security note
installer.php, when uploaded to a public web root during restore, is executable by anyone who knows the URL until it self-destructs at the end of the process. Always either:
- protect the upload folder with HTTP basic auth, or
- delete
installer.phpimmediately after a successful restore (the wizard offers a cleanup step).
License
GPLv2 - see source headers. Internal-use plugin; not for redistribution.
CSS architecture
Single source of truth: assets/css/slm.css - all of the plugin's UI rules live here. The old CSS files (style.css, slm-overrides.css, modern.css, packages.css, import.css, info-page.css, style-ctrl.css, global_admin_style.css, admin-notifications.css, admin-notices.css) have been STUBBED (empty) - they are kept only to avoid 404s for the old wp_register_style handles.
slm.css uses CSS @layer to control the cascade - no !important is needed in new code:
@layer reset, tokens, base, components, pages, vendor-fix;
| Layer | Purpose | Add a new rule when? |
|---|---|---|
tokens |
CSS variables (--slm-*) |
Changing global color/spacing/shadow |
base |
Typography + form defaults inside .slm-modern |
Rarely |
components |
Buttons, cards, tables, badges, pills, modals, toasts, progress | A new component |
pages |
Page-specific overrides | A new page |
vendor-fix |
Overrides for legacy plugin classes + jstree/select2 | Strictly a last resort |
Class naming convention
- New: prefix
slm-*for every class you write (.slm-btn,.slm-card,.slm-pill-complete, …). - Legacy (
.dup-*,.dpro-*): only touch them when editing an old template; prefer adding a parallel.slm-*class instead of renaming the legacy one.
When you need to add a new rule
- Open assets/css/slm.css
- Find the right
@layer(see the table above) - Add the rule inside that block - do not create a new CSS file
- Do NOT use
!importantunless you are in thevendor-fixlayer - Use CSS variables instead of hardcoded color/spacing (
var(--slm-blue)rather than#2563eb)
When you need to change the theme color
Edit @layer tokens at the top of slm.css. Change one variable and everywhere that uses it updates automatically.
Anti-patterns (do not do these)
- Creating a separate CSS file for a single component (for example
my-feature.css) - Inline
style="..."in PHP templates (unless the value is computed dynamically) - Overly specific selectors like
body[class*="page"] .wrap table.widefat:not(.a):not(.b) ...- use a simple.slm-*class instead !importantin thecomponentsorpageslayer