mirror of
https://github.com/zadam/trilium.git
synced 2024-12-28 10:52:18 +08:00
deleting attributes, closes #34
This commit is contained in:
parent
7c74c77a2c
commit
e011b9ae63
8 changed files with 60 additions and 25 deletions
1
db/migrations/0073__add_isDeleted_to_attributes.sql
Normal file
1
db/migrations/0073__add_isDeleted_to_attributes.sql
Normal file
|
@ -0,0 +1 @@
|
|||
ALTER TABLE attributes ADD COLUMN isDeleted INT NOT NULL DEFAULT 0;
|
|
@ -86,7 +86,8 @@ CREATE TABLE IF NOT EXISTS "attributes"
|
|||
name TEXT NOT NULL,
|
||||
value TEXT,
|
||||
dateCreated TEXT NOT NULL,
|
||||
dateModified TEXT NOT NULL
|
||||
dateModified TEXT NOT NULL,
|
||||
isDeleted INT NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
|
|
|
@ -24,7 +24,7 @@ class Note extends Entity {
|
|||
}
|
||||
|
||||
async getAttributes() {
|
||||
return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ?", [this.noteId]);
|
||||
return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ? AND isDeleted = 0", [this.noteId]);
|
||||
}
|
||||
|
||||
async getAttribute(name) {
|
||||
|
|
|
@ -26,6 +26,19 @@ const attributesDialog = (function() {
|
|||
setTimeout(() => $(".attribute-name:last").focus(), 100);
|
||||
};
|
||||
|
||||
this.deleteAttribute = function(data, event) {
|
||||
const attr = self.getTargetAttribute(event);
|
||||
const attrData = attr();
|
||||
|
||||
if (attrData) {
|
||||
attrData.isDeleted = 1;
|
||||
|
||||
attr(attrData);
|
||||
|
||||
addLastEmptyRow();
|
||||
}
|
||||
};
|
||||
|
||||
function isValid() {
|
||||
for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) {
|
||||
if (self.isEmptyName(i)) {
|
||||
|
@ -65,26 +78,25 @@ const attributesDialog = (function() {
|
|||
};
|
||||
|
||||
function addLastEmptyRow() {
|
||||
const attrs = self.attributes();
|
||||
const attrs = self.attributes().filter(attr => attr().isDeleted === 0);
|
||||
const last = attrs.length === 0 ? null : attrs[attrs.length - 1]();
|
||||
|
||||
if (!last || last.name.trim() !== "" || last.value !== "") {
|
||||
self.attributes.push(ko.observable({
|
||||
attributeId: '',
|
||||
name: '',
|
||||
value: ''
|
||||
value: '',
|
||||
isDeleted: 0
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
this.attributeChanged = function (row) {
|
||||
this.attributeChanged = function (data, event) {
|
||||
addLastEmptyRow();
|
||||
|
||||
for (const attr of self.attributes()) {
|
||||
if (row.attributeId === attr().attributeId) {
|
||||
attr.valueHasMutated();
|
||||
}
|
||||
}
|
||||
const attr = self.getTargetAttribute(event);
|
||||
|
||||
attr.valueHasMutated();
|
||||
};
|
||||
|
||||
this.isNotUnique = function(index) {
|
||||
|
@ -109,6 +121,13 @@ const attributesDialog = (function() {
|
|||
const cur = self.attributes()[index]();
|
||||
|
||||
return cur.name.trim() === "" && (cur.attributeId !== "" || cur.value !== "");
|
||||
};
|
||||
|
||||
this.getTargetAttribute = function(event) {
|
||||
const context = ko.contextFor(event.target);
|
||||
const index = context.$index();
|
||||
|
||||
return self.attributes()[index];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ const attributes = require('../../services/attributes');
|
|||
router.get('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||
const noteId = req.params.noteId;
|
||||
|
||||
res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId]));
|
||||
res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY dateCreated", [noteId]));
|
||||
}));
|
||||
|
||||
router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||
|
@ -23,10 +23,15 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res,
|
|||
await sql.doInTransaction(async () => {
|
||||
for (const attr of attributes) {
|
||||
if (attr.attributeId) {
|
||||
await sql.execute("UPDATE attributes SET name = ?, value = ?, dateModified = ? WHERE attributeId = ?",
|
||||
[attr.name, attr.value, now, attr.attributeId]);
|
||||
await sql.execute("UPDATE attributes SET name = ?, value = ?, dateModified = ?, isDeleted = ? WHERE attributeId = ?",
|
||||
[attr.name, attr.value, now, attr.isDeleted, attr.attributeId]);
|
||||
}
|
||||
else {
|
||||
// if it was "created" and then immediatelly deleted, we just don't create it at all
|
||||
if (attr.isDeleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attr.attributeId = utils.newAttributeId();
|
||||
|
||||
await sql.insert("attributes", {
|
||||
|
@ -35,7 +40,8 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res,
|
|||
name: attr.name,
|
||||
value: attr.value,
|
||||
dateCreated: now,
|
||||
dateModified: now
|
||||
dateModified: now,
|
||||
isDeleted: false
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -43,11 +49,11 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res,
|
|||
}
|
||||
});
|
||||
|
||||
res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId]));
|
||||
res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY dateCreated", [noteId]));
|
||||
}));
|
||||
|
||||
router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||
const names = await sql.getColumn("SELECT DISTINCT name FROM attributes");
|
||||
const names = await sql.getColumn("SELECT DISTINCT name FROM attributes WHERE isDeleted = 0");
|
||||
|
||||
for (const attr of attributes.BUILTIN_ATTRIBUTES) {
|
||||
if (!names.includes(attr)) {
|
||||
|
@ -63,7 +69,7 @@ router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) =
|
|||
router.get('/attributes/values/:attributeName', auth.checkApiAuth, wrap(async (req, res, next) => {
|
||||
const attributeName = req.params.attributeName;
|
||||
|
||||
const values = await sql.getColumn("SELECT DISTINCT value FROM attributes WHERE name = ? AND value != '' ORDER BY value", [attributeName]);
|
||||
const values = await sql.getColumn("SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [attributeName]);
|
||||
|
||||
res.send(values);
|
||||
}));
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
const build = require('./build');
|
||||
const packageJson = require('../../package');
|
||||
|
||||
const APP_DB_VERSION = 72;
|
||||
const APP_DB_VERSION = 73;
|
||||
|
||||
module.exports = {
|
||||
app_version: packageJson.version,
|
||||
|
|
|
@ -13,7 +13,10 @@ async function getNoteAttributeMap(noteId) {
|
|||
|
||||
async function getNoteIdWithAttribute(name, value) {
|
||||
return await sql.getValue(`SELECT notes.noteId FROM notes JOIN attributes USING(noteId)
|
||||
WHERE notes.isDeleted = 0 AND attributes.name = ? AND attributes.value = ?`, [name, value]);
|
||||
WHERE notes.isDeleted = 0
|
||||
AND attributes.isDeleted = 0
|
||||
AND attributes.name = ?
|
||||
AND attributes.value = ?`, [name, value]);
|
||||
}
|
||||
|
||||
async function getNotesWithAttribute(dataKey, name, value) {
|
||||
|
@ -23,11 +26,11 @@ async function getNotesWithAttribute(dataKey, name, value) {
|
|||
|
||||
if (value !== undefined) {
|
||||
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN attributes USING(noteId)
|
||||
WHERE notes.isDeleted = 0 AND attributes.name = ? AND attributes.value = ?`, [name, value]);
|
||||
WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ? AND attributes.value = ?`, [name, value]);
|
||||
}
|
||||
else {
|
||||
notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN attributes USING(noteId)
|
||||
WHERE notes.isDeleted = 0 AND attributes.name = ?`, [name]);
|
||||
WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ?`, [name]);
|
||||
}
|
||||
|
||||
return notes;
|
||||
|
@ -41,7 +44,7 @@ async function getNoteWithAttribute(dataKey, name, value) {
|
|||
|
||||
async function getNoteIdsWithAttribute(name) {
|
||||
return await sql.getColumn(`SELECT DISTINCT notes.noteId FROM notes JOIN attributes USING(noteId)
|
||||
WHERE notes.isDeleted = 0 AND attributes.name = ?`, [name]);
|
||||
WHERE notes.isDeleted = 0 AND attributes.isDeleted = 0 AND attributes.name = ? AND attributes.isDeleted = 0`, [name]);
|
||||
}
|
||||
|
||||
async function createAttribute(noteId, name, value = null, sourceId = null) {
|
||||
|
@ -54,7 +57,8 @@ async function createAttribute(noteId, name, value = null, sourceId = null) {
|
|||
name: name,
|
||||
value: value,
|
||||
dateModified: now,
|
||||
dateCreated: now
|
||||
dateCreated: now,
|
||||
isDeleted: false
|
||||
});
|
||||
|
||||
await sync_table.addAttributeSync(attributeId, sourceId);
|
||||
|
|
|
@ -376,7 +376,7 @@
|
|||
<div id="attributes-dialog" title="Note attributes" style="display: none; padding: 20px;">
|
||||
<form data-bind="submit: save">
|
||||
<div style="text-align: center">
|
||||
<button class="btn btn-large" style="width: 200px;" id="save-attributes-button" type="submit">Save <kbd>enter</kbd></button>
|
||||
<button class="btn btn-large" style="width: 200px;" id="save-attributes-button" type="submit">Save changes <kbd>enter</kbd></button>
|
||||
</div>
|
||||
|
||||
<div style="height: 97%; overflow: auto">
|
||||
|
@ -386,10 +386,11 @@
|
|||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind="foreach: attributes">
|
||||
<tr>
|
||||
<tr data-bind="if: isDeleted == 0">
|
||||
<td data-bind="text: attributeId"></td>
|
||||
<td>
|
||||
<!-- Change to valueUpdate: blur is necessary because jQuery UI autocomplete hijacks change event -->
|
||||
|
@ -400,6 +401,9 @@
|
|||
<td>
|
||||
<input type="text" class="attribute-value" data-bind="value: value, valueUpdate: 'blur', event: { blur: $parent.attributeChanged }" style="width: 300px"/>
|
||||
</td>
|
||||
<td title="Delete" style="padding: 13px;">
|
||||
<span class="glyphicon glyphicon-trash" data-bind="click: $parent.deleteAttribute"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
Loading…
Reference in a new issue