mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
detect existing attachment in target note
This commit is contained in:
parent
df17840dbc
commit
235b779dec
@ -62,7 +62,7 @@ class NoteContext extends Component {
|
||||
|
||||
this.notePath = resolvedNotePath;
|
||||
this.viewScope = opts.viewScope;
|
||||
({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(resolvedNotePath));
|
||||
({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromUrl(resolvedNotePath));
|
||||
|
||||
this.saveToRecentNotes(resolvedNotePath);
|
||||
|
||||
|
@ -41,7 +41,7 @@ export default class RootCommandExecutor extends Component {
|
||||
}
|
||||
|
||||
async searchInSubtreeCommand({notePath}) {
|
||||
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
||||
const noteId = treeService.getNoteIdFromUrl(notePath);
|
||||
|
||||
this.searchNotesCommand({ancestorNoteId: noteId});
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ export default class TabManager extends Component {
|
||||
// preload all notes at once
|
||||
await froca.getNotes([
|
||||
...noteContextsToOpen.flatMap(tab =>
|
||||
[ treeService.getNoteIdFromNotePath(tab.notePath), tab.hoistedNoteId]
|
||||
[ treeService.getNoteIdFromUrl(tab.notePath), tab.hoistedNoteId]
|
||||
),
|
||||
], true);
|
||||
|
||||
@ -66,7 +66,7 @@ export default class TabManager extends Component {
|
||||
return !!openTab.active;
|
||||
}
|
||||
|
||||
const noteId = treeService.getNoteIdFromNotePath(openTab.notePath);
|
||||
const noteId = treeService.getNoteIdFromUrl(openTab.notePath);
|
||||
if (!(noteId in froca.notes)) {
|
||||
// note doesn't exist so don't try to open tab for it
|
||||
return false;
|
||||
|
@ -94,7 +94,7 @@ async function renderText(note, options, $renderedContent) {
|
||||
renderMathInElement($renderedContent[0], {trust: true});
|
||||
}
|
||||
|
||||
const getNoteIdFromLink = el => treeService.getNoteIdFromNotePath($(el).attr('href'));
|
||||
const getNoteIdFromLink = el => treeService.getNoteIdFromUrl($(el).attr('href'));
|
||||
const referenceLinks = $renderedContent.find("a.reference-link");
|
||||
const noteIdsToPrefetch = referenceLinks.map(el => getNoteIdFromLink(el));
|
||||
await froca.getNotes(noteIdsToPrefetch);
|
||||
|
@ -360,6 +360,8 @@ class Froca {
|
||||
opts.preview = !!opts.preview;
|
||||
const key = `${entityType}-${entityId}-${opts.preview}`;
|
||||
|
||||
console.log(key);
|
||||
|
||||
if (!this.blobPromises[key]) {
|
||||
this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob?preview=${opts.preview}`)
|
||||
.then(row => new FBlob(row))
|
||||
|
@ -21,7 +21,13 @@ async function processEntityChanges(entityChanges) {
|
||||
} else if (ec.entityName === 'note_reordering') {
|
||||
processNoteReordering(loadResults, ec);
|
||||
} else if (ec.entityName === 'blobs') {
|
||||
delete froca.blobPromises[ec.entityId];
|
||||
for (const affectedNoteId of ec.noteIds) {
|
||||
for (const key of Object.keys(froca.blobPromises)) {
|
||||
if (key.includes(affectedNoteId)) {
|
||||
delete froca.blobPromises[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadResults.addNoteContent(ec.noteIds, ec.componentId);
|
||||
} else if (ec.entityName === 'note_revisions') {
|
||||
|
@ -49,7 +49,7 @@ async function checkNoteAccess(notePath, noteContext) {
|
||||
const hoistedNoteId = noteContext.hoistedNoteId;
|
||||
|
||||
if (!resolvedNotePath.includes(hoistedNoteId) && !resolvedNotePath.includes('_hidden')) {
|
||||
const requestedNote = await froca.getNote(treeService.getNoteIdFromNotePath(resolvedNotePath));
|
||||
const requestedNote = await froca.getNote(treeService.getNoteIdFromUrl(resolvedNotePath));
|
||||
const hoistedNote = await froca.getNote(hoistedNoteId);
|
||||
|
||||
if (!hoistedNote.hasAncestor('_hidden')
|
||||
|
@ -43,7 +43,7 @@ async function createLink(notePath, options = {}) {
|
||||
const showNoteIcon = options.showNoteIcon === undefined ? false : options.showNoteIcon;
|
||||
const referenceLink = options.referenceLink === undefined ? false : options.referenceLink;
|
||||
|
||||
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromNotePath(notePath);
|
||||
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||
const viewScope = options.viewScope || {};
|
||||
const viewMode = viewScope.viewMode || 'default';
|
||||
let linkTitle = options.title;
|
||||
@ -174,7 +174,7 @@ function parseNavigationStateFromUrl(url) {
|
||||
|
||||
return {
|
||||
notePath,
|
||||
noteId: treeService.getNoteIdFromNotePath(notePath),
|
||||
noteId: treeService.getNoteIdFromUrl(notePath),
|
||||
ntxId,
|
||||
hoistedNoteId,
|
||||
viewScope
|
||||
|
@ -27,7 +27,7 @@ async function createNote(parentNotePath, options = {}) {
|
||||
[options.title, options.content] = parseSelectedHtml(options.textEditor.getSelectedHtml());
|
||||
}
|
||||
|
||||
const parentNoteId = treeService.getNoteIdFromNotePath(parentNotePath);
|
||||
const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath);
|
||||
|
||||
if (options.type === 'mermaid' && !options.content) {
|
||||
options.content = `graph TD;
|
||||
@ -110,7 +110,7 @@ function parseSelectedHtml(selectedHtml) {
|
||||
}
|
||||
|
||||
async function duplicateSubtree(noteId, parentNotePath) {
|
||||
const parentNoteId = treeService.getNoteIdFromNotePath(parentNotePath);
|
||||
const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath);
|
||||
const {note} = await server.post(`notes/${noteId}/duplicate/${parentNoteId}`);
|
||||
|
||||
await ws.waitForMaxKnownEntityChangeId();
|
||||
|
@ -103,7 +103,7 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
|
||||
return effectivePathSegments;
|
||||
}
|
||||
else {
|
||||
const note = await froca.getNote(getNoteIdFromNotePath(notePath));
|
||||
const note = await froca.getNote(getNoteIdFromUrl(notePath));
|
||||
|
||||
const bestNotePath = note.getBestNotePath(hoistedNoteId);
|
||||
|
||||
@ -132,26 +132,30 @@ function getParentProtectedStatus(node) {
|
||||
return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
|
||||
}
|
||||
|
||||
function getNoteIdFromNotePath(notePath) {
|
||||
if (!notePath) {
|
||||
function getNoteIdFromUrl(url) {
|
||||
if (!url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const path = notePath.split("/");
|
||||
const [notePath] = url.split("?");
|
||||
const segments = notePath.split("/");
|
||||
|
||||
const lastSegment = path[path.length - 1];
|
||||
|
||||
// path could have also params suffix
|
||||
return lastSegment.split("?")[0];
|
||||
return segments[segments.length - 1];
|
||||
}
|
||||
|
||||
async function getBranchIdFromNotePath(notePath) {
|
||||
const {noteId, parentNoteId} = getNoteIdAndParentIdFromNotePath(notePath);
|
||||
async function getBranchIdFromUrl(url) {
|
||||
const {noteId, parentNoteId} = getNoteIdAndParentIdFromUrl(url);
|
||||
|
||||
return await froca.getBranchId(parentNoteId, noteId);
|
||||
}
|
||||
|
||||
function getNoteIdAndParentIdFromNotePath(notePath) {
|
||||
function getNoteIdAndParentIdFromUrl(url) {
|
||||
if (!url) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const [notePath] = url.split("?");
|
||||
|
||||
if (notePath === 'root') {
|
||||
return {
|
||||
noteId: 'root',
|
||||
@ -163,15 +167,12 @@ function getNoteIdAndParentIdFromNotePath(notePath) {
|
||||
let noteId = '';
|
||||
|
||||
if (notePath) {
|
||||
const path = notePath.split("/");
|
||||
const segments = notePath.split("/");
|
||||
|
||||
const lastSegment = path[path.length - 1];
|
||||
noteId = segments[segments.length - 1];
|
||||
|
||||
// path could have also params suffix
|
||||
noteId = lastSegment.split("?")[0];
|
||||
|
||||
if (path.length > 1) {
|
||||
parentNoteId = path[path.length - 2];
|
||||
if (segments.length > 1) {
|
||||
parentNoteId = segments[segments.length - 2];
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,9 +289,9 @@ export default {
|
||||
resolveNotePathToSegments,
|
||||
getParentProtectedStatus,
|
||||
getNotePath,
|
||||
getNoteIdFromNotePath,
|
||||
getNoteIdAndParentIdFromNotePath,
|
||||
getBranchIdFromNotePath,
|
||||
getNoteIdFromUrl,
|
||||
getNoteIdAndParentIdFromUrl,
|
||||
getBranchIdFromUrl,
|
||||
getNoteTitle,
|
||||
getNotePathTitle,
|
||||
getNoteTitleWithPathAsSuffix,
|
||||
|
@ -132,7 +132,7 @@ export default class AddLinkDialog extends BasicWidget {
|
||||
|
||||
this.updateTitleSettingsVisibility();
|
||||
|
||||
const noteId = treeService.getNoteIdFromNotePath(suggestion.notePath);
|
||||
const noteId = treeService.getNoteIdFromUrl(suggestion.notePath);
|
||||
|
||||
if (noteId) {
|
||||
setDefaultLinkTitle(noteId);
|
||||
@ -154,7 +154,7 @@ export default class AddLinkDialog extends BasicWidget {
|
||||
this.$linkTitle.val(suggestion.externalLink)
|
||||
}
|
||||
else {
|
||||
const noteId = treeService.getNoteIdFromNotePath(suggestion.notePath);
|
||||
const noteId = treeService.getNoteIdFromUrl(suggestion.notePath);
|
||||
|
||||
if (noteId) {
|
||||
setDefaultLinkTitle(noteId);
|
||||
|
@ -59,7 +59,7 @@ export default class BranchPrefixDialog extends BasicWidget {
|
||||
}
|
||||
|
||||
async refresh(notePath) {
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath);
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||
|
||||
if (!noteId || !parentNoteId) {
|
||||
return;
|
||||
|
@ -110,7 +110,7 @@ export default class CloneToDialog extends BasicWidget {
|
||||
}
|
||||
|
||||
async cloneNotesTo(notePath) {
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath);
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||
const targetBranchId = await froca.getBranchId(parentNoteId, noteId);
|
||||
|
||||
for (const cloneNoteId of this.clonedNoteIds) {
|
||||
|
@ -210,7 +210,7 @@ export default class ExportDialog extends BasicWidget {
|
||||
|
||||
utils.openDialog(this.$widget);
|
||||
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath);
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||
|
||||
this.branchId = await froca.getBranchId(parentNoteId, noteId);
|
||||
this.$noteTitle.text(await treeService.getNoteTitle(noteId));
|
||||
|
@ -92,7 +92,7 @@ export default class IncludeNoteDialog extends BasicWidget {
|
||||
}
|
||||
|
||||
async includeNote(notePath) {
|
||||
const noteId = treeService.getNoteIdFromNotePath(notePath);
|
||||
const noteId = treeService.getNoteIdFromUrl(notePath);
|
||||
const note = await froca.getNote(noteId);
|
||||
|
||||
const boxSize = $("input[name='include-note-box-size']:checked").val();
|
||||
|
@ -59,7 +59,7 @@ export default class MoveToDialog extends BasicWidget {
|
||||
if (notePath) {
|
||||
this.$widget.modal('hide');
|
||||
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(notePath);
|
||||
const {noteId, parentNoteId} = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||
froca.getBranchId(parentNoteId, noteId).then(branchId => this.moveNotesTo(branchId));
|
||||
}
|
||||
else {
|
||||
|
@ -29,7 +29,7 @@ class MobileDetailMenuWidget extends BasicWidget {
|
||||
}
|
||||
else if (command === "delete") {
|
||||
const notePath = appContext.tabManager.getActiveContextNotePath();
|
||||
const branchId = await treeService.getBranchIdFromNotePath(notePath);
|
||||
const branchId = await treeService.getBranchIdFromUrl(notePath);
|
||||
|
||||
if (!branchId) {
|
||||
throw new Error(`Cannot get branchId for notePath '${notePath}'`);
|
||||
|
@ -297,7 +297,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
else if ($attr.attr("data-attribute-type") === "relation") {
|
||||
const selectedPath = $attr.getSelectedNotePath();
|
||||
|
||||
value = selectedPath ? treeService.getNoteIdFromNotePath(selectedPath) : "";
|
||||
value = selectedPath ? treeService.getNoteIdFromUrl(selectedPath) : "";
|
||||
}
|
||||
else {
|
||||
value = $attr.val();
|
||||
|
@ -45,7 +45,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
|
||||
this.$wrapper.empty();
|
||||
this.children = [];
|
||||
|
||||
this.$linksWrapper.append(
|
||||
this.$linksWrapper.empty().append(
|
||||
"Owning note: ",
|
||||
await linkService.createLink(this.noteId),
|
||||
", you can also open the ",
|
||||
|
@ -37,7 +37,7 @@ export default class AttachmentListTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
async doRefresh(note) {
|
||||
this.$linksWrapper.append(
|
||||
this.$linksWrapper.empty().append(
|
||||
$('<div>').append(
|
||||
"Owning note: ",
|
||||
await linkService.createLink(this.noteId),
|
||||
|
@ -366,17 +366,32 @@ function checkImageAttachments(note, content) {
|
||||
const unknownAttachments = becca.getAttachments(unknownAttachmentIds);
|
||||
|
||||
for (const unknownAttachment of unknownAttachments) {
|
||||
// the attachment belongs to a different note (was copy pasted), we need to make a copy for this note.
|
||||
const newAttachment = unknownAttachment.copy();
|
||||
newAttachment.parentId = note.noteId;
|
||||
newAttachment.setContent(unknownAttachment.getContent(), { forceSave: true });
|
||||
// the attachment belongs to a different note (was copy pasted). Attachments can be linked only from the note
|
||||
// which owns it, so either find an existing attachment having the same content or make a copy.
|
||||
let localAttachment = note.getAttachments().find(att => att.role === unknownAttachment.role && att.blobId === unknownAttachment.blobId);
|
||||
|
||||
content = content.replace(`api/attachments/${unknownAttachment.attachmentId}/image`, `api/attachments/${newAttachment.attachmentId}/image`);
|
||||
content = content.replace(`attachmentId=${unknownAttachment.attachmentId}`, `attachmentId=${newAttachment.attachmentId}`);
|
||||
if (localAttachment) {
|
||||
if (localAttachment.utcDateScheduledForErasureSince) {
|
||||
// the attachment is for sure linked now, so reset the scheduled deletion
|
||||
localAttachment.utcDateScheduledForErasureSince = null;
|
||||
localAttachment.save();
|
||||
}
|
||||
|
||||
ws.sendMessageToAllClients({ type: 'toast', message: `Attachment '${newAttachment.title}' has been copied to note '${note.title}'.`});
|
||||
log.info(`Found equivalent attachment '${localAttachment.attachmentId}' of note '${note.noteId}' for the linked foreign attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.parentId}'`);
|
||||
} else {
|
||||
localAttachment = unknownAttachment.copy();
|
||||
localAttachment.parentId = note.noteId;
|
||||
localAttachment.setContent(unknownAttachment.getContent(), {forceSave: true});
|
||||
|
||||
log.info(`Copied attachment '${unknownAttachment.attachmentId}' to new '${newAttachment.attachmentId}'`);
|
||||
ws.sendMessageToAllClients({ type: 'toast', message: `Attachment '${localAttachment.title}' has been copied to note '${note.title}'.`});
|
||||
log.info(`Copied attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.parentId}' to new '${localAttachment.attachmentId}' of note '${note.noteId}'`);
|
||||
}
|
||||
|
||||
// replace image links
|
||||
content = content.replace(`api/attachments/${unknownAttachment.attachmentId}/image`, `api/attachments/${localAttachment.attachmentId}/image`);
|
||||
// replace reference links
|
||||
content = content.replace(new RegExp(`href="[^"]+attachmentId=${unknownAttachment.attachmentId}[^"]*"`, "g"),
|
||||
`href="#root/${localAttachment.parentId}?viewMode=attachments&attachmentId=${localAttachment.attachmentId}"`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -150,7 +150,11 @@ function fillInAdditionalProperties(entityChange) {
|
||||
} else if (entityChange.entityName === 'blobs') {
|
||||
entityChange.noteIds = sql.getColumn("SELECT noteId FROM notes WHERE blobId = ? AND isDeleted = 0", [entityChange.entityId]);
|
||||
} else if (entityChange.entityName === 'attachments') {
|
||||
entityChange.entity = sql.getRow(`SELECT * FROM attachments WHERE attachmentId = ?`, [entityChange.entityId]);
|
||||
entityChange.entity = sql.getRow(`
|
||||
SELECT attachments.*, LENGTH(blobs.content)
|
||||
FROM attachments
|
||||
JOIN blobs ON blobs.blobId = attachments.blobId
|
||||
WHERE attachmentId = ?`, [entityChange.entityId]);
|
||||
}
|
||||
|
||||
if (entityChange.entity instanceof AbstractBeccaEntity) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user