mirror of
https://github.com/nasa/openmct.git
synced 2024-12-18 20:57:53 +00:00
Add script to lock object sub-tree and fix object locking bugs (#7855)
* Script for locking an object tree * Show lock button if locked * Do not allow properties editing of locked objects * Remove package-lock.json * Added p-debounce * Allow duplication of locked objects * Better user feedback * Add semaphores to prevent file handle exhaustion * Leverage official Apache Couch library - nano. Clean up dependencies. Default to environment variables for couch config. Simplify batching mechanism to make it synchronouse * Added lock user attribution * Remove unused code * Modify open script for adding auth design doc * Added script for creating auth design doc * Add css class for disallow unlock * Add user attribution to lock button * Fix import * Typo * User it was locked by, not current user. Wow. * Closes #7877 - Front-end sanding and shimming: displays <span> instead of button when domainObject.disallowUnlock. * Fixed bug where lock is shown even if object is not locked --------- Co-authored-by: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov> Co-authored-by: Jesse Mazzella <ozyx@users.noreply.github.com>
This commit is contained in:
parent
c43ef64733
commit
703186adf1
94
package-lock.json
generated
94
package-lock.json
generated
@ -66,6 +66,7 @@
|
||||
"moment": "2.30.1",
|
||||
"moment-duration-format": "2.3.2",
|
||||
"moment-timezone": "0.5.41",
|
||||
"nano": "10.1.4",
|
||||
"npm-run-all2": "6.1.2",
|
||||
"nyc": "15.1.0",
|
||||
"painterro": "1.2.87",
|
||||
@ -2463,6 +2464,12 @@
|
||||
"@mdn/browser-compat-data": "^5.2.34"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/axe-core": {
|
||||
"version": "4.8.4",
|
||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.8.4.tgz",
|
||||
@ -2472,6 +2479,17 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-loader": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.0.tgz",
|
||||
@ -3012,6 +3030,18 @@
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/comma-separated-values": {
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/comma-separated-values/-/comma-separated-values-3.6.4.tgz",
|
||||
@ -4102,6 +4132,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@ -5755,6 +5794,20 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@ -7896,6 +7949,35 @@
|
||||
"multicast-dns": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/nano": {
|
||||
"version": "10.1.4",
|
||||
"resolved": "https://registry.npmjs.org/nano/-/nano-10.1.4.tgz",
|
||||
"integrity": "sha512-bJOFIPLExIbF6mljnfExXX9Cub4W0puhDjVMp+qV40xl/DBvgKao7St4+6/GB6EoHZap7eFnrnx4mnp5KYgwJA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"axios": "^1.7.4",
|
||||
"node-abort-controller": "^3.1.1",
|
||||
"qs": "^6.13.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/nano/node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
@ -7935,6 +8017,12 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-abort-controller": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
|
||||
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
@ -9185,6 +9273,12 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
|
@ -69,6 +69,7 @@
|
||||
"moment": "2.30.1",
|
||||
"moment-duration-format": "2.3.2",
|
||||
"moment-timezone": "0.5.41",
|
||||
"nano": "10.1.4",
|
||||
"npm-run-all2": "6.1.2",
|
||||
"nyc": "15.1.0",
|
||||
"painterro": "1.2.87",
|
||||
@ -156,4 +157,4 @@
|
||||
"keywords": [
|
||||
"nasa"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -109,8 +109,9 @@ class DuplicateAction {
|
||||
let currentParentKeystring = this.openmct.objects.makeKeyString(currentParent.identifier);
|
||||
let parentCandidateKeystring = this.openmct.objects.makeKeyString(parentCandidate.identifier);
|
||||
let objectKeystring = this.openmct.objects.makeKeyString(this.object.identifier);
|
||||
const isLocked = parentCandidate.locked === true;
|
||||
|
||||
if (!this.openmct.objects.isPersistable(parentCandidate.identifier)) {
|
||||
if (isLocked || !this.openmct.objects.isPersistable(parentCandidate.identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -139,10 +140,9 @@ class DuplicateAction {
|
||||
const parentType = parent && this.openmct.types.get(parent.type);
|
||||
const child = objectPath[0];
|
||||
const childType = child && this.openmct.types.get(child.type);
|
||||
const locked = child.locked ? child.locked : parent && parent.locked;
|
||||
const isPersistable = this.openmct.objects.isPersistable(child.identifier);
|
||||
|
||||
if (locked || !isPersistable) {
|
||||
if (!isPersistable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ class EditPropertiesAction extends PropertiesAction {
|
||||
const definition = this._getTypeDefinition(object.type);
|
||||
const persistable = this.openmct.objects.isPersistable(object.identifier);
|
||||
|
||||
return persistable && definition && definition.creatable;
|
||||
return persistable && definition && definition.creatable && !object.locked;
|
||||
}
|
||||
|
||||
invoke(objectPath) {
|
||||
|
@ -96,6 +96,8 @@ export default {
|
||||
const createdTimestamp = this.domainObject.created;
|
||||
const createdBy = this.domainObject.createdBy ? this.domainObject.createdBy : UNKNOWN_USER;
|
||||
const modifiedBy = this.domainObject.modifiedBy ? this.domainObject.modifiedBy : UNKNOWN_USER;
|
||||
const locked = this.domainObject.locked;
|
||||
const lockedBy = this.domainObject.lockedBy ?? UNKNOWN_USER;
|
||||
const modifiedTimestamp = this.domainObject.modified
|
||||
? this.domainObject.modified
|
||||
: this.domainObject.created;
|
||||
@ -148,6 +150,13 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
if (locked === true) {
|
||||
details.push({
|
||||
name: 'Locked By',
|
||||
value: lockedBy
|
||||
});
|
||||
}
|
||||
|
||||
if (version) {
|
||||
details.push({
|
||||
name: 'Version',
|
||||
|
191
src/plugins/persistence/couch/scripts/lockObjects.mjs
Normal file
191
src/plugins/persistence/couch/scripts/lockObjects.mjs
Normal file
@ -0,0 +1,191 @@
|
||||
import http from 'http';
|
||||
import nano from 'nano';
|
||||
import { parseArgs } from 'util';
|
||||
|
||||
const COUCH_URL = process.env.OPENMCT_COUCH_URL || 'http://127.0.0.1:5984';
|
||||
const COUCH_DB_NAME = process.env.OPENMCT_DATABASE_NAME || 'openmct';
|
||||
|
||||
const {
|
||||
values: { couchUrl, database, lock, unlock, startObjectKeystring, user, pass }
|
||||
} = parseArgs({
|
||||
options: {
|
||||
couchUrl: {
|
||||
type: 'string',
|
||||
default: COUCH_URL
|
||||
},
|
||||
database: {
|
||||
type: 'string',
|
||||
short: 'd',
|
||||
default: COUCH_DB_NAME
|
||||
},
|
||||
lock: {
|
||||
type: 'boolean',
|
||||
short: 'l'
|
||||
},
|
||||
unlock: {
|
||||
type: 'boolean',
|
||||
short: 'u'
|
||||
},
|
||||
startObjectKeystring: {
|
||||
type: 'string',
|
||||
short: 'o',
|
||||
default: 'mine'
|
||||
},
|
||||
user: {
|
||||
type: 'string'
|
||||
},
|
||||
pass: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const BATCH_SIZE = 100;
|
||||
const SOCKET_POOL_SIZE = 100;
|
||||
|
||||
const locked = lock === true;
|
||||
console.info(`Connecting to ${couchUrl}/${database}`);
|
||||
console.info(`${locked ? 'Locking' : 'Unlocking'} all children of ${startObjectKeystring}`);
|
||||
|
||||
const poolingAgent = new http.Agent({
|
||||
keepAlive: true,
|
||||
maxSockets: SOCKET_POOL_SIZE
|
||||
});
|
||||
|
||||
const db = nano({
|
||||
url: couchUrl,
|
||||
requestDefaults: {
|
||||
agent: poolingAgent
|
||||
}
|
||||
}).use(database);
|
||||
db.auth(user, pass);
|
||||
|
||||
if (!unlock && !lock) {
|
||||
throw new Error('Either -l or -u option is required');
|
||||
}
|
||||
|
||||
const startObjectIdentifier = keystringToIdentifier(startObjectKeystring);
|
||||
const documentBatch = [];
|
||||
const alreadySeen = new Set();
|
||||
let updatedDocumentCount = 0;
|
||||
|
||||
await processObjectTreeFrom(startObjectIdentifier);
|
||||
//Persist final batch
|
||||
await persistBatch();
|
||||
console.log(`Processed ${updatedDocumentCount} documents`);
|
||||
|
||||
function processObjectTreeFrom(parentObjectIdentifier) {
|
||||
//1. Fetch document for identifier;
|
||||
return fetchDocument(parentObjectIdentifier)
|
||||
.then(async (document) => {
|
||||
if (document !== undefined) {
|
||||
if (!alreadySeen.has(document._id)) {
|
||||
alreadySeen.add(document._id);
|
||||
//2. Lock or unlock object
|
||||
document.model.locked = locked;
|
||||
document.model.disallowUnlock = locked;
|
||||
|
||||
if (locked) {
|
||||
document.model.lockedBy = 'script';
|
||||
} else {
|
||||
delete document.model.lockedBy;
|
||||
}
|
||||
//3. Push document to a batch
|
||||
documentBatch.push(document);
|
||||
//4. Persist batch if necessary, reporting failures
|
||||
await persistBatchIfNeeded();
|
||||
//5. Repeat for each composee
|
||||
const composition = document.model.composition || [];
|
||||
return Promise.all(
|
||||
composition.map((composee) => {
|
||||
return processObjectTreeFrom(composee);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(`Error ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchDocument(identifierOrKeystring) {
|
||||
let keystring;
|
||||
if (typeof identifierOrKeystring === 'object') {
|
||||
keystring = identifierToKeystring(identifierOrKeystring);
|
||||
} else {
|
||||
keystring = identifierOrKeystring;
|
||||
}
|
||||
|
||||
try {
|
||||
const document = await db.get(keystring);
|
||||
|
||||
return document;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function persistBatchIfNeeded() {
|
||||
if (documentBatch.length >= BATCH_SIZE) {
|
||||
return persistBatch();
|
||||
} else {
|
||||
//Noop - batch is not big enough yet
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function persistBatch() {
|
||||
try {
|
||||
const localBatch = [].concat(documentBatch);
|
||||
|
||||
//Immediately clear the shared batch array. This asynchronous process is non-blocking, and
|
||||
//we don't want to try and persist the same batch multiple times while we are waiting for
|
||||
//the subsequent bulk operation to complete.
|
||||
updatedDocumentCount += documentBatch.length;
|
||||
|
||||
documentBatch.splice(0, documentBatch.length);
|
||||
const response = await db.bulk({ docs: localBatch });
|
||||
|
||||
if (response instanceof Array) {
|
||||
response.forEach((r) => {
|
||||
console.info(JSON.stringify(r));
|
||||
});
|
||||
} else {
|
||||
console.info(JSON.stringify(response));
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Array) {
|
||||
error.forEach((e) => console.error(JSON.stringify(e)));
|
||||
} else {
|
||||
console.error(`${error.statusCode} - ${error.reason}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function keystringToIdentifier(keystring) {
|
||||
const tokens = keystring.split(':');
|
||||
if (tokens.length === 2) {
|
||||
return {
|
||||
namespace: tokens[0],
|
||||
key: tokens[1]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
namespace: '',
|
||||
key: tokens[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function identifierToKeystring(identifier) {
|
||||
if (typeof identifier === 'string') {
|
||||
return identifier;
|
||||
} else if (typeof identifier === 'object') {
|
||||
if (identifier.namespace) {
|
||||
return `${identifier.namespace}:${identifier.key}`;
|
||||
} else {
|
||||
return identifier.key;
|
||||
}
|
||||
}
|
||||
}
|
@ -160,6 +160,24 @@ add_index_and_views() {
|
||||
echo "Unable to create annotation_keystring_index"
|
||||
echo $response
|
||||
fi
|
||||
|
||||
# Add auth database for locked objects
|
||||
response=$(curl --silent --user "${CURL_USERPASS_ARG}" --request PUT "$COUCH_BASE_LOCAL"/"$OPENMCT_DATABASE_NAME"/_design/auth \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"_id": "_design/auth",
|
||||
"language": "javascript",
|
||||
"validate_doc_update": "function (newDoc, oldDoc, userCtx) { if (userCtx.roles.indexOf('\''_admin'\'') !== -1) { return; } else if (oldDoc === null) { return; } else if (oldDoc.model.type === '\''timer'\'' || oldDoc.model.type === '\''notebook'\'' || oldDoc.model.type === '\''restricted-notebook'\'') { if (oldDoc.model.name !== newDoc.model.name) { throw ({ forbidden: '\''Read-only object'\'' }); } else { return; } } else if (oldDoc.model.locked === true && oldDoc.model.disallowUnlock === true) { throw ({ forbidden: '\''Read-only object'\'' }); } else { return; }}"
|
||||
}')
|
||||
|
||||
if [[ $response =~ "\"ok\":true" ]]; then
|
||||
echo "Successfully created _design/auth design document for locked objects"
|
||||
elif [[ $response =~ "\"error\":\"conflict\"" ]]; then
|
||||
echo "_design/auth already exists, skipping creation"
|
||||
else
|
||||
echo "Unable to create _design/auth"
|
||||
echo $response
|
||||
fi
|
||||
}
|
||||
|
||||
# Main script execution
|
||||
|
@ -36,7 +36,7 @@
|
||||
ref="objectName"
|
||||
class="l-browse-bar__object-name c-object-label__name"
|
||||
:class="{ 'c-input-inline': isPersistable }"
|
||||
:contenteditable="isPersistable"
|
||||
:contenteditable="isNameEditable"
|
||||
@blur="updateName"
|
||||
@keydown.enter.prevent
|
||||
@keyup.enter.prevent="updateNameOnEnterKeyPress"
|
||||
@ -78,7 +78,7 @@
|
||||
></button>
|
||||
|
||||
<button
|
||||
v-if="isViewEditable & !isEditing"
|
||||
v-if="shouldShowLock"
|
||||
:aria-label="lockedOrUnlockedTitle"
|
||||
:title="lockedOrUnlockedTitle"
|
||||
:class="{
|
||||
@ -88,6 +88,13 @@
|
||||
@click="toggleLock(!domainObject.locked)"
|
||||
></button>
|
||||
|
||||
<span
|
||||
v-else-if="domainObject?.locked"
|
||||
class="icon-lock"
|
||||
aria-label="Locked for editing, cannot be unlocked."
|
||||
title="Locked for editing, cannot be unlocked."
|
||||
></span>
|
||||
|
||||
<button
|
||||
v-if="isViewEditable && !isEditing && !domainObject.locked"
|
||||
class="l-browse-bar__actions__edit c-button c-button--major icon-pencil"
|
||||
@ -180,6 +187,18 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isNameEditable() {
|
||||
return this.isPersistable && !this.domainObject.locked;
|
||||
},
|
||||
shouldShowLock() {
|
||||
if (this.domainObject === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (this.domainObject.disallowUnlock) {
|
||||
return false;
|
||||
}
|
||||
return this.domainObject.locked || (this.isViewEditable && !this.isEditing);
|
||||
},
|
||||
statusClass() {
|
||||
return this.status ? `is-status--${this.status}` : '';
|
||||
},
|
||||
@ -253,11 +272,19 @@ export default {
|
||||
return false;
|
||||
},
|
||||
lockedOrUnlockedTitle() {
|
||||
let title;
|
||||
if (this.domainObject.locked) {
|
||||
return 'Locked for editing - click to unlock.';
|
||||
if (this.domainObject.lockedBy !== undefined) {
|
||||
title = `Locked for editing by ${this.domainObject.lockedBy}. `;
|
||||
} else {
|
||||
title = 'Locked for editing. ';
|
||||
}
|
||||
title += 'Click to unlock.';
|
||||
} else {
|
||||
return 'Unlocked for editing - click to lock.';
|
||||
title = 'Unlocked for editing, click to lock.';
|
||||
}
|
||||
|
||||
return title;
|
||||
},
|
||||
domainObjectName() {
|
||||
return this.domainObject?.name ?? '';
|
||||
@ -288,7 +315,6 @@ export default {
|
||||
document.addEventListener('click', this.closeViewAndSaveMenu);
|
||||
this.promptUserbeforeNavigatingAway = this.promptUserbeforeNavigatingAway.bind(this);
|
||||
window.addEventListener('beforeunload', this.promptUserbeforeNavigatingAway);
|
||||
|
||||
this.openmct.editor.on('isEditing', (isEditing) => {
|
||||
this.isEditing = isEditing;
|
||||
});
|
||||
@ -421,8 +447,27 @@ export default {
|
||||
this.actionCollection.off('update', this.updateActionItems);
|
||||
delete this.actionCollection;
|
||||
},
|
||||
toggleLock(flag) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'locked', flag);
|
||||
async toggleLock(flag) {
|
||||
if (!this.domainObject.disallowUnlock) {
|
||||
const wasTransactionActive = this.openmct.objects.isTransactionActive();
|
||||
let transaction;
|
||||
|
||||
if (!wasTransactionActive) {
|
||||
transaction = this.openmct.objects.startTransaction();
|
||||
}
|
||||
|
||||
this.openmct.objects.mutate(this.domainObject, 'locked', flag);
|
||||
const user = await this.openmct.user.getCurrentUser();
|
||||
|
||||
if (user !== undefined) {
|
||||
this.openmct.objects.mutate(this.domainObject, 'lockedBy', user.id);
|
||||
}
|
||||
|
||||
if (!wasTransactionActive) {
|
||||
await transaction.commit();
|
||||
this.openmct.objects.endTransaction();
|
||||
}
|
||||
}
|
||||
},
|
||||
setStatus(status) {
|
||||
this.status = status;
|
||||
|
Loading…
Reference in New Issue
Block a user