diff --git a/classes/plugin/AdminController.php b/classes/plugin/AdminController.php index c585942b..553a617b 100644 --- a/classes/plugin/AdminController.php +++ b/classes/plugin/AdminController.php @@ -2268,6 +2268,7 @@ class AdminController extends AdminBaseController * @var string $name * @var Medium|ImageMedium $medium */ + $this->grav['log']->debug('[AI Pro][listmedia] route=' . $this->route . ' path=' . ($media->getPath() ?: 'n/a') . ' count=' . count($media->all())); foreach ($media->all() as $name => $medium) { $metadata = []; diff --git a/classes/plugin/SafeUpgradeManager.php b/classes/plugin/SafeUpgradeManager.php index e675755d..70f93d0f 100644 --- a/classes/plugin/SafeUpgradeManager.php +++ b/classes/plugin/SafeUpgradeManager.php @@ -243,10 +243,13 @@ class SafeUpgradeManager return $this->errorResult('Unable to locate Grav update package information.'); } - $this->recovery->markUpgradeWindow('core-upgrade', [ - 'scope' => 'core', - 'target_version' => $remoteVersion, - ]); + if ($this->recovery && method_exists($this->recovery, 'markUpgradeWindow')) { + // Newer Grav exposes upgrade window helpers; guard for older cores. + $this->recovery->markUpgradeWindow('core-upgrade', [ + 'scope' => 'core', + 'target_version' => $remoteVersion, + ]); + } try { $file = $this->download($package, $timeout); @@ -265,7 +268,9 @@ class SafeUpgradeManager $this->setProgress('finalizing', 'Finalizing upgrade...', 100); $safeUpgrade->clearRecoveryFlag(); - $this->recovery->closeUpgradeWindow(); + if ($this->recovery && method_exists($this->recovery, 'closeUpgradeWindow')) { + $this->recovery->closeUpgradeWindow(); + } $manifest = $this->resolveLatestManifest(); diff --git a/themes/grav/app/updates/safe-upgrade.js b/themes/grav/app/updates/safe-upgrade.js index bbbb1824..f738e350 100644 --- a/themes/grav/app/updates/safe-upgrade.js +++ b/themes/grav/app/updates/safe-upgrade.js @@ -48,6 +48,8 @@ export default class SafeUpgrade { this.urls = this.buildUrls(); this.decisions = {}; this.pollTimer = null; + this.statusRequest = null; + this.isPolling = false; this.active = false; this.registerEvents(); @@ -347,9 +349,7 @@ export default class SafeUpgrade { this.buttons.start.prop('disabled', true); this.stopPolling(); - this.pollTimer = setInterval(() => { - this.fetchStatus(true); - }, 1200); + this.beginPolling(); const body = { decisions: this.decisions @@ -388,8 +388,42 @@ export default class SafeUpgrade { }); } + beginPolling(delay = 1200) { + if (this.isPolling) { + return; + } + + this.isPolling = true; + this.schedulePoll(delay); + } + + schedulePoll(delay = 1200) { + this.clearPollTimer(); + if (!this.isPolling) { + return; + } + + this.pollTimer = setTimeout(() => { + this.fetchStatus(true); + }, delay); + } + + clearPollTimer() { + if (this.pollTimer) { + clearTimeout(this.pollTimer); + this.pollTimer = null; + } + } + fetchStatus(silent = false) { - request(this.urls.status, (response) => { + if (this.statusRequest) { + return; + } + + this.pollTimer = null; + let nextStage = null; + + this.statusRequest = request(this.urls.status, (response) => { if (response.status === 'error') { if (!silent) { this.renderProgress({ @@ -398,16 +432,30 @@ export default class SafeUpgrade { percent: null }); } + nextStage = 'error'; return; } const data = response.data || {}; + nextStage = data.stage || null; this.renderProgress(data); - - if (data.stage === 'complete') { - this.stopPolling(); - } }); + + const finalize = () => { + this.statusRequest = null; + + if (!this.isPolling) { + return; + } + + if (nextStage === 'complete' || nextStage === 'error') { + this.stopPolling(); + } else { + this.schedulePoll(); + } + }; + + this.statusRequest.then(finalize, finalize); } renderProgress(data) { @@ -495,10 +543,8 @@ export default class SafeUpgrade { } stopPolling() { - if (this.pollTimer) { - clearInterval(this.pollTimer); - this.pollTimer = null; - } + this.isPolling = false; + this.clearPollTimer(); } } diff --git a/themes/grav/js/admin.min.js b/themes/grav/js/admin.min.js index 1a9cd95b..a6088899 100644 --- a/themes/grav/js/admin.min.js +++ b/themes/grav/js/admin.min.js @@ -4600,6 +4600,8 @@ var SafeUpgrade = /*#__PURE__*/function () { this.urls = this.buildUrls(); this.decisions = {}; this.pollTimer = null; + this.statusRequest = null; + this.isPolling = !1; this.active = false; this.registerEvents(); } @@ -4810,9 +4812,7 @@ var SafeUpgrade = /*#__PURE__*/function () { }); this.buttons.start.prop('disabled', true); this.stopPolling(); - this.pollTimer = setInterval(function () { - _this4.fetchStatus(true); - }, 1200); + this.beginPolling(); var body = { decisions: this.decisions }; @@ -4849,28 +4849,75 @@ var SafeUpgrade = /*#__PURE__*/function () { _this4.fetchStatus(true); }); } + }, { + key: "beginPolling", + value: function beginPolling() { + var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1200; + if (this.isPolling) { + return; + } + this.isPolling = !0; + this.schedulePoll(delay); + } + }, { + key: "schedulePoll", + value: function schedulePoll() { + var _this5 = this; + var delay = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1200; + this.clearPollTimer(); + if (!this.isPolling) { + return; + } + this.pollTimer = setTimeout(function () { + _this5.fetchStatus(true); + }, delay); + } + }, { + key: "clearPollTimer", + value: function clearPollTimer() { + if (this.pollTimer) { + clearTimeout(this.pollTimer); + this.pollTimer = null; + } + } }, { key: "fetchStatus", value: function fetchStatus() { - var _this5 = this; + var _this6 = this; var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; - utils_request(this.urls.status, function (response) { + if (this.statusRequest) { + return; + } + this.pollTimer = null; + var nextStage = null; + this.statusRequest = utils_request(this.urls.status, function (response) { if (response.status === 'error') { if (!silent) { - _this5.renderProgress({ + _this6.renderProgress({ stage: 'error', message: response.message || t('SAFE_UPGRADE_GENERIC_ERROR', 'Safe upgrade could not complete. See Grav logs for details.'), percent: null }); } + nextStage = 'error'; return; } var data = response.data || {}; - _this5.renderProgress(data); - if (data.stage === 'complete') { - _this5.stopPolling(); - } + nextStage = data.stage || null; + _this6.renderProgress(data); }); + var finalize = function finalize() { + _this6.statusRequest = null; + if (!_this6.isPolling) { + return; + } + if (nextStage === 'complete' || nextStage === 'error') { + _this6.stopPolling(); + } else { + _this6.schedulePoll(); + } + }; + this.statusRequest.then(finalize, finalize); } }, { key: "renderProgress", @@ -4935,10 +4982,8 @@ var SafeUpgrade = /*#__PURE__*/function () { }, { key: "stopPolling", value: function stopPolling() { - if (this.pollTimer) { - clearInterval(this.pollTimer); - this.pollTimer = null; - } + this.isPolling = !1; + this.clearPollTimer(); } }]); }();