mirror of
https://github.com/nasa/openmct.git
synced 2024-12-24 15:26:39 +00:00
When dropping an unsupported file onto a notebook entry, tell the user it isnt supported (#7115)
* handle unknown files and deal with copy/paste * add some nice errors if couch pukes * added how to adjust couchdb limits * throw error on anntotation change * keep blockquotes * add test for blockquotes * allow multi-file drop of images too * add test for big files * spell check * check for null * need to ignore console errors * reorder tests so we can ignore console errors * when creating new entry, ready it for editing * fix tests and empty embeds * fix tests * found similar issue from notebooks in plots
This commit is contained in:
parent
a16a1d35b6
commit
26d3bd1e69
@ -483,7 +483,11 @@
|
|||||||
"websockets",
|
"websockets",
|
||||||
"swgs",
|
"swgs",
|
||||||
"memlab",
|
"memlab",
|
||||||
"devmode"
|
"devmode",
|
||||||
|
"blockquote",
|
||||||
|
"blockquotes",
|
||||||
|
"Blockquote",
|
||||||
|
"Blockquotes"
|
||||||
],
|
],
|
||||||
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US"],
|
"dictionaries": ["npm", "softwareTerms", "node", "html", "css", "bash", "en_US"],
|
||||||
"ignorePaths": [
|
"ignorePaths": [
|
||||||
|
@ -34,7 +34,6 @@ async function enterTextEntry(page, text) {
|
|||||||
await page.locator(NOTEBOOK_DROP_AREA).click();
|
await page.locator(NOTEBOOK_DROP_AREA).click();
|
||||||
|
|
||||||
// enter text
|
// enter text
|
||||||
await page.getByLabel('Notebook Entry Display').last().click();
|
|
||||||
await page.getByLabel('Notebook Entry Input').last().fill(text);
|
await page.getByLabel('Notebook Entry Input').last().fill(text);
|
||||||
await commitEntry(page);
|
await commitEntry(page);
|
||||||
}
|
}
|
||||||
@ -53,6 +52,7 @@ async function dragAndDropEmbed(page, notebookObject) {
|
|||||||
await page.click('button[title="Show selected item in tree"]');
|
await page.click('button[title="Show selected item in tree"]');
|
||||||
// Drag and drop the SWG into the notebook
|
// Drag and drop the SWG into the notebook
|
||||||
await page.dragAndDrop(`text=${swg.name}`, NOTEBOOK_DROP_AREA);
|
await page.dragAndDrop(`text=${swg.name}`, NOTEBOOK_DROP_AREA);
|
||||||
|
await commitEntry(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -279,7 +279,7 @@ test.describe('Notebook entry tests', () => {
|
|||||||
|
|
||||||
// Click .c-notebook__drag-area
|
// Click .c-notebook__drag-area
|
||||||
await page.locator('.c-notebook__drag-area').click();
|
await page.locator('.c-notebook__drag-area').click();
|
||||||
await expect(page.getByLabel('Notebook Entry Display')).toBeVisible();
|
await expect(page.getByLabel('Notebook Entry Input')).toBeVisible();
|
||||||
await expect(page.getByLabel('Notebook Entry', { exact: true })).toHaveClass(/is-selected/);
|
await expect(page.getByLabel('Notebook Entry', { exact: true })).toHaveClass(/is-selected/);
|
||||||
});
|
});
|
||||||
test('When an object is dropped into a notebook, a new entry is created and it should be focused @unstable', async ({
|
test('When an object is dropped into a notebook, a new entry is created and it should be focused @unstable', async ({
|
||||||
@ -514,10 +514,23 @@ test.describe('Notebook entry tests', () => {
|
|||||||
const childItem = page.locator('li:has-text("List Item 2") ol li:has-text("Order 2")');
|
const childItem = page.locator('li:has-text("List Item 2") ol li:has-text("Order 2")');
|
||||||
await expect(childItem).toBeVisible();
|
await expect(childItem).toBeVisible();
|
||||||
|
|
||||||
// Blocks
|
// Code Blocks
|
||||||
const blockTest = '```javascript\nconst foo = "bar";\nconst bar = "foo";\n```';
|
const codeblockTest = '```javascript\nconst foo = "bar";\nconst bar = "foo";\n```';
|
||||||
await nbUtils.enterTextEntry(page, blockTest);
|
await nbUtils.enterTextEntry(page, codeblockTest);
|
||||||
const codeBlock = page.locator('code.language-javascript:has-text("const foo = \\"bar\\";")');
|
const codeBlock = page.locator('code.language-javascript:has-text("const foo = \\"bar\\";")');
|
||||||
await expect(codeBlock).toBeVisible();
|
await expect(codeBlock).toBeVisible();
|
||||||
|
|
||||||
|
// Blockquotes
|
||||||
|
const blockquoteTest =
|
||||||
|
'This is a quote by Mark Twain:\n> "The man with a new idea is a crank\n>until the idea succeeds."';
|
||||||
|
await nbUtils.enterTextEntry(page, blockquoteTest);
|
||||||
|
const firstLineOfBlockquoteText = page.locator(
|
||||||
|
'blockquote:has-text("The man with a new idea is a crank")'
|
||||||
|
);
|
||||||
|
await expect(firstLineOfBlockquoteText).toBeVisible();
|
||||||
|
const secondLineOfBlockquoteText = page.locator(
|
||||||
|
'blockquote:has-text("until the idea succeeds")'
|
||||||
|
);
|
||||||
|
await expect(secondLineOfBlockquoteText).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -188,12 +188,11 @@ test.describe('Snapshot image tests', () => {
|
|||||||
}, fileData);
|
}, fileData);
|
||||||
|
|
||||||
await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer });
|
await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer });
|
||||||
|
await page.locator('.c-ne__save-button > button').click();
|
||||||
// be sure that entry was created
|
// be sure that entry was created
|
||||||
await expect(page.getByText('favicon-96x96.png')).toBeVisible();
|
await expect(page.getByText('favicon-96x96.png')).toBeVisible();
|
||||||
|
|
||||||
await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).click();
|
await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).click();
|
||||||
|
|
||||||
// expect large image to be displayed
|
// expect large image to be displayed
|
||||||
await expect(page.getByRole('dialog').getByText('favicon-96x96.png')).toBeVisible();
|
await expect(page.getByRole('dialog').getByText('favicon-96x96.png')).toBeVisible();
|
||||||
|
|
||||||
@ -215,3 +214,59 @@ test.describe('Snapshot image tests', () => {
|
|||||||
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1);
|
expect(await page.getByRole('img', { name: 'favicon-96x96.png thumbnail' }).count()).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe('Snapshot image failure tests', () => {
|
||||||
|
test.use({ failOnConsoleError: false });
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
//Navigate to baseURL
|
||||||
|
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||||
|
|
||||||
|
// Create Notebook
|
||||||
|
await createDomainObjectWithDefaults(page, {
|
||||||
|
type: NOTEBOOK_NAME
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Get an error notification when dropping unknown file onto notebook entry', async ({
|
||||||
|
page
|
||||||
|
}) => {
|
||||||
|
// fill Uint8Array array with some garbage data
|
||||||
|
const garbageData = new Uint8Array(100);
|
||||||
|
const fileData = Array.from(garbageData);
|
||||||
|
|
||||||
|
const dropTransfer = await page.evaluateHandle((data) => {
|
||||||
|
const dataTransfer = new DataTransfer();
|
||||||
|
const file = new File([new Uint8Array(data)], 'someGarbage.foo', { type: 'unknown/garbage' });
|
||||||
|
dataTransfer.items.add(file);
|
||||||
|
return dataTransfer;
|
||||||
|
}, fileData);
|
||||||
|
|
||||||
|
await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: dropTransfer });
|
||||||
|
|
||||||
|
// should have gotten a notification from OpenMCT that we couldn't add it
|
||||||
|
await expect(page.getByText('Unknown object(s) dropped and cannot embed')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Get an error notification when dropping big files onto notebook entry', async ({
|
||||||
|
page
|
||||||
|
}) => {
|
||||||
|
const garbageSize = 15 * 1024 * 1024; // 15 megabytes
|
||||||
|
|
||||||
|
await page.addScriptTag({
|
||||||
|
// make the garbage client side
|
||||||
|
content: `window.bigGarbageData = new Uint8Array(${garbageSize})`
|
||||||
|
});
|
||||||
|
|
||||||
|
const bigDropTransfer = await page.evaluateHandle(() => {
|
||||||
|
const dataTransfer = new DataTransfer();
|
||||||
|
const file = new File([window.bigGarbageData], 'bigBoy.png', { type: 'image/png' });
|
||||||
|
dataTransfer.items.add(file);
|
||||||
|
return dataTransfer;
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.dispatchEvent('.c-notebook__drag-area', 'drop', { dataTransfer: bigDropTransfer });
|
||||||
|
|
||||||
|
// should have gotten a notification from OpenMCT that we couldn't add it as it's too big
|
||||||
|
await expect(page.getByText('unable to embed')).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -150,7 +150,6 @@ test.describe('Tagging in Notebooks @addInit', () => {
|
|||||||
await createNotebookEntryAndTags(page);
|
await createNotebookEntryAndTags(page);
|
||||||
|
|
||||||
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
|
await page.locator('text=To start a new entry, click here or drag and drop any object').click();
|
||||||
await page.getByLabel('Notebook Entry Display').last().click();
|
|
||||||
await page.getByLabel('Notebook Entry Input').fill(`An entry without tags`);
|
await page.getByLabel('Notebook Entry Input').fill(`An entry without tags`);
|
||||||
await page.locator('.c-ne__save-button > button').click();
|
await page.locator('.c-ne__save-button > button').click();
|
||||||
|
|
||||||
|
@ -131,7 +131,6 @@ test.describe('Performance tests', () => {
|
|||||||
await page.evaluate(() => window.performance.mark('new-notebook-entry-created'));
|
await page.evaluate(() => window.performance.mark('new-notebook-entry-created'));
|
||||||
|
|
||||||
// Enter Notebook Entry text
|
// Enter Notebook Entry text
|
||||||
await page.getByLabel('Notebook Entry').last().click();
|
|
||||||
await page.getByLabel('Notebook Entry Input').last().fill('New Entry');
|
await page.getByLabel('Notebook Entry Input').last().fill('New Entry');
|
||||||
await page.locator('.c-ne__save-button').click();
|
await page.locator('.c-ne__save-button').click();
|
||||||
await page.evaluate(() => window.performance.mark('new-notebook-entry-filled'));
|
await page.evaluate(() => window.performance.mark('new-notebook-entry-filled'));
|
||||||
|
@ -625,21 +625,35 @@ export default {
|
|||||||
dropEvent.preventDefault();
|
dropEvent.preventDefault();
|
||||||
dropEvent.stopImmediatePropagation();
|
dropEvent.stopImmediatePropagation();
|
||||||
|
|
||||||
const localImageDropped = dropEvent.dataTransfer.files?.[0]?.type.includes('image');
|
const dataTransferFiles = Array.from(dropEvent.dataTransfer.files);
|
||||||
const imageUrl = dropEvent.dataTransfer.getData('URL');
|
const localImageDropped = dataTransferFiles.some((file) => file.type.includes('image'));
|
||||||
const snapshotId = dropEvent.dataTransfer.getData('openmct/snapshot/id');
|
const snapshotId = dropEvent.dataTransfer.getData('openmct/snapshot/id');
|
||||||
|
const domainObjectData = dropEvent.dataTransfer.getData('openmct/domain-object-path');
|
||||||
|
const imageUrl = dropEvent.dataTransfer.getData('URL');
|
||||||
if (localImageDropped) {
|
if (localImageDropped) {
|
||||||
// local image dropped from disk (file)
|
// local image(s) dropped from disk (file)
|
||||||
const imageData = dropEvent.dataTransfer.files[0];
|
const embeds = [];
|
||||||
const imageEmbed = await createNewImageEmbed(imageData, this.openmct, imageData?.name);
|
await Promise.all(
|
||||||
this.newEntry(imageEmbed);
|
dataTransferFiles.map(async (file) => {
|
||||||
|
if (file.type.includes('image')) {
|
||||||
|
const imageData = file;
|
||||||
|
const imageEmbed = await createNewImageEmbed(
|
||||||
|
imageData,
|
||||||
|
this.openmct,
|
||||||
|
imageData?.name
|
||||||
|
);
|
||||||
|
embeds.push(imageEmbed);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.newEntry(embeds);
|
||||||
} else if (imageUrl) {
|
} else if (imageUrl) {
|
||||||
// remote image dropped (URL)
|
// remote image dropped (URL)
|
||||||
try {
|
try {
|
||||||
const response = await fetch(imageUrl);
|
const response = await fetch(imageUrl);
|
||||||
const imageData = await response.blob();
|
const imageData = await response.blob();
|
||||||
const imageEmbed = await createNewImageEmbed(imageData, this.openmct);
|
const imageEmbed = await createNewImageEmbed(imageData, this.openmct);
|
||||||
this.newEntry(imageEmbed);
|
this.newEntry([imageEmbed]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.openmct.notifications.alert(`Unable to add image: ${error.message} `);
|
this.openmct.notifications.alert(`Unable to add image: ${error.message} `);
|
||||||
console.error(`Problem embedding remote image`, error);
|
console.error(`Problem embedding remote image`, error);
|
||||||
@ -647,7 +661,7 @@ export default {
|
|||||||
} else if (snapshotId.length) {
|
} else if (snapshotId.length) {
|
||||||
// snapshot object
|
// snapshot object
|
||||||
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
||||||
this.newEntry(snapshot.embedObject);
|
this.newEntry([snapshot.embedObject]);
|
||||||
this.snapshotContainer.removeSnapshot(snapshotId);
|
this.snapshotContainer.removeSnapshot(snapshotId);
|
||||||
|
|
||||||
const namespace = this.domainObject.identifier.namespace;
|
const namespace = this.domainObject.identifier.namespace;
|
||||||
@ -656,10 +670,9 @@ export default {
|
|||||||
namespace
|
namespace
|
||||||
);
|
);
|
||||||
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
|
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
|
||||||
} else {
|
} else if (domainObjectData) {
|
||||||
// plain domain object
|
// plain domain object
|
||||||
const data = dropEvent.dataTransfer.getData('openmct/domain-object-path');
|
const objectPath = JSON.parse(domainObjectData);
|
||||||
const objectPath = JSON.parse(data);
|
|
||||||
const bounds = this.openmct.time.bounds();
|
const bounds = this.openmct.time.bounds();
|
||||||
const snapshotMeta = {
|
const snapshotMeta = {
|
||||||
bounds,
|
bounds,
|
||||||
@ -668,8 +681,15 @@ export default {
|
|||||||
openmct: this.openmct
|
openmct: this.openmct
|
||||||
};
|
};
|
||||||
const embed = await createNewEmbed(snapshotMeta);
|
const embed = await createNewEmbed(snapshotMeta);
|
||||||
|
this.newEntry([embed]);
|
||||||
this.newEntry(embed);
|
} else {
|
||||||
|
this.openmct.notifications.error(
|
||||||
|
`Unknown object(s) dropped and cannot embed. Try again with an image or domain object.`
|
||||||
|
);
|
||||||
|
console.warn(
|
||||||
|
`Unknown object(s) dropped and cannot embed. Try again with an image or domain object.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
focusOnEntryId() {
|
focusOnEntryId() {
|
||||||
@ -838,12 +858,12 @@ export default {
|
|||||||
getSelectedSectionId() {
|
getSelectedSectionId() {
|
||||||
return this.selectedSection?.id;
|
return this.selectedSection?.id;
|
||||||
},
|
},
|
||||||
async newEntry(embed, event) {
|
async newEntry(embeds, event) {
|
||||||
this.startTransaction();
|
this.startTransaction();
|
||||||
this.resetSearch();
|
this.resetSearch();
|
||||||
const notebookStorage = this.createNotebookStorageObject();
|
const notebookStorage = this.createNotebookStorageObject();
|
||||||
this.updateDefaultNotebook(notebookStorage);
|
this.updateDefaultNotebook(notebookStorage);
|
||||||
const id = await addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embed);
|
const id = await addNotebookEntry(this.openmct, this.domainObject, notebookStorage, embeds);
|
||||||
|
|
||||||
const element = this.$refs.notebookEntries.querySelector(`#${id}`);
|
const element = this.$refs.notebookEntries.querySelector(`#${id}`);
|
||||||
const entryAnnotations = this.notebookAnnotations[id] ?? {};
|
const entryAnnotations = this.notebookAnnotations[id] ?? {};
|
||||||
@ -861,6 +881,11 @@ export default {
|
|||||||
this.filterAndSortEntries();
|
this.filterAndSortEntries();
|
||||||
this.focusEntryId = id;
|
this.focusEntryId = id;
|
||||||
this.selectedEntryId = id;
|
this.selectedEntryId = id;
|
||||||
|
|
||||||
|
// put entry into edit mode
|
||||||
|
this.$nextTick(() => {
|
||||||
|
element.dispatchEvent(new Event('click'));
|
||||||
|
});
|
||||||
},
|
},
|
||||||
orientationChange() {
|
orientationChange() {
|
||||||
this.formatSidebar();
|
this.formatSidebar();
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
@drop.capture="cancelEditMode"
|
@drop.capture="cancelEditMode"
|
||||||
@drop.prevent="dropOnEntry"
|
@drop.prevent="dropOnEntry"
|
||||||
@click="selectAndEmitEntry($event, entry)"
|
@click="selectAndEmitEntry($event, entry)"
|
||||||
|
@paste="addImageFromPaste"
|
||||||
>
|
>
|
||||||
<div class="c-ne__time-and-content">
|
<div class="c-ne__time-and-content">
|
||||||
<div class="c-ne__time-and-creator-and-delete">
|
<div class="c-ne__time-and-creator-and-delete">
|
||||||
@ -375,10 +376,40 @@ export default {
|
|||||||
openmct: this.openmct
|
openmct: this.openmct
|
||||||
};
|
};
|
||||||
const newEmbed = await createNewEmbed(snapshotMeta);
|
const newEmbed = await createNewEmbed(snapshotMeta);
|
||||||
|
if (!this.entry.embeds) {
|
||||||
|
this.entry.embeds = [];
|
||||||
|
}
|
||||||
this.entry.embeds.push(newEmbed);
|
this.entry.embeds.push(newEmbed);
|
||||||
|
|
||||||
this.manageEmbedLayout();
|
this.manageEmbedLayout();
|
||||||
},
|
},
|
||||||
|
async addImageFromPaste(event) {
|
||||||
|
const clipboardItems = Array.from(
|
||||||
|
(event.clipboardData || event.originalEvent.clipboardData).items
|
||||||
|
);
|
||||||
|
const hasImage = clipboardItems.some(
|
||||||
|
(clipboardItem) => clipboardItem.type.includes('image') && clipboardItem.kind === 'file'
|
||||||
|
);
|
||||||
|
// If the clipboard contained an image, prevent the paste event from reaching the textarea.
|
||||||
|
if (hasImage) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
await Promise.all(
|
||||||
|
Array.from(clipboardItems).map(async (clipboardItem) => {
|
||||||
|
const isImage = clipboardItem.type.includes('image') && clipboardItem.kind === 'file';
|
||||||
|
if (isImage) {
|
||||||
|
const imageFile = clipboardItem.getAsFile();
|
||||||
|
const imageEmbed = await createNewImageEmbed(imageFile, this.openmct, imageFile?.name);
|
||||||
|
if (!this.entry.embeds) {
|
||||||
|
this.entry.embeds = [];
|
||||||
|
}
|
||||||
|
this.entry.embeds.push(imageEmbed);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.manageEmbedLayout();
|
||||||
|
this.timestampAndUpdate();
|
||||||
|
},
|
||||||
convertMarkDownToHtml(text) {
|
convertMarkDownToHtml(text) {
|
||||||
let markDownHtml = this.marked.parse(text, {
|
let markDownHtml = this.marked.parse(text, {
|
||||||
breaks: true,
|
breaks: true,
|
||||||
@ -450,15 +481,30 @@ export default {
|
|||||||
},
|
},
|
||||||
async dropOnEntry(dropEvent) {
|
async dropOnEntry(dropEvent) {
|
||||||
dropEvent.stopImmediatePropagation();
|
dropEvent.stopImmediatePropagation();
|
||||||
|
const dataTransferFiles = Array.from(dropEvent.dataTransfer.files);
|
||||||
|
|
||||||
const localImageDropped = dropEvent.dataTransfer.files?.[0]?.type.includes('image');
|
const localImageDropped = dataTransferFiles.some((file) => file.type.includes('image'));
|
||||||
const snapshotId = dropEvent.dataTransfer.getData('openmct/snapshot/id');
|
const snapshotId = dropEvent.dataTransfer.getData('openmct/snapshot/id');
|
||||||
|
const domainObjectData = dropEvent.dataTransfer.getData('openmct/domain-object-path');
|
||||||
const imageUrl = dropEvent.dataTransfer.getData('URL');
|
const imageUrl = dropEvent.dataTransfer.getData('URL');
|
||||||
if (localImageDropped) {
|
if (localImageDropped) {
|
||||||
// local image dropped from disk (file)
|
// local image(s) dropped from disk (file)
|
||||||
const imageData = dropEvent.dataTransfer.files[0];
|
await Promise.all(
|
||||||
const imageEmbed = await createNewImageEmbed(imageData, this.openmct, imageData?.name);
|
dataTransferFiles.map(async (file) => {
|
||||||
|
if (file.type.includes('image')) {
|
||||||
|
const imageData = file;
|
||||||
|
const imageEmbed = await createNewImageEmbed(
|
||||||
|
imageData,
|
||||||
|
this.openmct,
|
||||||
|
imageData?.name
|
||||||
|
);
|
||||||
|
if (!this.entry.embeds) {
|
||||||
|
this.entry.embeds = [];
|
||||||
|
}
|
||||||
this.entry.embeds.push(imageEmbed);
|
this.entry.embeds.push(imageEmbed);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
this.manageEmbedLayout();
|
this.manageEmbedLayout();
|
||||||
} else if (imageUrl) {
|
} else if (imageUrl) {
|
||||||
try {
|
try {
|
||||||
@ -466,15 +512,21 @@ export default {
|
|||||||
const response = await fetch(imageUrl);
|
const response = await fetch(imageUrl);
|
||||||
const imageData = await response.blob();
|
const imageData = await response.blob();
|
||||||
const imageEmbed = await createNewImageEmbed(imageData, this.openmct);
|
const imageEmbed = await createNewImageEmbed(imageData, this.openmct);
|
||||||
|
if (!this.entry.embeds) {
|
||||||
|
this.entry.embeds = [];
|
||||||
|
}
|
||||||
this.entry.embeds.push(imageEmbed);
|
this.entry.embeds.push(imageEmbed);
|
||||||
this.manageEmbedLayout();
|
this.manageEmbedLayout();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.openmct.notifications.alert(`Unable to add image: ${error.message} `);
|
this.openmct.notifications.error(`Unable to add image: ${error.message} `);
|
||||||
console.error(`Problem embedding remote image`, error);
|
console.error(`Problem embedding remote image`, error);
|
||||||
}
|
}
|
||||||
} else if (snapshotId.length) {
|
} else if (snapshotId.length) {
|
||||||
// snapshot object
|
// snapshot object
|
||||||
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
const snapshot = this.snapshotContainer.getSnapshot(snapshotId);
|
||||||
|
if (!this.entry.embeds) {
|
||||||
|
this.entry.embeds = [];
|
||||||
|
}
|
||||||
this.entry.embeds.push(snapshot.embedObject);
|
this.entry.embeds.push(snapshot.embedObject);
|
||||||
this.snapshotContainer.removeSnapshot(snapshotId);
|
this.snapshotContainer.removeSnapshot(snapshotId);
|
||||||
|
|
||||||
@ -484,11 +536,18 @@ export default {
|
|||||||
namespace
|
namespace
|
||||||
);
|
);
|
||||||
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
|
saveNotebookImageDomainObject(this.openmct, notebookImageDomainObject);
|
||||||
} else {
|
} else if (domainObjectData) {
|
||||||
// plain domain object
|
// plain domain object
|
||||||
const data = dropEvent.dataTransfer.getData('openmct/domain-object-path');
|
const objectPath = JSON.parse(domainObjectData);
|
||||||
const objectPath = JSON.parse(data);
|
|
||||||
await this.addNewEmbed(objectPath);
|
await this.addNewEmbed(objectPath);
|
||||||
|
} else {
|
||||||
|
this.openmct.notifications.error(
|
||||||
|
`Unknown object(s) dropped and cannot embed. Try again with an image or domain object.`
|
||||||
|
);
|
||||||
|
console.warn(
|
||||||
|
`Unknown object(s) dropped and cannot embed. Try again with an image or domain object.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timestampAndUpdate();
|
this.timestampAndUpdate();
|
||||||
@ -574,7 +633,9 @@ export default {
|
|||||||
this.editMode = false;
|
this.editMode = false;
|
||||||
const rawEntryValue = $event.target.value;
|
const rawEntryValue = $event.target.value;
|
||||||
const sanitizeInput = sanitizeHtml(rawEntryValue, { allowedAttributes: [], allowedTags: [] });
|
const sanitizeInput = sanitizeHtml(rawEntryValue, { allowedAttributes: [], allowedTags: [] });
|
||||||
this.entry.text = sanitizeInput;
|
// change > back to > for markdown to do blockquotes
|
||||||
|
const restoredQuoteBrackets = sanitizeInput.replace(/>/g, '>');
|
||||||
|
this.entry.text = restoredQuoteBrackets;
|
||||||
this.timestampAndUpdate();
|
this.timestampAndUpdate();
|
||||||
},
|
},
|
||||||
selectAndEmitEntry(event, entry) {
|
selectAndEmitEntry(event, entry) {
|
||||||
|
@ -125,6 +125,7 @@ export function createNewImageEmbed(image, openmct, imageName = '') {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onloadend = async () => {
|
reader.onloadend = async () => {
|
||||||
|
try {
|
||||||
const base64Data = reader.result;
|
const base64Data = reader.result;
|
||||||
const blobUrl = URL.createObjectURL(image);
|
const blobUrl = URL.createObjectURL(image);
|
||||||
const imageDomainObject = createNotebookImageDomainObject(base64Data);
|
const imageDomainObject = createNotebookImageDomainObject(base64Data);
|
||||||
@ -149,6 +150,10 @@ export function createNewImageEmbed(image, openmct, imageName = '') {
|
|||||||
|
|
||||||
const createdEmbed = await createNewEmbed(embedMetaData, snapshot);
|
const createdEmbed = await createNewEmbed(embedMetaData, snapshot);
|
||||||
resolve(createdEmbed);
|
resolve(createdEmbed);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`${error.message} - unable to embed image ${imageName}`, error);
|
||||||
|
openmct.notifications.error(`${error.message} -- unable to embed image ${imageName}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.readAsDataURL(image);
|
reader.readAsDataURL(image);
|
||||||
@ -202,7 +207,7 @@ export async function addNotebookEntry(
|
|||||||
openmct,
|
openmct,
|
||||||
domainObject,
|
domainObject,
|
||||||
notebookStorage,
|
notebookStorage,
|
||||||
embed = null,
|
passedEmbeds = [],
|
||||||
entryText = ''
|
entryText = ''
|
||||||
) {
|
) {
|
||||||
if (!openmct || !domainObject || !notebookStorage) {
|
if (!openmct || !domainObject || !notebookStorage) {
|
||||||
@ -212,7 +217,9 @@ export async function addNotebookEntry(
|
|||||||
const date = openmct.time.now();
|
const date = openmct.time.now();
|
||||||
const configuration = domainObject.configuration;
|
const configuration = domainObject.configuration;
|
||||||
const entries = configuration.entries || {};
|
const entries = configuration.entries || {};
|
||||||
const embeds = embed ? [embed] : [];
|
// if embeds isn't an array, make it one
|
||||||
|
const embedsNormalized =
|
||||||
|
passedEmbeds && !Array.isArray(passedEmbeds) ? [passedEmbeds] : passedEmbeds;
|
||||||
|
|
||||||
const id = `entry-${uuid()}`;
|
const id = `entry-${uuid()}`;
|
||||||
const [createdBy, createdByRole] = await Promise.all([
|
const [createdBy, createdByRole] = await Promise.all([
|
||||||
@ -225,7 +232,7 @@ export async function addNotebookEntry(
|
|||||||
createdBy,
|
createdBy,
|
||||||
createdByRole,
|
createdByRole,
|
||||||
text: entryText,
|
text: entryText,
|
||||||
embeds
|
embeds: embedsNormalized
|
||||||
};
|
};
|
||||||
|
|
||||||
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
|
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
|
||||||
|
@ -54,13 +54,23 @@ export async function saveNotebookImageDomainObject(openmct, object) {
|
|||||||
await openmct.objects.save(object);
|
await openmct.objects.save(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateNotebookImageDomainObject(openmct, identifier, fullSizeImage) {
|
export async function updateNotebookImageDomainObject(openmct, identifier, fullSizeImage) {
|
||||||
openmct.objects.get(identifier).then((domainObject) => {
|
const domainObject = await openmct.objects.get(identifier);
|
||||||
const configuration = domainObject.configuration;
|
const configuration = domainObject.configuration;
|
||||||
configuration.fullSizeImageURL = fullSizeImage.src;
|
configuration.fullSizeImageURL = fullSizeImage.src;
|
||||||
|
try {
|
||||||
|
// making a transactions as we can't catch errors on mutations
|
||||||
|
if (!openmct.objects.isTransactionActive()) {
|
||||||
|
openmct.objects.startTransaction();
|
||||||
|
}
|
||||||
openmct.objects.mutate(domainObject, 'configuration', configuration);
|
openmct.objects.mutate(domainObject, 'configuration', configuration);
|
||||||
});
|
const transaction = openmct.objects.getActiveTransaction();
|
||||||
|
await transaction.commit();
|
||||||
|
openmct.objects.endTransaction();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`${error.message} -- unable to save image`, error);
|
||||||
|
openmct.notifications.error(`${error.message} -- unable to save image`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateNamespaceOfDomainObject(object, namespace) {
|
export function updateNamespaceOfDomainObject(object, namespace) {
|
||||||
|
@ -60,21 +60,21 @@ While we highly recommend using the CouchDB docker-compose installation, it is s
|
|||||||
1. Install CouchDB using: `brew install couchdb`.
|
1. Install CouchDB using: `brew install couchdb`.
|
||||||
2. Edit `/usr/local/etc/local.ini` and add the following settings:
|
2. Edit `/usr/local/etc/local.ini` and add the following settings:
|
||||||
|
|
||||||
```txt
|
```ini
|
||||||
[admins]
|
[admins]
|
||||||
admin = youradminpassword
|
admin = youradminpassword
|
||||||
```
|
```
|
||||||
|
|
||||||
And set the server up for single node:
|
And set the server up for single node:
|
||||||
|
|
||||||
```txt
|
```ini
|
||||||
[couchdb]
|
[couchdb]
|
||||||
single_node=true
|
single_node=true
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable CORS
|
Enable CORS
|
||||||
|
|
||||||
```txt
|
```ini
|
||||||
[chttpd]
|
[chttpd]
|
||||||
enable_cors = true
|
enable_cors = true
|
||||||
[cors]
|
[cors]
|
||||||
@ -119,6 +119,18 @@ sh ./setup-couchdb.sh
|
|||||||
5. Navigate to <http://127.0.0.1:5984/_utils/#/database/openmct/permissions>
|
5. Navigate to <http://127.0.0.1:5984/_utils/#/database/openmct/permissions>
|
||||||
6. Remove permission restrictions in CouchDB from Open MCT by deleting `_admin` roles for both `Admin` and `Member`.
|
6. Remove permission restrictions in CouchDB from Open MCT by deleting `_admin` roles for both `Admin` and `Member`.
|
||||||
|
|
||||||
|
## Document Sizes
|
||||||
|
CouchDB has size limits on both its internal documents, and its httpd interface. If dealing with larger documents in Open MCT (e.g., users adding images to notebook entries), you may to increase this limit. To do this, add the following to the two sections:
|
||||||
|
```ini
|
||||||
|
[couchdb]
|
||||||
|
max_document_size = 4294967296 ; approx 4 GB
|
||||||
|
|
||||||
|
[chttpd]
|
||||||
|
max_http_request_size = 4294967296 ; approx 4 GB
|
||||||
|
```
|
||||||
|
|
||||||
|
If not present, add them under proper sections. The values are in bytes, and can be adjusted to whatever is appropriate for your use case.
|
||||||
|
|
||||||
# Configuring Open MCT to use CouchDB
|
# Configuring Open MCT to use CouchDB
|
||||||
|
|
||||||
## Configuration script
|
## Configuration script
|
||||||
|
@ -1399,6 +1399,9 @@ export default {
|
|||||||
Object.keys(pointsInBoxBySeries).forEach((seriesKeyString) => {
|
Object.keys(pointsInBoxBySeries).forEach((seriesKeyString) => {
|
||||||
const pointsInBox = pointsInBoxBySeries[seriesKeyString];
|
const pointsInBox = pointsInBoxBySeries[seriesKeyString];
|
||||||
if (pointsInBox && pointsInBox.length) {
|
if (pointsInBox && pointsInBox.length) {
|
||||||
|
if (!annotationsBySeries[seriesKeyString]) {
|
||||||
|
annotationsBySeries[seriesKeyString] = [];
|
||||||
|
}
|
||||||
annotationsBySeries[seriesKeyString].push(...pointsInBox);
|
annotationsBySeries[seriesKeyString].push(...pointsInBox);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user