diff --git a/db/migrations/0137__links_to_attributes.sql b/db/migrations/0137__links_to_attributes.sql
new file mode 100644
index 000000000..294debe34
--- /dev/null
+++ b/db/migrations/0137__links_to_attributes.sql
@@ -0,0 +1,10 @@
+UPDATE links SET type = 'internal-link' WHERE type = 'hyper';
+UPDATE links SET type = 'image-link' WHERE type = 'image';
+UPDATE links SET type = 'relation-map-link' WHERE type = 'relation-map';
+
+INSERT INTO attributes (attributeId, noteId, type, name, value, position, utcDateCreated, utcDateModified, isDeleted, hash, isInheritable)
+SELECT linkId, noteId, 'relation', type, targetNoteId, 0, utcDateCreated, utcDateModified, isDeleted, hash, 0 FROM links;
+
+UPDATE sync SET entityName = 'attributes' WHERE entityName = 'links';
+
+DROP TABLE links;
\ No newline at end of file
diff --git a/src/entities/entity_constructor.js b/src/entities/entity_constructor.js
index b7ee4b313..45e0e332a 100644
--- a/src/entities/entity_constructor.js
+++ b/src/entities/entity_constructor.js
@@ -1,6 +1,5 @@
const Note = require('../entities/note');
const NoteRevision = require('../entities/note_revision');
-const Link = require('../entities/link');
const Branch = require('../entities/branch');
const Attribute = require('../entities/attribute');
const RecentNote = require('../entities/recent_note');
@@ -16,7 +15,6 @@ const ENTITY_NAME_TO_ENTITY = {
"recent_notes": RecentNote,
"options": Option,
"api_tokens": ApiToken,
- "links": Link
};
function getEntityFromEntityName(entityName) {
@@ -36,9 +34,6 @@ function createEntityFromRow(row) {
else if (row.noteRevisionId) {
entity = new NoteRevision(row);
}
- else if (row.linkId) {
- entity = new Link(row);
- }
else if (row.branchId && row.notePath) {
entity = new RecentNote(row);
}
diff --git a/src/entities/link.js b/src/entities/link.js
deleted file mode 100644
index b9c832903..000000000
--- a/src/entities/link.js
+++ /dev/null
@@ -1,51 +0,0 @@
-"use strict";
-
-const Entity = require('./entity');
-const repository = require('../services/repository');
-const dateUtils = require('../services/date_utils');
-
-/**
- * This class represents link from one note to another in the form of hyperlink or image reference. Note that
- * this is different concept than attribute/relation.
- *
- * @param {string} linkId
- * @param {string} noteId
- * @param {string} targetNoteId
- * @param {string} type
- * @param {boolean} isDeleted
- * @param {string} utcDateModified
- * @param {string} utcDateCreated
- *
- * @extends Entity
- */
-class Link extends Entity {
- static get entityName() { return "links"; }
- static get primaryKeyName() { return "linkId"; }
- static get hashedProperties() { return ["linkId", "noteId", "targetNoteId", "type", "isDeleted", "utcDateCreated", "utcDateModified"]; }
-
- async getNote() {
- return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
- }
-
- async getTargetNote() {
- return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.targetNoteId]);
- }
-
- beforeSaving() {
- if (!this.isDeleted) {
- this.isDeleted = false;
- }
-
- if (!this.utcDateCreated) {
- this.utcDateCreated = dateUtils.utcNowDateTime();
- }
-
- super.beforeSaving();
-
- if (this.isChanged) {
- this.utcDateModified = dateUtils.utcNowDateTime();
- }
- }
-}
-
-module.exports = Link;
\ No newline at end of file
diff --git a/src/entities/note.js b/src/entities/note.js
index 860888929..39b3cbe9f 100644
--- a/src/entities/note.js
+++ b/src/entities/note.js
@@ -628,10 +628,17 @@ class Note extends Entity {
/**
* Get list of links coming out of this note.
*
- * @returns {Promise}
+ * @deprecated - not intended for general use
+ * @returns {Promise}
*/
async getLinks() {
- return await repository.getEntities("SELECT * FROM links WHERE noteId = ? AND isDeleted = 0", [this.noteId]);
+ return await repository.getEntities(`
+ SELECT *
+ FROM attributes
+ WHERE noteId = ? AND
+ isDeleted = 0 AND
+ type = 'relation' AND
+ name IN ('internal-link', 'image-link', 'relation-map-link')`, [this.noteId]);
}
/**
diff --git a/src/public/javascripts/entities/link.js b/src/public/javascripts/entities/link.js
new file mode 100644
index 000000000..89e7ec380
--- /dev/null
+++ b/src/public/javascripts/entities/link.js
@@ -0,0 +1,33 @@
+class Link {
+ constructor(treeCache, row) {
+ this.treeCache = treeCache;
+ /** @param {string} linkId */
+ this.linkId = row.linkId;
+ /** @param {string} noteId */
+ this.noteId = row.noteId;
+ /** @param {string} type */
+ this.type = row.type;
+ /** @param {string} targetNoteId */
+ this.targetNoteId = row.targetNoteId;
+ /** @param {string} utcDateCreated */
+ this.utcDateCreated = row.utcDateCreated;
+ /** @param {string} utcDateModified */
+ this.utcDateModified = row.utcDateModified;
+ }
+
+ /** @returns {NoteShort} */
+ async getNote() {
+ return await this.treeCache.getNote(this.noteId);
+ }
+
+ /** @returns {NoteShort} */
+ async getTargetNote() {
+ return await this.treeCache.getNote(this.targetNoteId);
+ }
+
+ get toString() {
+ return `Link(linkId=${this.linkId}, type=${this.type}, note=${this.noteId}, targetNoteId=${this.targetNoteId})`;
+ }
+}
+
+export default Link;
\ No newline at end of file
diff --git a/src/public/javascripts/entities/note_short.js b/src/public/javascripts/entities/note_short.js
index 2c983bd67..dccf14850 100644
--- a/src/public/javascripts/entities/note_short.js
+++ b/src/public/javascripts/entities/note_short.js
@@ -1,4 +1,6 @@
import server from '../services/server.js';
+import Attribute from './attribute.js';
+import Link from './link.js';
const LABEL = 'label';
const LABEL_DEFINITION = 'label-definition';
@@ -84,7 +86,8 @@ class NoteShort {
*/
async getAttributes(name) {
if (!this.attributeCache) {
- this.attributeCache = await server.get('notes/' + this.noteId + '/attributes');
+ this.attributeCache = (await server.get('notes/' + this.noteId + '/attributes'))
+ .map(attrRow => new Attribute(this.treeCache, attrRow));
}
if (name) {
@@ -227,6 +230,14 @@ class NoteShort {
this.attributeCache = null;
}
+ /**
+ * @return {Promise}
+ */
+ async getLinks() {
+ return (await server.get('notes/' + this.noteId + '/links'))
+ .map(linkRow => new Link(this.treeCache, linkRow));
+ }
+
get toString() {
return `Note(noteId=${this.noteId}, title=${this.title})`;
}
diff --git a/src/public/javascripts/widgets/link_map.js b/src/public/javascripts/widgets/link_map.js
index 625270a8c..2bef87030 100644
--- a/src/public/javascripts/widgets/link_map.js
+++ b/src/public/javascripts/widgets/link_map.js
@@ -58,9 +58,9 @@ class LinkMapWidget extends StandardWidget {
const linkTypes = [ "hyper", "image", "relation", "relation-map" ];
const maxNotes = 50;
- const noteId = this.ctx.note.noteId;
+ const currentNoteId = this.ctx.note.noteId;
- const links = await server.post(`notes/${noteId}/link-map`, {
+ const links = await server.post(`notes/${currentNoteId}/link-map`, {
linkTypes,
maxNotes,
maxDepth: 1
@@ -69,7 +69,7 @@ class LinkMapWidget extends StandardWidget {
const noteIds = new Set(links.map(l => l.noteId).concat(links.map(l => l.targetNoteId)));
if (noteIds.size === 0) {
- noteIds.add(noteId);
+ noteIds.add(currentNoteId);
}
// preload all notes
@@ -104,7 +104,7 @@ class LinkMapWidget extends StandardWidget {
$noteBox.append($("").addClass("title").append($link));
});
- if (noteId === noteId) {
+ if (noteId === currentNoteId) {
$noteBox.addClass("link-map-active-note");
}
diff --git a/src/public/javascripts/widgets/what_links_here.js b/src/public/javascripts/widgets/what_links_here.js
new file mode 100644
index 000000000..322718013
--- /dev/null
+++ b/src/public/javascripts/widgets/what_links_here.js
@@ -0,0 +1,31 @@
+import StandardWidget from "./standard_widget.js";
+
+class WhatLinksHereWidget extends StandardWidget {
+ getWidgetTitle() { return "What links here"; }
+
+ async doRenderBody() {
+
+
+ const $noteId = this.$body.find(".note-info-note-id");
+ const $dateCreated = this.$body.find(".note-info-date-created");
+ const $dateModified = this.$body.find(".note-info-date-modified");
+ const $type = this.$body.find(".note-info-type");
+ const $mime = this.$body.find(".note-info-mime");
+
+ const note = this.ctx.note;
+
+ $noteId.text(note.noteId);
+ $dateCreated.text(note.dateCreated);
+ $dateModified.text(note.dateModified);
+ $type.text(note.type);
+ $mime.text(note.mime);
+ }
+
+ syncDataReceived(syncData) {
+ if (syncData.find(sd => sd.entityName === 'notes' && sd.entityId === this.ctx.note.noteId)) {
+ this.doRenderBody();
+ }
+ }
+}
+
+export default WhatLinksHereWidget;
\ No newline at end of file
diff --git a/src/public/stylesheets/link_map.css b/src/public/stylesheets/link_map.css
index 8e917bc27..86a8cafb1 100644
--- a/src/public/stylesheets/link_map.css
+++ b/src/public/stylesheets/link_map.css
@@ -39,5 +39,4 @@
.link-map-active-note {
background-color: var(--more-accented-background-color) !important;
- border-width: 3px !important;
}
\ No newline at end of file
diff --git a/src/routes/api/clipper.js b/src/routes/api/clipper.js
index 07122d3c4..4a3904c2c 100644
--- a/src/routes/api/clipper.js
+++ b/src/routes/api/clipper.js
@@ -9,7 +9,7 @@ const messagingService = require('../../services/messaging');
const log = require('../../services/log');
const utils = require('../../services/utils');
const path = require('path');
-const Link = require('../../entities/link');
+const Attribute = require('../../entities/attribute');
async function findClippingNote(todayNote, pageUrl) {
const notes = await todayNote.getDescendantNotesWithLabel('pageUrl', pageUrl);
@@ -84,10 +84,11 @@ async function addImagesToNote(images, note, content) {
const {note: imageNote, url} = await imageService.saveImage(buffer, filename, note.noteId, true);
- await new Link({
+ await new Attribute({
noteId: note.noteId,
- targetNoteId: imageNote.noteId,
- type: 'image'
+ type: 'relation',
+ value: imageNote.noteId,
+ name: 'image-link'
}).save();
console.log(`Replacing ${imageId} with ${url}`);
diff --git a/src/routes/api/link_map.js b/src/routes/api/link_map.js
index abfedb4e8..4ea882f23 100644
--- a/src/routes/api/link_map.js
+++ b/src/routes/api/link_map.js
@@ -2,38 +2,34 @@
const sql = require('../../services/sql');
-async function getLinks(noteIds, linkTypes) {
+async function getRelations(noteIds, relationNames) {
return (await sql.getManyRows(`
- SELECT noteId, targetNoteId, type
- FROM links
- WHERE (noteId IN (???) OR targetNoteId IN (???))
- AND isDeleted = 0
- UNION
- SELECT noteId, value, 'relation'
+ SELECT noteId, name, value AS targetNoteId
FROM attributes
WHERE (noteId IN (???) OR value IN (???))
AND type = 'relation'
AND isDeleted = 0
- `, Array.from(noteIds))).filter(l => linkTypes.includes(l.type));
+ `, Array.from(noteIds))).filter(l => relationNames.includes(l.name));
}
async function getLinkMap(req) {
const {noteId} = req.params;
- const {linkTypes, maxNotes, maxDepth} = req.body;
+ const {relationNames, maxNotes, maxDepth} = req.body;
let noteIds = new Set([noteId]);
- let links = [];
+ let relations;
let depth = 0;
while (true) {
- links = await getLinks(noteIds, linkTypes);
+ relations = await getRelations(noteIds, relationNames);
if (depth === maxDepth) {
break;
}
- const newNoteIds = new Set(links.map(l => l.noteId).concat(links.map(l => l.targetNoteId)));
+ const newNoteIds = new Set(relations.map(rel => rel.noteId)
+ .concat(relations.map(rel => rel.targetNoteId)));
if (newNoteIds.size === noteIds.size) {
// no new note discovered, no need to search any further
@@ -51,9 +47,9 @@ async function getLinkMap(req) {
}
// keep only links coming from and targetting some note in the noteIds set
- links = links.filter(l => noteIds.has(l.noteId) && noteIds.has(l.targetNoteId));
+ relations = relations.filter(rel => noteIds.has(rel.noteId) && noteIds.has(rel.targetNoteId));
- return links;
+ return relations;
}
module.exports = {
diff --git a/src/routes/api/links.js b/src/routes/api/links.js
new file mode 100644
index 000000000..04a2bab36
--- /dev/null
+++ b/src/routes/api/links.js
@@ -0,0 +1,28 @@
+"use strict";
+
+const repository = require('../../services/repository');
+
+async function getLinks(req) {
+ const note = await repository.getNote(req.params.noteId);
+
+ if (!note) {
+ return [404, `Note ${req.params.noteId} not found`];
+ }
+
+ return await note.getLinks();
+}
+
+async function getIncomingLinks(req) {
+ const note = await repository.getNote(req.params.noteId);
+
+ if (!note) {
+ return [404, `Note ${req.params.noteId} not found`];
+ }
+
+ note.getTargetRelations()
+}
+
+module.exports = {
+ getLinks,
+ getIncomingLinks
+};
\ No newline at end of file
diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js
index 72b8654db..c1298d2cf 100644
--- a/src/routes/api/notes.js
+++ b/src/routes/api/notes.js
@@ -114,8 +114,7 @@ async function getRelationMap(req) {
noteTitles: {},
relations: [],
// relation name => inverse relation name
- inverseRelations: {},
- links: []
+ inverseRelations: {}
};
if (noteIds.length === 0) {
@@ -145,16 +144,6 @@ async function getRelationMap(req) {
}
}
- resp.links = (await repository.getEntities(`SELECT * FROM links WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds))
- .filter(link => noteIds.includes(link.targetNoteId))
- .map(link => {
- return {
- linkId: link.linkId,
- sourceNoteId: link.noteId,
- targetNoteId: link.targetNoteId
- }
- });
-
return resp;
}
diff --git a/src/routes/routes.js b/src/routes/routes.js
index dce51be9c..8792e5197 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -33,6 +33,7 @@ const filesRoute = require('./api/file_upload');
const searchRoute = require('./api/search');
const dateNotesRoute = require('./api/date_notes');
const linkMapRoute = require('./api/link_map');
+const linksRoute = require('./api/links');
const clipperRoute = require('./api/clipper');
const log = require('../services/log');
@@ -158,6 +159,8 @@ function register(app) {
apiRoute(GET, '/api/attributes/values/:attributeName', attributesRoute.getValuesForAttribute);
apiRoute(POST, '/api/notes/:noteId/link-map', linkMapRoute.getLinkMap);
+ apiRoute(GET, '/api/notes/:noteId/links', linksRoute.getLinks);
+ apiRoute(GET, '/api/notes/:noteId/incoming-links', linksRoute.getIncomingLinks);
apiRoute(GET, '/api/date-notes/date/:date', dateNotesRoute.getDateNote);
apiRoute(GET, '/api/date-notes/month/:month', dateNotesRoute.getMonthNote);
diff --git a/src/services/app_info.js b/src/services/app_info.js
index 5bf647553..2fe2f4f19 100644
--- a/src/services/app_info.js
+++ b/src/services/app_info.js
@@ -4,8 +4,8 @@ const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
-const APP_DB_VERSION = 136;
-const SYNC_VERSION = 9;
+const APP_DB_VERSION = 137;
+const SYNC_VERSION = 10;
const CLIPPER_PROTOCOL_VERSION = "1.0";
module.exports = {
diff --git a/src/services/content_hash.js b/src/services/content_hash.js
index 69b0d9745..c89322817 100644
--- a/src/services/content_hash.js
+++ b/src/services/content_hash.js
@@ -12,7 +12,6 @@ const Attribute = require('../entities/attribute');
const NoteRevision = require('../entities/note_revision');
const RecentNote = require('../entities/recent_note');
const Option = require('../entities/option');
-const Link = require('../entities/link');
async function getHash(tableName, primaryKeyName, whereBranch) {
// subselect is necessary to have correct ordering in GROUP_CONCAT
@@ -40,7 +39,6 @@ async function getHashes() {
options: await getHash(Option.entityName, Option.primaryKeyName, "isSynced = 1"),
attributes: await getHash(Attribute.entityName, Attribute.primaryKeyName),
api_tokens: await getHash(ApiToken.entityName, ApiToken.primaryKeyName),
- links: await getHash(Link.entityName, Link.primaryKeyName)
};
const elapseTimeMs = Date.now() - startTime.getTime();
diff --git a/src/services/export/tar.js b/src/services/export/tar.js
index e2aa1dd28..2704ad4e3 100644
--- a/src/services/export/tar.js
+++ b/src/services/export/tar.js
@@ -115,12 +115,6 @@ async function exportToTar(exportContext, branch, format, res) {
isInheritable: attribute.isInheritable,
position: attribute.position
};
- }),
- links: (await note.getLinks()).map(link => {
- return {
- type: link.type,
- targetNoteId: link.targetNoteId
- }
})
};
@@ -220,9 +214,8 @@ async function exportToTar(exportContext, branch, format, res) {
};
for (const noteMeta of Object.values(noteIdToMeta)) {
- // filter out relations and links which are not inside this export
+ // filter out relations which are not inside this export
noteMeta.attributes = noteMeta.attributes.filter(attr => attr.type !== 'relation' || attr.value in noteIdToMeta);
- noteMeta.links = noteMeta.links.filter(link => link.targetNoteId in noteIdToMeta);
}
if (!metaFile.files[0]) { // corner case of disabled export for exported note
diff --git a/src/services/import/tar.js b/src/services/import/tar.js
index c13e6651d..be1a38316 100644
--- a/src/services/import/tar.js
+++ b/src/services/import/tar.js
@@ -1,7 +1,6 @@
"use strict";
const Attribute = require('../../entities/attribute');
-const Link = require('../../entities/link');
const utils = require('../../services/utils');
const log = require('../../services/log');
const repository = require('../../services/repository');
@@ -26,7 +25,6 @@ async function importTar(importContext, fileBuffer, importRootNote) {
// maps from original noteId (in tar file) to newly generated noteId
const noteIdMap = {};
const attributes = [];
- const links = [];
// path => noteId
const createdPaths = { '/': importRootNote.noteId, '\\': importRootNote.noteId };
const mdReader = new commonmark.Parser();
@@ -146,7 +144,7 @@ async function importTar(importContext, fileBuffer, importRootNote) {
return { type, mime };
}
- async function saveAttributesAndLinks(note, noteMeta) {
+ async function saveAttributes(note, noteMeta) {
if (!noteMeta) {
return;
}
@@ -169,13 +167,6 @@ async function importTar(importContext, fileBuffer, importRootNote) {
attributes.push(attr);
}
-
- for (const link of noteMeta.links) {
- link.noteId = note.noteId;
- link.targetNoteId = getNewNoteId(link.targetNoteId);
-
- links.push(link);
- }
}
async function saveDirectory(filePath) {
@@ -200,7 +191,7 @@ async function importTar(importContext, fileBuffer, importRootNote) {
isProtected: importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
}));
- await saveAttributesAndLinks(note, noteMeta);
+ await saveAttributes(note, noteMeta);
if (!firstNote) {
firstNote = note;
@@ -246,9 +237,11 @@ async function importTar(importContext, fileBuffer, importRootNote) {
content = content.toString("UTF-8");
if (noteMeta) {
+ const internalLinks = (noteMeta.attributes || []).find(attr => attr.type === 'relation' && attr.name === 'internal-link');
+
// this will replace all internal links ( and
) inside the body
// links pointing outside the export will be broken and changed (ctx.getNewNoteId() will still assign new noteId)
- for (const link of noteMeta.links || []) {
+ for (const link of internalLinks) {
// no need to escape the regexp find string since it's a noteId which doesn't contain any special characters
content = content.replace(new RegExp(link.targetNoteId, "g"), getNewNoteId(link.targetNoteId));
}
@@ -278,7 +271,7 @@ async function importTar(importContext, fileBuffer, importRootNote) {
isProtected: importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
}));
- await saveAttributesAndLinks(note, noteMeta);
+ await saveAttributes(note, noteMeta);
if (!noteMeta && (type === 'file' || type === 'image')) {
attributes.push({
@@ -379,15 +372,6 @@ async function importTar(importContext, fileBuffer, importRootNote) {
}
}
- for (const link of links) {
- if (link.targetNoteId in createdNoteIds) {
- await new Link(link).save();
- }
- else {
- log.info("Link not imported since target note doesn't exist: " + JSON.stringify(link));
- }
- }
-
resolve(firstNote);
});
diff --git a/src/services/notes.js b/src/services/notes.js
index 25bbf7e28..25eb1a336 100644
--- a/src/services/notes.js
+++ b/src/services/notes.js
@@ -8,7 +8,6 @@ const eventService = require('./events');
const repository = require('./repository');
const cls = require('../services/cls');
const Note = require('../entities/note');
-const Link = require('../entities/link');
const NoteRevision = require('../entities/note_revision');
const Branch = require('../entities/branch');
const Attribute = require('../entities/attribute');
@@ -215,8 +214,8 @@ function findImageLinks(content, foundLinks) {
while (match = re.exec(content)) {
foundLinks.push({
- type: 'image',
- targetNoteId: match[1]
+ type: 'image-link',
+ value: match[1]
});
}
@@ -225,14 +224,14 @@ function findImageLinks(content, foundLinks) {
return content.replace(/src="[^"]*\/api\/images\//g, 'src="api/images/');
}
-function findHyperLinks(content, foundLinks) {
+function findInternalLinks(content, foundLinks) {
const re = /href="[^"]*#root[a-zA-Z0-9\/]*\/([a-zA-Z0-9]+)\/?"/g;
let match;
while (match = re.exec(content)) {
foundLinks.push({
- type: 'hyper',
- targetNoteId: match[1]
+ name: 'internal-link',
+ value: match[1]
});
}
@@ -245,8 +244,8 @@ function findRelationMapLinks(content, foundLinks) {
for (const note of obj.notes) {
foundLinks.push({
- type: 'relation-map',
- targetNoteId: note.noteId
+ type: 'relation-map-link',
+ value: note.noteId
})
}
}
@@ -260,7 +259,7 @@ async function saveLinks(note, content) {
if (note.type === 'text') {
content = findImageLinks(content, foundLinks);
- content = findHyperLinks(content, foundLinks);
+ content = findInternalLinks(content, foundLinks);
}
else if (note.type === 'relation-map') {
findRelationMapLinks(content, foundLinks);
@@ -273,14 +272,15 @@ async function saveLinks(note, content) {
for (const foundLink of foundLinks) {
const existingLink = existingLinks.find(existingLink =>
- existingLink.targetNoteId === foundLink.targetNoteId
- && existingLink.type === foundLink.type);
+ existingLink.value === foundLink.value
+ && existingLink.name === foundLink.name);
if (!existingLink) {
- await new Link({
+ await new Attribute({
noteId: note.noteId,
- targetNoteId: foundLink.targetNoteId,
- type: foundLink.type
+ type: 'relation',
+ name: foundLink.name,
+ value: foundLink.targetNoteId,
}).save();
}
else if (existingLink.isDeleted) {
@@ -292,8 +292,8 @@ async function saveLinks(note, content) {
// marking links as deleted if they are not present on the page anymore
const unusedLinks = existingLinks.filter(existingLink => !foundLinks.some(foundLink =>
- existingLink.targetNoteId === foundLink.targetNoteId
- && existingLink.type === foundLink.type));
+ existingLink.value === foundLink.value
+ && existingLink.name === foundLink.name));
for (const unusedLink of unusedLinks) {
unusedLink.isDeleted = true;
@@ -415,11 +415,6 @@ async function deleteNote(branch) {
await relation.save();
}
- for (const link of await note.getLinks()) {
- link.isDeleted = true;
- await link.save();
- }
-
for (const link of await note.getTargetLinks()) {
link.isDeleted = true;
await link.save();
diff --git a/src/services/sync.js b/src/services/sync.js
index 28cfdc3af..1803f90c7 100644
--- a/src/services/sync.js
+++ b/src/services/sync.js
@@ -249,8 +249,7 @@ const primaryKeys = {
"recent_notes": "noteId",
"api_tokens": "apiTokenId",
"options": "name",
- "attributes": "attributeId",
- "links": "linkId"
+ "attributes": "attributeId"
};
async function getEntityRow(entityName, entityId) {
diff --git a/src/services/sync_table.js b/src/services/sync_table.js
index f5465e234..03b0356d5 100644
--- a/src/services/sync_table.js
+++ b/src/services/sync_table.js
@@ -32,10 +32,6 @@ async function addRecentNoteSync(noteId, sourceId) {
await addEntitySync("recent_notes", noteId, sourceId);
}
-async function addLinkSync(linkId, sourceId) {
- await addEntitySync("links", linkId, sourceId);
-}
-
async function addAttributeSync(attributeId, sourceId) {
await addEntitySync("attributes", attributeId, sourceId);
}
@@ -101,7 +97,6 @@ async function fillAllSyncRows() {
await fillSyncRows("recent_notes", "noteId");
await fillSyncRows("attributes", "attributeId");
await fillSyncRows("api_tokens", "apiTokenId");
- await fillSyncRows("links", "linkId");
await fillSyncRows("options", "name", 'isSynced = 1');
}
@@ -115,7 +110,6 @@ module.exports = {
addRecentNoteSync,
addAttributeSync,
addApiTokenSync,
- addLinkSync,
addEntitySync,
fillAllSyncRows
};
\ No newline at end of file
diff --git a/src/services/sync_update.js b/src/services/sync_update.js
index 548675357..5779bbf34 100644
--- a/src/services/sync_update.js
+++ b/src/services/sync_update.js
@@ -28,9 +28,6 @@ async function updateEntity(sync, entity, sourceId) {
else if (entityName === 'recent_notes') {
await updateRecentNotes(entity, sourceId);
}
- else if (entityName === 'links') {
- await updateLink(entity, sourceId);
- }
else if (entityName === 'attributes') {
await updateAttribute(entity, sourceId);
}
@@ -159,20 +156,6 @@ async function updateRecentNotes(entity, sourceId) {
}
}
-async function updateLink(entity, sourceId) {
- const origLink = await sql.getRow("SELECT * FROM links WHERE linkId = ?", [entity.linkId]);
-
- if (!origLink || origLink.utcDateModified <= entity.utcDateModified) {
- await sql.transactional(async () => {
- await sql.replace("links", entity);
-
- await syncTableService.addLinkSync(entity.linkId, sourceId);
- });
-
- log.info("Update/sync link " + entity.linkId);
- }
-}
-
async function updateAttribute(entity, sourceId) {
const origAttribute = await sql.getRow("SELECT * FROM attributes WHERE attributeId = ?", [entity.attributeId]);