Skip to content

Commit 053da89

Browse files
Merge pull request #1985 from umbraco/v14/bugfix/document-public-access-cancelled-server-requests
Bugfix: Document Public Access cancelled server requests
2 parents 877b5a9 + 89fef64 commit 053da89

File tree

7 files changed

+140
-43
lines changed

7 files changed

+140
-43
lines changed

src/assets/lang/en-us.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default {
4646
logout: 'Exit',
4747
move: 'Move to',
4848
notify: 'Notifications',
49-
protect: 'Restrict Public Access',
49+
protect: 'Public Access',
5050
publish: 'Publish',
5151
refreshNode: 'Reload',
5252
remove: 'Remove',

src/assets/lang/en.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default {
4545
logout: 'Exit',
4646
move: 'Move to',
4747
notify: 'Notifications',
48-
protect: 'Restrict Public Access',
48+
protect: 'Public Access',
4949
publish: 'Publish',
5050
refreshNode: 'Reload',
5151
remove: 'Remove',

src/packages/documents/documents/entity-actions/public-access/manifests.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ const entityActions: Array<ManifestTypes> = [
2525
{
2626
alias: UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS,
2727
},
28+
{
29+
alias: 'Umb.Condition.SectionUserPermission',
30+
match: 'Umb.Section.Members',
31+
},
2832
],
2933
},
3034
];

