Skip to content

Commit 3b6cdbe

Browse files
authored
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
1 parent 7c9e04f commit 3b6cdbe

File tree

7 files changed

+117
-68
lines changed

7 files changed

+117
-68
lines changed

src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ export class UmbBackofficeContext extends UmbContextBase<UmbBackofficeContext> {
7878
this.#activeSectionAlias.setValue(alias);
7979
}
8080

81-
public async serverUpgradeCheck(): Promise<boolean> {
81+
public async serverUpgradeCheck() {
82+
const version = await this.observe(this.version).asPromise();
8283
const repository = new UmbSysinfoRepository(this);
83-
const check = await repository.serverUpgradeCheck();
84-
return !!check;
84+
return repository.serverUpgradeCheck(version);
8585
}
8686
}
8787

src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { isCurrentUserAnAdmin } from '@umbraco-cms/backoffice/current-user';
33
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
44
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
55
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
6+
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
67
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
78
import { UMB_NEWVERSION_MODAL, UMB_SYSINFO_MODAL } from '@umbraco-cms/backoffice/sysinfo';
8-
import { UMB_APP_CONTEXT } from '@umbraco-cms/backoffice/app';
9+
import type { UmbServerUpgradeCheck } from '@umbraco-cms/backoffice/sysinfo';
910

1011
@customElement('umb-backoffice-header-logo')
1112
export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
@@ -16,7 +17,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
1617
private _isUserAdmin = false;
1718

1819
@state()
19-
private _serverUpgradeCheck = false;
20+
private _serverUpgradeCheck: UmbServerUpgradeCheck | null = null;
2021

2122
@state()
2223
private _serverUrl = '';
@@ -52,7 +53,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
5253
this._isUserAdmin = await isCurrentUserAnAdmin(this);
5354

5455
if (this._isUserAdmin) {
55-
this._serverUpgradeCheck = this.#backofficeContext ? await this.#backofficeContext.serverUpgradeCheck() : false;
56+
this._serverUpgradeCheck = this.#backofficeContext ? await this.#backofficeContext.serverUpgradeCheck() : null;
5657
}
5758
}
5859

@@ -76,7 +77,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
7677
${this._serverUpgradeCheck
7778
? html`<uui-button
7879
@click=${this.#openNewVersion}
79-
color="danger"
80+
color="positive"
8081
label=${this.localize.term('general_newVersionAvailable')}></uui-button>`
8182
: ''}
8283
@@ -98,9 +99,15 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
9899
}
99100

100101
async #openNewVersion() {
102+
if (!this._serverUpgradeCheck) return;
101103
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
102104
modalManager
103-
.open(this, UMB_NEWVERSION_MODAL)
105+
.open(this, UMB_NEWVERSION_MODAL, {
106+
data: {
107+
comment: this._serverUpgradeCheck.comment,
108+
downloadUrl: this._serverUpgradeCheck.url,
109+
},
110+
})
104111
.onSubmit()
105112
.catch(() => {});
106113
}

src/Umbraco.Web.UI.Client/src/mocks/handlers/server.handlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ export const serverInformationHandlers = [
5959
ctx.status(200),
6060
ctx.json<GetServerUpgradeCheckResponse>({
6161
type: 'Minor',
62-
comment: "14.2.0.0 is released. Upgrade today - it's free!",
63-
url: 'https://our.umbraco.com/download/releases/1420',
62+
comment: "15.2.0 is released. Upgrade today - it's free!",
63+
url: 'https://our.umbraco.com/download/releases/1520',
6464
}),
6565
);
6666
}),

src/Umbraco.Web.UI.Client/src/packages/sysinfo/components/new-version.element.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,29 @@
1-
import { UmbSysinfoRepository } from '../repository/sysinfo.repository.js';
21
import type { UmbServerUpgradeCheck } from '../types.js';
3-
import { css, customElement, html, state, when } from '@umbraco-cms/backoffice/external/lit';
2+
import type { UmbNewVersionModalData } from '../modals/new-version-modal.token.js';
3+
import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit';
44
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
55
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
66

