From 3b6cdbe59507880cc1c9c5d33625bc71b70e81c8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:37:05 +0100 Subject: [PATCH] V15: "New version available" text is not reverified after an upgrade (#18013) * fix: compare current version with the stored version this ensures that if you come from an older version of Umbraco, we re-verify the upgrade-check to remove the message * chore: update version in mock handler * set "new version" button to a positive color * feat: use modal data to pass on info to new-version modal element * Amended `type` import --- .../src/apps/backoffice/backoffice.context.ts | 6 +- .../backoffice-header-logo.element.ts | 17 ++- .../src/mocks/handlers/server.handlers.ts | 4 +- .../sysinfo/components/new-version.element.ts | 23 +--- .../sysinfo/modals/new-version-modal.token.ts | 14 ++- .../sysinfo/repository/sysinfo.repository.ts | 113 ++++++++++++------ .../src/packages/sysinfo/types.ts | 6 +- 7 files changed, 116 insertions(+), 67 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts index 74ea04410e..ecd4a56bdb 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts @@ -78,10 +78,10 @@ export class UmbBackofficeContext extends UmbContextBase { this.#activeSectionAlias.setValue(alias); } - public async serverUpgradeCheck(): Promise { + public async serverUpgradeCheck() { + const version = await this.observe(this.version).asPromise(); const repository = new UmbSysinfoRepository(this); - const check = await repository.serverUpgradeCheck(); - return !!check; + return repository.serverUpgradeCheck(version); } } diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts index 959141a403..fe4c3af09a 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts @@ -3,9 +3,10 @@ import { isCurrentUserAnAdmin } from '@umbraco-cms/backoffice/current-user'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { UMB_NEWVERSION_MODAL, UMB_SYSINFO_MODAL } from '@umbraco-cms/backoffice/sysinfo'; -import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app'; +import type { UmbServerUpgradeCheck } from '@umbraco-cms/backoffice/sysinfo'; @customElement('umb-backoffice-header-logo') export class UmbBackofficeHeaderLogoElement extends UmbLitElement { @@ -16,7 +17,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { private _isUserAdmin = false; @state() - private _serverUpgradeCheck = false; + private _serverUpgradeCheck: UmbServerUpgradeCheck | null = null; @state() private _serverUrl = ''; @@ -52,7 +53,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { this._isUserAdmin = await isCurrentUserAnAdmin(this); if (this._isUserAdmin) { - this._serverUpgradeCheck = this.#backofficeContext ? await this.#backofficeContext.serverUpgradeCheck() : false; + this._serverUpgradeCheck = this.#backofficeContext ? await this.#backofficeContext.serverUpgradeCheck() : null; } } @@ -76,7 +77,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { ${this._serverUpgradeCheck ? html`` : ''} @@ -98,9 +99,15 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { } async #openNewVersion() { + if (!this._serverUpgradeCheck) return; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); modalManager - .open(this, UMB_NEWVERSION_MODAL) + .open(this, UMB_NEWVERSION_MODAL, { + data: { + comment: this._serverUpgradeCheck.comment, + downloadUrl: this._serverUpgradeCheck.url, + }, + }) .onSubmit() .catch(() => {}); } diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/server.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/server.handlers.ts index b635eb3b90..a792041089 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/server.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/server.handlers.ts @@ -59,8 +59,8 @@ export const serverInformationHandlers = [ ctx.status(200), ctx.json({ type: 'Minor', - comment: "14.2.0.0 is released. Upgrade today - it's free!", - url: 'https://our.umbraco.com/download/releases/1420', + comment: "15.2.0 is released. Upgrade today - it's free!", + url: 'https://our.umbraco.com/download/releases/1520', }), ); }), diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/components/new-version.element.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/components/new-version.element.ts index b12c0e7b23..232a4735c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/components/new-version.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/components/new-version.element.ts @@ -1,30 +1,19 @@ -import { UmbSysinfoRepository } from '../repository/sysinfo.repository.js'; import type { UmbServerUpgradeCheck } from '../types.js'; -import { css, customElement, html, state, when } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbNewVersionModalData } from '../modals/new-version-modal.token.js'; +import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; @customElement('umb-new-version') -export class UmbNewVersionElement extends UmbModalBaseElement { +export class UmbNewVersionElement extends UmbModalBaseElement { @state() private _serverUpgradeCheck: UmbServerUpgradeCheck | null = null; - #sysinfoRepository = new UmbSysinfoRepository(this); - - override async connectedCallback() { - super.connectedCallback(); - this._serverUpgradeCheck = await this.#sysinfoRepository.serverUpgradeCheck(); - } - override render() { return html` - ${when( - this._serverUpgradeCheck === null, - () => html``, - () => html`
${this._serverUpgradeCheck!.comment}
`, - )} + ${this.data?.comment} - ${this._serverUpgradeCheck?.url + ${this.data?.downloadUrl ? html` ('Umb.Modal.NewVersion', { modal: { type: 'dialog', size: 'medium', diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts index 081f50ac55..1e964b826e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts @@ -20,48 +20,103 @@ export class UmbSysinfoRepository extends UmbRepositoryBase { return data; } - async serverUpgradeCheck(): Promise { + /** + * Check if the server has an upgrade available and return the result. + * If the server has an upgrade available, the result will be stored in local storage. + * If the server does not have an upgrade available, the result will be stored in local storage as well. + * @param {string} currentVersion The current version of the server. + * @returns {Promise} The server upgrade check result or null if the check is not allowed or if the check failed. + */ + async serverUpgradeCheck(currentVersion: string): Promise { // Check if we are allowed to check again const appContext = await this.getContext(UMB_APP_CONTEXT); const versionCheckPeriod = await this.observe(appContext.getServerConnection().versionCheckPeriod).asPromise(); if (versionCheckPeriod <= 0) { + // We do not need to check the server for an upgrade return null; } - let shouldCheck = true; + const lastUpgradeCheck = this.#getStoredServerUpgradeCheck(currentVersion); - const lastCheck = localStorage.getItem('umb:lastUpgradeCheck'); - const now = new Date(); - if (lastCheck) { - const lastCheckDate = new Date(lastCheck); - const diff = now.getTime() - lastCheckDate.getTime(); - const diffDays = diff / (1000 * 3600 * 24); + // If we have a stored check, then return it if it is still valid + if (lastUpgradeCheck !== null) { + // If we have a stored check, then check if we should check again based on the period + if (lastUpgradeCheck.createdAt) { + const lastCheckDate = new Date(lastUpgradeCheck.createdAt); + const diff = new Date().getTime() - lastCheckDate.getTime(); + const diffDays = diff / (1000 * 3600 * 24); - if (diffDays < versionCheckPeriod) { - shouldCheck = false; - } - - // If we should not check, then return what we have stored if it is still valid - if (!shouldCheck) { - return this.#getStoredServerUpgradeCheck(lastCheckDate); + if (diffDays < versionCheckPeriod) { + // If we should not check, then return what we have stored if it is still valid + if (lastUpgradeCheck.type.toLowerCase() !== 'none') { + return lastUpgradeCheck; + } + return null; // no upgrade available + } } } - if (!shouldCheck) { + // Check the server for an upgrade because we have no stored check or the stored check is invalid + return this.#fetchServerUpgradeCheck(versionCheckPeriod, currentVersion); + } + + /** + * Get the stored server upgrade check if it is still valid, otherwise return null and remove the stored check. + * @param {string} currentVersion The current version of the server. + * @returns {UmbServerUpgradeCheck | null} The stored server upgrade check or null if it is not valid. + */ + #getStoredServerUpgradeCheck(currentVersion: string): UmbServerUpgradeCheck | null { + const storedCheck = localStorage.getItem('umb:serverUpgradeCheck'); + if (!storedCheck) { return null; } - // Check the server + const upgradeCheck: UmbServerUpgradeCheck = JSON.parse(storedCheck); + + // Check that the stored check is for the same version + if (upgradeCheck.version !== currentVersion) { + localStorage.removeItem('umb:serverUpgradeCheck'); + return null; + } + + // Check that the stored check is not older than the last check + if (upgradeCheck.createdAt) { + const createdAt = new Date(upgradeCheck.createdAt); + const expiresAt = new Date(upgradeCheck.expires); + if (expiresAt.getTime() <= createdAt.getTime()) { + localStorage.removeItem('umb:serverUpgradeCheck'); + return null; + } + } + + return upgradeCheck; + } + + /** + * Fetch the server upgrade check from the server and store the result in local storage. + * @param {number} versionCheckPeriod A period in days to wait before checking the server again. + * @param {string} currentVersion The current version of the server. + * @returns {Promise} The server upgrade check result or null if the check failed. + */ + async #fetchServerUpgradeCheck( + versionCheckPeriod: number, + currentVersion: string, + ): Promise { + // Check the server for an upgrade because we have no stored check or the stored check is invalid const { data } = await tryExecute(ServerService.getServerUpgradeCheck()); if (data) { // Save the last check date including the data received const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + versionCheckPeriod); - const upgradeCheck = { ...data, expires: expiresAt.toISOString() } satisfies UmbServerUpgradeCheck; + const upgradeCheck = { + ...data, + expires: expiresAt.toISOString(), + version: currentVersion, + createdAt: new Date().toISOString(), + } satisfies UmbServerUpgradeCheck; localStorage.setItem('umb:serverUpgradeCheck', JSON.stringify(upgradeCheck)); - localStorage.setItem('umb:lastUpgradeCheck', now.toISOString()); // Only return if we have a valid type if (data.type.toLowerCase() !== 'none') { @@ -69,24 +124,6 @@ export class UmbSysinfoRepository extends UmbRepositoryBase { } } - return null; - } - - #getStoredServerUpgradeCheck(lastCheck: Date): UmbServerUpgradeCheck | null { - const storedCheck = localStorage.getItem('umb:serverUpgradeCheck'); - if (storedCheck) { - const upgradeCheck: UmbServerUpgradeCheck = JSON.parse(storedCheck); - // Check that the stored check is not older than the last check - const expiresAt = new Date(upgradeCheck.expires); - if (expiresAt.getTime() > lastCheck.getTime()) { - if (upgradeCheck.type.toLowerCase() !== 'none') { - return upgradeCheck; - } - } else { - localStorage.removeItem('umb:serverUpgradeCheck'); - } - } - - return null; + return null; // no upgrade available } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/types.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/types.ts index 7babfafeb4..2a1d73e196 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/types.ts @@ -1,3 +1,7 @@ import type { UpgradeCheckResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; -export type UmbServerUpgradeCheck = UpgradeCheckResponseModel & { expires: string }; +export type UmbServerUpgradeCheck = UpgradeCheckResponseModel & { + expires: string; + version?: string; + createdAt?: string; +};