Skip to content
39 changes: 38 additions & 1 deletion elements/directory/nuxeo-vocabulary-management.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,13 @@

<template is="dom-if" if="[[_isVocabularySelected(selectedVocabulary)]]">
<div class="top actions">
<paper-button id="addEntry" class="text" on-tap="_createEntry" aria-labelledby="addEntryLabel">
<paper-button
id="addEntry"
class="text"
on-tap="_createEntry"
hidden$="[[_isReadOnly]]"
aria-labelledby="addEntryLabel"
>
<span id="addEntryLabel">+ [[i18n('vocabularyManagement.addEntry')]]</span>
</paper-button>
</div>
Expand All @@ -142,6 +148,7 @@
id="edit-button-[[index]]"
icon="nuxeo:edit"
on-tap="_editEntry"
hidden$="[[_isReadOnly]]"
aria-labelledby="editButtonTooltip"
></paper-icon-button>
<nuxeo-tooltip for="edit-button-[[index]]" id="editButtonTooltip"
Expand All @@ -152,6 +159,7 @@
name="delete"
icon="nuxeo:delete"
on-tap="_deleteEntry"
hidden$="[[_isReadOnly]]"
aria-labelledby="deleteButtonTooltip"
></paper-icon-button>
<nuxeo-tooltip for="delete-button-[[index]]" id="deleteButtonTooltip"
Expand Down Expand Up @@ -212,6 +220,11 @@
type: String,
computed: '_schemaFor(selectedVocabulary)',
},
_isReadOnly: {
type: Boolean,
computed: '_computeReadOnly(selectedVocabulary, vocabularies)',
value: true,
Comment thread
vaibhavagarwal4-lab marked this conversation as resolved.
Outdated
},
},

observers: ['_refresh(selectedVocabulary)'],
Expand All @@ -220,6 +233,18 @@
return entries.length ? 'display: block;' : 'display: none;';
},

// Returns true when the currently selected vocabulary is declared read-only on
// the server (NXP-31054 exposes the `readOnly` flag on each directory entity).
// Falls back to false on older servers that omit the field.
_computeReadOnly(selectedVocabulary, vocabularies) {
if (!selectedVocabulary || !Array.isArray(vocabularies)) {
return false;
}
const v = vocabularies.find((d) => d && d.name === selectedVocabulary);
return !!(v && v.readOnly);

Check warning on line 244 in elements/directory/nuxeo-vocabulary-management.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=nuxeo_nuxeo-web-ui&issues=AZ5eHnEiRLy2PeGoaaBE&open=AZ5eHnEiRLy2PeGoaaBE&pullRequest=3187

Comment thread
vaibhavagarwal4-lab marked this conversation as resolved.
Outdated
},

_visibleChanged() {
if (this.visible && !this.vocabularies) {
this.$.directory.get().then((response) => {
Expand Down Expand Up @@ -332,6 +357,9 @@
},

_deleteEntry(e) {
if (this._isReadOnly) {
return;
}
if (window.confirm(this.i18n('vocabularyManagement.confirmDelete'))) {
const { item } = e.target.parentNode;
this.$.directory.path = `/directory/${item.directoryName}/${item.properties.id}`;
Expand Down Expand Up @@ -360,6 +388,9 @@
},

_editEntry(e) {
if (this._isReadOnly) {
return;
}
this._new = false;
this._selectedEntry = e.target.parentNode.item;
this.$.vocabularyEditDialog.toggle();
Expand All @@ -372,6 +403,9 @@
},

_save() {
if (this._isReadOnly) {
return;
}
if (!this.$.layout.validate()) {
return;
}
Expand Down Expand Up @@ -429,6 +463,9 @@
},

_createEntry() {
if (this._isReadOnly) {
return;
}
const emptyEntry = {
'entity-type': 'directoryEntry',
directoryName: this.selectedVocabulary,
Expand Down
67 changes: 65 additions & 2 deletions test/nuxeo-vocabulary-management.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ suite('nuxeo-vocabulary-management', () => {
sinon.stub(element, 'i18n').callsFake((key) => key);
// Provide vocabularies to prevent _schemaFor from crashing
element.vocabularies = [
{ name: 'coverage', schema: 'coverage', parent: '' },
{ name: 'continent', schema: 'xvocabulary', parent: 'coverage' },
{ name: 'coverage', schema: 'coverage', parent: '', readOnly: false },
{ name: 'continent', schema: 'xvocabulary', parent: 'coverage', readOnly: false },
{ name: 'country', schema: 'xvocabulary', parent: 'continent', readOnly: true },
{ name: 'nature', schema: '', parent: '' },
];
});
Expand Down Expand Up @@ -83,6 +84,68 @@ suite('nuxeo-vocabulary-management', () => {
});
});

suite('_computeReadOnly', () => {
test('should return false when no vocabulary is selected', () => {
expect(element._computeReadOnly('', element.vocabularies)).to.be.false;
expect(element._computeReadOnly(null, element.vocabularies)).to.be.false;
});

test('should return false when vocabularies is not an array', () => {
expect(element._computeReadOnly('country', null)).to.be.false;
expect(element._computeReadOnly('country', undefined)).to.be.false;
});

test('should return false for a writable vocabulary', () => {
expect(element._computeReadOnly('coverage', element.vocabularies)).to.be.false;
});

test('should return true for a readOnly vocabulary', () => {
expect(element._computeReadOnly('country', element.vocabularies)).to.be.true;
});

test('should return false when the vocabulary has no readOnly field (legacy server)', () => {
expect(element._computeReadOnly('nature', element.vocabularies)).to.be.false;
});

test('should expose _isReadOnly as a computed property reflecting the selection', () => {
element.selectedVocabulary = 'country';
expect(element._isReadOnly).to.be.true;
element.selectedVocabulary = 'coverage';
expect(element._isReadOnly).to.be.false;
});
Comment thread
vaibhavagarwal4-lab marked this conversation as resolved.
Outdated
});

suite('read-only guards', () => {
setup(() => {
element.selectedVocabulary = 'country';
});

test('_createEntry should be a no-op for readOnly vocabularies', () => {
const dialog = { toggle: sinon.spy() };
element.$.vocabularyEditDialog = dialog;
element._createEntry();
Comment thread
vaibhavagarwal4-lab marked this conversation as resolved.
Outdated
expect(dialog.toggle).to.not.have.been.called;
expect(element._selectedEntry).to.be.undefined;
});

test('_save should be a no-op for readOnly vocabularies', () => {
const layout = { validate: sinon.spy() };
element.$.layout = layout;
element._save();
expect(layout.validate).to.not.have.been.called;
});

test('_deleteEntry should be a no-op for readOnly vocabularies', () => {
const confirmStub = sinon.stub(window, 'confirm').returns(true);
try {
element._deleteEntry({ target: { parentNode: { item: { directoryName: 'country', properties: { id: 'x' } } } } });
expect(confirmStub).to.not.have.been.called;
} finally {
confirmStub.restore();
}
});
});

suite('_computeDialogHeading', () => {
test('should return addEntry key for new entry', () => {
expect(element._computeDialogHeading(true)).to.equal('vocabularyManagement.popup.addEntry');
Expand Down
Loading