77
@customElement('umb-new-version')
8-
export class UmbNewVersionElement extends UmbModalBaseElement {
8+
export class UmbNewVersionElement extends UmbModalBaseElement<UmbNewVersionModalData> {
99
@state()
1010
private _serverUpgradeCheck: UmbServerUpgradeCheck | null = null;
1111

12-
#sysinfoRepository = new UmbSysinfoRepository(this);
13-
14-
override async connectedCallback() {
15-
super.connectedCallback();
16-
this._serverUpgradeCheck = await this.#sysinfoRepository.serverUpgradeCheck();
17-
}
18-
1912
override render() {
2013
return html`
2114
<uui-dialog>
2215
<uui-dialog-layout headline=${this.localize.term('general_newVersionAvailable')}>
23-
${when(
24-
this._serverUpgradeCheck === null,
25-
() => html`<uui-loader-bar></uui-loader-bar>`,
26-
() => html` <div>${this._serverUpgradeCheck!.comment}</div> `,
27-
)}
16+
${this.data?.comment}
2817
2918
<uui-button
3019
@click=${this._submitModal}
3120
slot="actions"
3221
look="secondary"
3322
label=${this.localize.term('general_close')}></uui-button>
3423
35-
${this._serverUpgradeCheck?.url
24+
${this.data?.downloadUrl
3625
? html` <uui-button
37-
.href=${this._serverUpgradeCheck.url}
26+
.href=${this.data.downloadUrl}
3827
target="_blank"
3928
slot="actions"
4029
look="primary"

src/Umbraco.Web.UI.Client/src/packages/sysinfo/modals/new-version-modal.token.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
22

3-
export const UMB_NEWVERSION_MODAL = new UmbModalToken('Umb.Modal.NewVersion', {
3+
export interface UmbNewVersionModalData {
4+
/**
5+
* The release notes of the new version.
6+
*/
7+
comment: string;
8+
9+
/**
10+
* The download URL of the new version.
11+
*/
12+
downloadUrl: string;
13+
}
14+
15+
export const UMB_NEWVERSION_MODAL = new UmbModalToken<UmbNewVersionModalData>('Umb.Modal.NewVersion', {
416
modal: {
517
type: 'dialog',
618
size: 'medium',

src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts

Lines changed: 76 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,73 +20,110 @@ export class UmbSysinfoRepository extends UmbRepositoryBase {
2020
return data;
2121
}
2222

23-
async serverUpgradeCheck(): Promise<UmbServerUpgradeCheck | null> {
23+
/**
24+
* Check if the server has an upgrade available and return the result.
25+
* If the server has an upgrade available, the result will be stored in local storage.
26+
* If the server does not have an upgrade available, the result will be stored in local storage as well.
27+
* @param {string} currentVersion The current version of the server.
28+
* @returns {Promise<UmbServerUpgradeCheck | null>} The server upgrade check result or null if the check is not allowed or if the check failed.
29+
*/
30+
async serverUpgradeCheck(currentVersion: string): Promise<UmbServerUpgradeCheck | null> {
2431
// Check if we are allowed to check again
2532
const appContext = await this.getContext(UMB_APP_CONTEXT);
2633
const versionCheckPeriod = await this.observe(appContext.getServerConnection().versionCheckPeriod).asPromise();
2734

2835
if (versionCheckPeriod <= 0) {
36+
// We do not need to check the server for an upgrade
2937
return null;
3038
}
3139

32-
let shouldCheck = true;
33-
34-
const lastCheck = localStorage.getItem('umb:lastUpgradeCheck');
35-
const now = new Date();
36-
if (lastCheck) {
37-
const lastCheckDate = new Date(lastCheck);
38-
const diff = now.getTime() - lastCheckDate.getTime();
39-
const diffDays = diff / (1000 * 3600 * 24);
40-
41-
if (diffDays < versionCheckPeriod) {
42-
shouldCheck = false;
40+
const lastUpgradeCheck = this.#getStoredServerUpgradeCheck(currentVersion);
41+
42+
// If we have a stored check, then return it if it is still valid
43+
if (lastUpgradeCheck !== null) {
44+
// If we have a stored check, then check if we should check again based on the period
45+
if (lastUpgradeCheck.createdAt) {
46+
const lastCheckDate = new Date(lastUpgradeCheck.createdAt);
47+
const diff = new Date().getTime() - lastCheckDate.getTime();
48+
const diffDays = diff / (1000 * 3600 * 24);
49+
50+
if (diffDays < versionCheckPeriod) {
51+
// If we should not check, then return what we have stored if it is still valid
52+
if (lastUpgradeCheck.type.toLowerCase() !== 'none') {
53+
return lastUpgradeCheck;
54+
}
55+
return null; // no upgrade available
56+
}
4357
}
58+
}
4459

45-
// If we should not check, then return what we have stored if it is still valid
46-
if (!shouldCheck) {
47-
return this.#getStoredServerUpgradeCheck(lastCheckDate);
48-
}
60+
// Check the server for an upgrade because we have no stored check or the stored check is invalid
61+
return this.#fetchServerUpgradeCheck(versionCheckPeriod, currentVersion);
62+
}
63+
64+
/**
65+
* Get the stored server upgrade check if it is still valid, otherwise return null and remove the stored check.
66+
* @param {string} currentVersion The current version of the server.
67+
* @returns {UmbServerUpgradeCheck | null} The stored server upgrade check or null if it is not valid.
68+
*/
69+
#getStoredServerUpgradeCheck(currentVersion: string): UmbServerUpgradeCheck | null {
70+
const storedCheck = localStorage.getItem('umb:serverUpgradeCheck');
71+
if (!storedCheck) {
72+
return null;
4973
}
5074

51-
if (!shouldCheck) {
75+
const upgradeCheck: UmbServerUpgradeCheck = JSON.parse(storedCheck);
76+
77+
// Check that the stored check is for the same version
78+
if (upgradeCheck.version !== currentVersion) {
79+
localStorage.removeItem('umb:serverUpgradeCheck');
5280
return null;
5381
}
5482

55-
// Check the server
83+
// Check that the stored check is not older than the last check
84+
if (upgradeCheck.createdAt) {
85+
const createdAt = new Date(upgradeCheck.createdAt);
86+
const expiresAt = new Date(upgradeCheck.expires);
87+
if (expiresAt.getTime() <= createdAt.getTime()) {
88+
localStorage.removeItem('umb:serverUpgradeCheck');
89+
return null;
90+
}
91+
}
92+
93+
return upgradeCheck;
94+
}
95+
96+
/**
97+
* Fetch the server upgrade check from the server and store the result in local storage.
98+
* @param {number} versionCheckPeriod A period in days to wait before checking the server again.
99+
* @param {string} currentVersion The current version of the server.
100+
* @returns {Promise<UmbServerUpgradeCheck | null>} The server upgrade check result or null if the check failed.
101+
*/
102+
async #fetchServerUpgradeCheck(
103+
versionCheckPeriod: number,
104+
currentVersion: string,
105+
): Promise<UmbServerUpgradeCheck | null> {
106+
// Check the server for an upgrade because we have no stored check or the stored check is invalid
56107
const { data } = await tryExecute(ServerService.getServerUpgradeCheck());
57108

58109
if (data) {
59110
// Save the last check date including the data received
60111
const expiresAt = new Date();
61112
expiresAt.setDate(expiresAt.getDate() + versionCheckPeriod);
62-
const upgradeCheck = { ...data, expires: expiresAt.toISOString() } satisfies UmbServerUpgradeCheck;
113+
const upgradeCheck = {
114+
...data,
115+
expires: expiresAt.toISOString(),
116+
version: currentVersion,
117+
createdAt: new Date().toISOString(),
118+
} satisfies UmbServerUpgradeCheck;
63119
localStorage.setItem('umb:serverUpgradeCheck', JSON.stringify(upgradeCheck));
64-
localStorage.setItem('umb:lastUpgradeCheck', now.toISOString());
65120

66121
// Only return if we have a valid type
67122
if (data.type.toLowerCase() !== 'none') {
68123
return upgradeCheck;
69124
}
70125
}
71126

72-
return null;
73-
}
74-
75-
#getStoredServerUpgradeCheck(lastCheck: Date): UmbServerUpgradeCheck | null {
76-
const storedCheck = localStorage.getItem('umb:serverUpgradeCheck');
77-
if (storedCheck) {
78-
const upgradeCheck: UmbServerUpgradeCheck = JSON.parse(storedCheck);
79-
// Check that the stored check is not older than the last check
80-
const expiresAt = new Date(upgradeCheck.expires);
81-
if (expiresAt.getTime() > lastCheck.getTime()) {
82-
if (upgradeCheck.type.toLowerCase() !== 'none') {
83-
return upgradeCheck;
84-
}
85-
} else {
86-
localStorage.removeItem('umb:serverUpgradeCheck');
87-
}
88-
}
89-
90-
return null;
127+
return null; // no upgrade available
91128
}
92129
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
import type { UpgradeCheckResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
22

3-
export type UmbServerUpgradeCheck = UpgradeCheckResponseModel & { expires: string };
3+
export type UmbServerUpgradeCheck = UpgradeCheckResponseModel & {
4+
expires: string;
5+
version?: string;
6+
createdAt?: string;
7+
};

0 commit comments

Comments
 (0)