src/packages/documents/documents/entity-actions/public-access/modal/public-access-modal.element.ts

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { UmbDocumentPublicAccessRepository } from '../repository/public-access.repository.js';
2-
import { UmbDocumentDetailRepository } from '../../../repository/index.js';
2+
import { UmbDocumentItemRepository } from '../../../repository/index.js';
33
import type { UmbInputDocumentElement } from '../../../components/index.js';
44
import type { UmbPublicAccessModalData, UmbPublicAccessModalValue } from './public-access-modal.token.js';
55
import { css, customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit';
66
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
77
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
8-
import type { UmbInputMemberElement } from '@umbraco-cms/backoffice/member';
9-
import type { UmbInputMemberGroupElement } from '@umbraco-cms/backoffice/member-group';
8+
import { UmbMemberDetailRepository, type UmbInputMemberElement } from '@umbraco-cms/backoffice/member';
9+
import { UmbMemberGroupItemRepository, type UmbInputMemberGroupElement } from '@umbraco-cms/backoffice/member-group';
1010
import type { PublicAccessRequestModel } from '@umbraco-cms/backoffice/external/backend-api';
1111
import type { UUIRadioEvent } from '@umbraco-cms/backoffice/external/uui';
1212

@@ -32,56 +32,52 @@ export class UmbPublicAccessModalElement extends UmbModalBaseElement<
3232
private _selection: Array<string> = [];
3333

3434
@state()
35-
private _loginPageId?: string;
35+
private _loginDocumentId?: string;
3636

3737
@state()
38-
private _errorPageId?: string;
38+
private _errorDocumentId?: string;
3939

4040
// Init
4141

4242
firstUpdated() {
4343
this.#unique = this.data?.unique;
4444
this.#getDocumentName();
45-
this.#getPublicAccessModel();
4645
}
4746

4847
async #getDocumentName() {
4948
if (!this.#unique) return;
5049
// Should this be done here or in the action file?
51-
const { data } = await new UmbDocumentDetailRepository(this).requestByUnique(this.#unique);
50+
const { data } = await new UmbDocumentItemRepository(this).requestItems([this.#unique]);
5251
if (!data) return;
52+
const item = data[0];
5353
//TODO How do we ensure we get the correct variant?
54-
this._documentName = data.variants[0]?.name;
54+
this._documentName = item.variants[0]?.name;
55+
56+
if (item.isProtected) {
57+
this.#getPublicAccessModel();
58+
}
5559
}
5660

5761
async #getPublicAccessModel() {
5862
if (!this.#unique) return;
59-
//const { data } = (await this.#publicAccessRepository.read(this.#unique));
60-
// TODO Currently returning "void". Remove mock data when API is ready. Will it be Response or Request model?
61-
const data: any = undefined;
62-
/*const data: PublicAccessResponseModel = {
63-
members: [{ name: 'Agent', id: '007' }],
64-
groups: [],
65-
loginPageId: '123',
66-
errorPageId: '456',
67-
};*/
63+
const { data } = await this.#publicAccessRepository.read(this.#unique);
6864

6965
if (!data) return;
7066
this.#isNew = false;
7167
this._startPage = false;
7268

7369
// Specific or Groups
74-
this._specific = data.members.length > 0 ? true : false;
70+
this._specific = data.members.length > 0;
7571

7672
//selection
7773
if (data.members.length > 0) {
78-
this._selection = data.members.map((m: any) => m.id);
74+
this._selection = data.members.map((m) => m.id);
7975
} else if (data.groups.length > 0) {
80-
this._selection = data.groups.map((g: any) => g.id);
76+
this._selection = data.groups.map((g) => g.id);
8177
}
8278

83-
this._loginPageId = data.loginPageId;
84-
this._errorPageId = data.errorPageId;
79+
this._loginDocumentId = data.loginDocument.id;
80+
this._errorDocumentId = data.errorDocument.id;
8581
}
8682

8783
// Modal events
@@ -91,30 +87,54 @@ export class UmbPublicAccessModalElement extends UmbModalBaseElement<
9187
}
9288

9389
async #handleSave() {
94-
if (!this._loginPageId || !this._errorPageId || !this.#unique) return;
95-
96-
const groups = this._specific ? [] : this._selection;
97-
const members = this._specific ? this._selection : [];
90+
if (!this._loginDocumentId || !this._errorDocumentId || !this.#unique) return;
9891

92+
// TODO: [v15] Currently the Management API doesn't support passing the member/group ids, only the userNames/names.
93+
// This is a temporary solution where we have to look them up until the API is updated to support this.
9994
const requestBody: PublicAccessRequestModel = {
100-
memberGroupNames: groups,
101-
memberUserNames: members,
102-
loginDocument: { id: this._loginPageId },
103-
errorDocument: { id: this._errorPageId },
95+
memberGroupNames: [],
96+
memberUserNames: [],
97+
loginDocument: { id: this._loginDocumentId },
98+
errorDocument: { id: this._errorDocumentId },
10499
};
105100

101+
if (this._specific) {
102+
// Members
103+
// user name is not part of the item model, so we need to look it up from the member detail repository
104+
// be aware that the detail repository requires access to the member section.
105+
const repo = new UmbMemberDetailRepository(this);
106+
const promises = this._selection.map((memberId) => repo.requestByUnique(memberId));
107+
const responses = await Promise.all(promises);
108+
const memberUserNames = responses
109+
.filter((response) => response.data)
110+
.map((response) => response.data?.username) as string[];
111+
112+
requestBody.memberUserNames = memberUserNames;
113+
} else {
114+
// Groups
115+
const repo = new UmbMemberGroupItemRepository(this);
116+
const { data } = await repo.requestItems(this._selection);
117+
if (!data) throw new Error('No Member groups returned');
118+
119+
const groupNames = data
120+
.filter((groupItem) => this._selection.includes(groupItem.unique))
121+
.map((groupItem) => groupItem.name);
122+
123+
requestBody.memberGroupNames = groupNames;
124+
}
125+
106126
if (this.#isNew) {
107-
this.#publicAccessRepository.create(this.#unique, requestBody);
127+
await this.#publicAccessRepository.create(this.#unique, requestBody);
108128
} else {
109-
this.#publicAccessRepository.update(this.#unique, requestBody);
129+
await this.#publicAccessRepository.update(this.#unique, requestBody);
110130
}
111131

112132
this.modalContext?.submit();
113133
}
114134

115-
#handleDelete() {
135+
async #handleDelete() {
116136
if (!this.#unique) return;
117-
this.#publicAccessRepository.delete(this.#unique);
137+
await this.#publicAccessRepository.delete(this.#unique);
118138
this.modalContext?.submit();
119139
}
120140

@@ -125,11 +145,11 @@ export class UmbPublicAccessModalElement extends UmbModalBaseElement<
125145
// Change Events
126146

127147
#onChangeLoginPage(e: CustomEvent) {
128-
this._loginPageId = (e.target as UmbInputDocumentElement).selection[0];
148+
this._loginDocumentId = (e.target as UmbInputDocumentElement).selection[0];
129149
}
130150

131151
#onChangeErrorPage(e: CustomEvent) {
132-
this._errorPageId = (e.target as UmbInputDocumentElement).selection[0];
152+
this._errorDocumentId = (e.target as UmbInputDocumentElement).selection[0];
133153
}
134154

135155
#onChangeGroup(e: CustomEvent) {
@@ -182,7 +202,10 @@ export class UmbPublicAccessModalElement extends UmbModalBaseElement<
182202
<small>
183203
<umb-localize key="publicAccess_paLoginPageHelp"> Choose the page that contains the login form </umb-localize>
184204
</small>
185-
<umb-input-document max="1" @change=${this.#onChangeLoginPage}></umb-input-document>
205+
<umb-input-document
206+
.value=${this._loginDocumentId ? this._loginDocumentId : ''}
207+
max="1"
208+
@change=${this.#onChangeLoginPage}></umb-input-document>
186209
</div>
187210
<br />
188211
<div class="select-item">
@@ -192,7 +215,10 @@ export class UmbPublicAccessModalElement extends UmbModalBaseElement<
192215
Used when people are logged on, but do not have access
193216
</umb-localize>
194217
</small>
195-
<umb-input-document max="1" @change=${this.#onChangeErrorPage}></umb-input-document>
218+
<umb-input-document
219+
.value=${this._errorDocumentId ? this._errorDocumentId : ''}
220+
max="1"
221+
@change=${this.#onChangeErrorPage}></umb-input-document>
196222
</div>`;
197223
}
198224

@@ -220,7 +246,7 @@ export class UmbPublicAccessModalElement extends UmbModalBaseElement<
220246
look="primary"
221247
color="positive"
222248
label=${this.localize.term('buttons_save')}
223-
?disabled=${!this._loginPageId || !this._errorPageId || this._selection.length === 0}
249+
?disabled=${!this._loginDocumentId || !this._errorDocumentId || this._selection.length === 0}
224250
@click="${this.#handleSave}"></uui-button>`
225251
: html`<uui-button
226252
slot="actions"

src/packages/documents/documents/entity-actions/public-access/public-access.action.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { UMB_PUBLIC_ACCESS_MODAL } from './modal/public-access-modal.token.js';
22
import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action';
3-
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
3+
import {
4+
UmbEntityActionBase,
5+
UmbRequestReloadChildrenOfEntityEvent,
6+
UmbRequestReloadStructureForEntityEvent,
7+
} from '@umbraco-cms/backoffice/entity-action';
48
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
59
import type { UmbDocumentDetailRepository } from '@umbraco-cms/backoffice/document';
10+
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
611

712
export class UmbDocumentPublicAccessEntityAction extends UmbEntityActionBase<never> {
813
constructor(host: UmbDocumentDetailRepository, args: UmbEntityActionArgs<never>) {
@@ -14,5 +19,23 @@ export class UmbDocumentPublicAccessEntityAction extends UmbEntityActionBase<nev
1419
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
1520
const modal = modalManager.open(this, UMB_PUBLIC_ACCESS_MODAL, { data: { unique: this.args.unique } });
1621
await modal.onSubmit();
22+
this.#requestReloadEntity();
23+
}
24+
25+
async #requestReloadEntity() {
26+
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
27+
28+
const entityStructureEvent = new UmbRequestReloadStructureForEntityEvent({
29+
unique: this.args.unique,
30+
entityType: this.args.entityType,
31+
});
32+
33+
const entityChildrenEvent = new UmbRequestReloadChildrenOfEntityEvent({
34+
unique: this.args.unique,
35+
entityType: this.args.entityType,
36+
});
37+
38+
actionEventContext.dispatchEvent(entityStructureEvent);
39+
actionEventContext.dispatchEvent(entityChildrenEvent);
1740
}
1841
}

src/packages/documents/documents/entity-actions/public-access/repository/public-access.repository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class UmbDocumentPublicAccessRepository extends UmbControllerBase impleme
2525
if (!unique) throw new Error('unique is missing');
2626
if (!data) throw new Error('Data is missing');
2727

28-
const { error } = await this.#dataSource.update(unique, data);
28+
const { error } = await this.#dataSource.create(unique, data);
2929
if (!error) {
3030
const notification = { data: { message: `Public acccess setting created` } };
3131
this.#notificationContext?.peek('positive', notification);

src/packages/documents/documents/tree/tree-item/document-tree-item.element.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export class UmbDocumentTreeItemElement extends UmbTreeItemElementBase<UmbDocume
7474
${this.item?.documentType.icon
7575
? html`
7676
<umb-icon id="icon" slot="icon" name="${this.item.documentType.icon}"></umb-icon>
77+
${this.item.isProtected ? this.#renderIsProtectedIcon() : nothing}
7778
<!--
7879
// TODO: implement correct status symbol
7980
<span id="status-symbol"></span>
@@ -90,6 +91,10 @@ export class UmbDocumentTreeItemElement extends UmbTreeItemElementBase<UmbDocume
9091
> `;
9192
}
9293

94+
#renderIsProtectedIcon() {
95+
return html`<umb-icon id="icon-lock" slot="icon" name="icon-lock" title="Protected"></umb-icon>`;
96+
}
97+
9398
static styles = [
9499
UmbTextStyles,
95100
css`
@@ -113,6 +118,45 @@ export class UmbDocumentTreeItemElement extends UmbTreeItemElementBase<UmbDocume
113118
border-radius: 100%;
114119
}
115120
121+
#icon-lock {
122+
position: absolute;
123+
bottom: -5px;
124+
right: -5px;
125+
font-size: 10px;
126+
background: var(--uui-color-surface);
127+
width: 14px;
128+
height: 14px;
129+
border-radius: 100%;
130+
line-height: 14px;
131+
}
132+
133+
:hover #icon-lock {
134+
background: var(--uui-color-surface-emphasis);
135+
}
136+
137+
/** Active */
138+
[active] #icon-lock {
139+
background: var(--uui-color-current);
140+
}
141+
142+
[active]:hover #icon-lock {
143+
background: var(--uui-color-current-emphasis);
144+
}
145+
146+
/** Selected */
147+
[selected] #icon-lock {
148+
background-color: var(--uui-color-selected);
149+
}
150+
151+
[selected]:hover #icon-lock {
152+
background-color: var(--uui-color-selected-emphasis);
153+
}
154+
155+
/** Disabled */
156+
[disabled] #icon-lock {
157+
background-color: var(--uui-color-disabled);
158+
}
159+
116160
.draft {
117161
opacity: 0.6;
118162
}

0 commit comments

Comments
 (0)