Merge pull request 'feat(ui): unify backup/restore progress modals with the runbackup theme' (#281) from feat/unified-progress-theme into dev

This commit was merged in pull request #281.
This commit is contained in:
2026-07-16 14:25:28 +00:00
4 changed files with 197 additions and 16 deletions
+3
View File
@@ -4,6 +4,9 @@
### Fixed
- Database view "One Problem" on MokoSuiteBackup: reconciled the `#__mokosuitebackup_profiles` schema-checker output with the update-file history. Corrected the stale `TINYINT` definition in `01.01.01.sql` to the final `VARCHAR(20)` type, fixed the `MODIFY COLUMN` parser trip-up in `01.39.00.sql`, and rewrote the retired `sftp_*` `ADD COLUMN` statements in `01.35.00.sql`/`01.36.00.sql` as bare `ADD` so Joomla's checker no longer demands columns that `02.52.25` and the postflight purge deliberately remove. Joomla's schema checker now reports no problems.
### Changed
- Backup/restore progress modals in the backups view now use the same full-screen black, big-text, animated Atum-coloured progress-bar theme as the `runbackup` screen, so all backup progress surfaces match. New `media/css/backup-progress.css` scopes the black to the modal (the list page is unaffected until a modal opens).
## [02.64.00] --- 2026-07-14
## [02.64.00] --- 2026-07-14
@@ -0,0 +1,139 @@
/*
* @package MokoSuiteBackup
* @subpackage com_mokosuitebackup
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GNU General Public License version 3 or later; see LICENSE
*
* Full-screen backup/restore progress modals (backups view). Gives the stepped
* backup and restore modals the same full-black, big-text, Atum-coloured
* progress-bar treatment as the runbackup screen (media/css/runbackup.css), so
* all backup progress surfaces share one look. The black is scoped to the modal
* (via .msb-progress-modal) rather than html/body, so the list page behind it is
* unaffected until the modal opens.
*/
/* Accent colours pulled from the Atum admin template's own custom properties,
with Bootstrap and then a literal as progressive fallbacks — identical to the
runbackup screen so the two match whatever theme the site runs. */
.msb-progress-modal {
--msb-accent: var(--template-link-color, var(--bs-link-color, #2a69b8));
--msb-accent2: var(--template-special-color, var(--msb-accent));
--msb-success: var(--template-success-color, var(--bs-success, #2f7d32));
--msb-danger: var(--template-danger-color, var(--bs-danger, #c52128));
--msb-track: var(--template-bg-light, var(--bs-gray-200, #edf0f5));
}
/* Full-screen black takeover, mirroring the runbackup page. */
.msb-progress-modal .modal-dialog {
max-width: 100%;
width: 100%;
min-height: 100%;
margin: 0;
}
.msb-progress-modal .modal-content {
min-height: 100vh;
border: 0;
border-radius: 0;
background-color: #000;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 1.5rem;
}
.msb-progress-modal .modal-header {
border: 0;
padding: 0 0 1.5rem;
justify-content: center;
text-align: center;
}
/* Big heading, like the runbackup title. */
.msb-progress-modal .modal-title {
color: #fff;
font-size: clamp(1.75rem, 4vw, 2.75rem);
font-weight: 700;
letter-spacing: .01em;
}
.msb-progress-modal .modal-body {
width: 100%;
max-width: 680px;
padding: 0;
text-align: center;
}
.msb-progress-modal .msb-pm-status {
color: #fff;
font-size: 1.25rem;
margin: 1rem 0 .25rem;
}
.msb-progress-modal .msb-pm-phase {
color: rgba(255, 255, 255, .6);
font-size: .95rem;
margin: 0;
}
.msb-progress-modal .msb-pm-warn {
color: #ffd23f;
font-weight: 600;
font-size: 1rem;
margin-bottom: 1.25rem;
}
/* Progress bar — mirrors runbackup.css .msb-rb-progress/.msb-rb-bar. */
.msb-progress-modal .msb-rb-progress {
height: 2.75rem;
background: var(--msb-track);
border-radius: .35rem;
overflow: hidden;
}
.msb-progress-modal .msb-rb-bar {
position: relative;
height: 100%;
width: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 600;
font-size: 1rem;
white-space: nowrap;
background-color: var(--msb-accent);
background-image: linear-gradient(90deg, var(--msb-accent), var(--msb-accent2));
transition: width .35s ease;
}
.msb-progress-modal .msb-rb-bar.is-animated::after {
content: "";
position: absolute;
inset: 0;
background-image: linear-gradient(45deg, rgba(255, 255, 255, .18) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .18) 50%, rgba(255, 255, 255, .18) 75%, transparent 75%, transparent);
background-size: 1rem 1rem;
animation: msb-pm-stripes 1s linear infinite;
}
.msb-progress-modal .msb-rb-bar.is-success {
background-image: none;
background-color: var(--msb-success);
}
.msb-progress-modal .msb-rb-bar.is-danger {
background-image: none;
background-color: var(--msb-danger);
}
@keyframes msb-pm-stripes {
from {
background-position: 1rem 0;
}
to {
background-position: 0 0;
}
}
@@ -14,6 +14,11 @@
"name": "com_mokosuitebackup.runbackup",
"type": "style",
"uri": "com_mokosuitebackup/css/runbackup.css"
},
{
"name": "com_mokosuitebackup.backup-progress",
"type": "style",
"uri": "com_mokosuitebackup/css/backup-progress.css"
}
]
}
@@ -16,8 +16,14 @@ use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
use Joomla\CMS\Uri\Uri;
HTMLHelper::_('behavior.multiselect');
// Full-black progress theme for the backup/restore modals (matches view=runbackup).
// Loaded directly rather than via useStyle(), which does not emit a link for this
// component (same pattern as the runbackup screen).
$this->getDocument()->addStyleSheet(Uri::root(true) . '/media/com_mokosuitebackup/css/backup-progress.css');
$user = Factory::getApplication()->getIdentity();
$canDownload = $user->authorise('mokosuitebackup.backup.download', 'com_mokosuitebackup');
@@ -135,22 +141,25 @@ $listDirn = $this->escape($this->state->get('list.direction'));
</form>
<!-- Stepped Backup Modal (for shared hosting) -->
<div class="modal fade" id="mokosuitebackup-modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal fade msb-progress-modal" id="mokosuitebackup-modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="mb-modal-title">Backup in Progress</h5>
<h2 class="modal-title" id="mb-modal-title">
<span class="icon-archive" aria-hidden="true"></span>
Backup in Progress
</h2>
</div>
<div class="modal-body">
<div class="alert alert-warning py-1 px-2 mb-2" style="font-size:0.85rem;">
<p class="msb-pm-warn">
<span class="icon-warning-circle" aria-hidden="true"></span>
<strong>Do not navigate away or close this window</strong> while the backup is running.
Do not navigate away or close this window while the backup is running.
</p>
<div class="msb-rb-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div id="mb-progress-bar" class="msb-rb-bar is-animated" style="width:0%;">0%</div>
</div>
<div class="progress mb-2" style="height:24px;">
<div id="mb-progress-bar" class="progress-bar" role="progressbar" style="width:0%;">0%</div>
</div>
<p id="mb-status" class="text-muted mb-1" style="font-size:0.9rem;">Initializing...</p>
<p id="mb-phase" class="text-muted mb-0" style="font-size:0.8rem;">Phase: init</p>
<p id="mb-status" class="msb-pm-status">Initializing...</p>
<p id="mb-phase" class="msb-pm-phase">Phase: init</p>
</div>
</div>
</div>
@@ -183,12 +192,29 @@ $listDirn = $this->escape($this->state->get('list.direction'));
bootstrap.Modal.getInstance(document.getElementById('mokosuitebackup-modal'))?.hide();
}
// Shared bar-state styling for both the backup and restore progress bars,
// matching the runbackup screen: animated stripes while running, solid green
// on completion, solid red on failure.
function setBarState(bar, phase, progress) {
if (phase === 'failed') {
bar.classList.remove('is-animated', 'is-success');
bar.classList.add('is-danger');
} else if (progress >= 100 || phase === 'complete') {
bar.classList.remove('is-animated', 'is-danger');
bar.classList.add('is-success');
}
if (bar.parentElement) {
bar.parentElement.setAttribute('aria-valuenow', progress);
}
}
function updateProgress(progress, message, phase) {
const bar = document.getElementById('mb-progress-bar');
bar.style.width = progress + '%';
bar.textContent = progress + '%';
document.getElementById('mb-status').textContent = message;
document.getElementById('mb-phase').textContent = 'Phase: ' + phase;
setBarState(bar, phase, progress);
}
async function postAjax(params) {
@@ -229,7 +255,7 @@ $listDirn = $this->escape($this->state->get('list.direction'));
if (initResult.warnings && initResult.warnings.length > 0) {
var warningEl = document.getElementById('mb-phase');
warningEl.textContent = 'Warnings: ' + initResult.warnings.join('; ');
warningEl.style.color = '#856404';
warningEl.style.color = '#ffd23f';
}
const sessionId = initResult.session_id;
@@ -312,6 +338,7 @@ $listDirn = $this->escape($this->state->get('list.direction'));
bar.textContent = progress + '%';
document.getElementById('mb-restore-status').textContent = message;
document.getElementById('mb-restore-phase').textContent = 'Phase: ' + phase;
setBarState(bar, phase, progress);
}
window.addEventListener('beforeunload', function(e) {
@@ -452,18 +479,25 @@ $listDirn = $this->escape($this->state->get('list.direction'));
</div>
<!-- Restore Progress Modal -->
<div class="modal fade" id="mb-restore-progress-modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal fade msb-progress-modal" id="mb-restore-progress-modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="mb-restore-title">Restore in Progress</h5>
<h2 class="modal-title" id="mb-restore-title">
<span class="icon-refresh" aria-hidden="true"></span>
Restore in Progress
</h2>
</div>
<div class="modal-body">
<div class="progress mb-2" style="height:24px;">
<div id="mb-restore-progress-bar" class="progress-bar bg-danger" role="progressbar" style="width:0%;">0%</div>
<p class="msb-pm-warn">
<span class="icon-warning-circle" aria-hidden="true"></span>
Do not navigate away or close this window while the restore is running.
</p>
<div class="msb-rb-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div id="mb-restore-progress-bar" class="msb-rb-bar is-animated is-danger" style="width:0%;">0%</div>
</div>
<p id="mb-restore-status" class="text-muted mb-1" style="font-size:0.9rem;">Initializing...</p>
<p id="mb-restore-phase" class="text-muted mb-0" style="font-size:0.8rem;">Phase: init</p>
<p id="mb-restore-status" class="msb-pm-status">Initializing...</p>
<p id="mb-restore-phase" class="msb-pm-phase">Phase: init</p>
</div>
</div>
</div>