mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
safe import implementation
This commit is contained in:
parent
caa7dd9619
commit
14f7a8b7b9
@ -12,6 +12,7 @@ const $fileUploadInput = $("#import-file-upload-input");
|
||||
const $importProgressCountWrapper = $("#import-progress-count-wrapper");
|
||||
const $importProgressCount = $("#import-progress-count");
|
||||
const $importButton = $("#import-button");
|
||||
const $safeImport = $("#safe-import");
|
||||
|
||||
let importId;
|
||||
|
||||
@ -21,6 +22,7 @@ async function showDialog() {
|
||||
$importProgressCountWrapper.hide();
|
||||
$importProgressCount.text('0');
|
||||
$fileUploadInput.val('').change(); // to trigger Import button disabling listener below
|
||||
$safeImport.attr("checked", "checked");
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
@ -49,8 +51,10 @@ function importIntoNote(importNoteId) {
|
||||
// dialog (which shouldn't happen, but still ...)
|
||||
importId = utils.randomString(10);
|
||||
|
||||
const safeImport = $safeImport.is(":checked") ? 1 : 0;
|
||||
|
||||
$.ajax({
|
||||
url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId,
|
||||
url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId + '/safe/' + safeImport,
|
||||
headers: server.getHeaders(),
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
|
@ -12,10 +12,13 @@ const noteCacheService = require('../../services/note_cache');
|
||||
const log = require('../../services/log');
|
||||
|
||||
class ImportContext {
|
||||
constructor(importId) {
|
||||
constructor(importId, safeImport) {
|
||||
// importId is to distinguish between different import events - it is possible (though not recommended)
|
||||
// to have multiple imports going at the same time
|
||||
this.importId = importId;
|
||||
|
||||
this.safeImport = safeImport;
|
||||
|
||||
// // count is mean to represent count of exported notes where practical, otherwise it's just some measure of progress
|
||||
this.progressCount = 0;
|
||||
this.lastSentCountTs = Date.now();
|
||||
@ -53,7 +56,10 @@ class ImportContext {
|
||||
}
|
||||
|
||||
async function importToBranch(req) {
|
||||
const {parentNoteId, importId} = req.params;
|
||||
let {parentNoteId, importId, safeImport} = req.params;
|
||||
|
||||
safeImport = safeImport !== '0';
|
||||
|
||||
const file = req.file;
|
||||
|
||||
if (!file) {
|
||||
@ -74,7 +80,7 @@ async function importToBranch(req) {
|
||||
|
||||
let note; // typically root of the import - client can show it after finishing the import
|
||||
|
||||
const importContext = new ImportContext(importId);
|
||||
const importContext = new ImportContext(importId, safeImport);
|
||||
|
||||
try {
|
||||
if (extension === '.tar') {
|
||||
|
@ -129,7 +129,7 @@ function register(app) {
|
||||
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
|
||||
|
||||
route(GET, '/api/notes/:branchId/export/:type/:format/:exportId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
|
||||
route(POST, '/api/notes/:parentNoteId/import/:importId', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler);
|
||||
route(POST, '/api/notes/:parentNoteId/import/:importId/safe/:safeImport', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler);
|
||||
|
||||
route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware],
|
||||
filesRoute.uploadFile, apiResultHandler);
|
||||
|
@ -5,13 +5,14 @@ const sql = require('./sql');
|
||||
const utils = require('./utils');
|
||||
const Attribute = require('../entities/attribute');
|
||||
|
||||
const ATTRIBUTE_TYPES = [ 'label', 'label-definition', 'relation', 'relation-definition' ];
|
||||
|
||||
const BUILTIN_ATTRIBUTES = [
|
||||
// label names
|
||||
{ type: 'label', name: 'disableVersioning' },
|
||||
{ type: 'label', name: 'calendarRoot' },
|
||||
{ type: 'label', name: 'archived' },
|
||||
{ type: 'label', name: 'excludeFromExport' },
|
||||
{ type: 'label', name: 'run' },
|
||||
{ type: 'label', name: 'manualTransactionHandling' },
|
||||
{ type: 'label', name: 'disableInclusion' },
|
||||
{ type: 'label', name: 'appCss' },
|
||||
@ -19,19 +20,20 @@ const BUILTIN_ATTRIBUTES = [
|
||||
{ type: 'label', name: 'hideChildrenOverview' },
|
||||
{ type: 'label', name: 'hidePromotedAttributes' },
|
||||
{ type: 'label', name: 'readOnly' },
|
||||
{ type: 'label', name: 'customRequestHandler' },
|
||||
{ type: 'label', name: 'customResourceProvider' },
|
||||
{ type: 'label', name: 'run', isDangerous: true },
|
||||
{ type: 'label', name: 'customRequestHandler', isDangerous: true },
|
||||
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
|
||||
|
||||
// relation names
|
||||
{ type: 'relation', name: 'runOnNoteView' },
|
||||
{ type: 'relation', name: 'runOnNoteCreation' },
|
||||
{ type: 'relation', name: 'runOnNoteTitleChange' },
|
||||
{ type: 'relation', name: 'runOnNoteChange' },
|
||||
{ type: 'relation', name: 'runOnChildNoteCreation' },
|
||||
{ type: 'relation', name: 'runOnAttributeCreation' },
|
||||
{ type: 'relation', name: 'runOnAttributeChange' },
|
||||
{ type: 'relation', name: 'runOnNoteView', isDangerous: true },
|
||||
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
|
||||
{ type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true },
|
||||
{ type: 'relation', name: 'runOnNoteChange', isDangerous: true },
|
||||
{ type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true },
|
||||
{ type: 'relation', name: 'runOnAttributeCreation', isDangerous: true },
|
||||
{ type: 'relation', name: 'runOnAttributeChange', isDangerous: true },
|
||||
{ type: 'relation', name: 'template' },
|
||||
{ type: 'relation', name: 'renderNote' }
|
||||
{ type: 'relation', name: 'renderNote', isDangerous: true }
|
||||
];
|
||||
|
||||
async function getNotesWithLabel(name, value) {
|
||||
@ -94,11 +96,25 @@ async function getAttributeNames(type, nameLike) {
|
||||
return names;
|
||||
}
|
||||
|
||||
function isAttributeType(type) {
|
||||
return ATTRIBUTE_TYPES.includes(type);
|
||||
}
|
||||
|
||||
function isAttributeDangerous(type, name) {
|
||||
return BUILTIN_ATTRIBUTES.some(attr =>
|
||||
attr.type === attr.type &&
|
||||
attr.name.toLowerCase() === name.trim().toLowerCase() &&
|
||||
attr.isDangerous
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getNotesWithLabel,
|
||||
getNotesWithLabels,
|
||||
getNoteWithLabel,
|
||||
createLabel,
|
||||
createAttribute,
|
||||
getAttributeNames
|
||||
getAttributeNames,
|
||||
isAttributeType,
|
||||
isAttributeDangerous
|
||||
};
|
@ -6,6 +6,7 @@ const utils = require('../../services/utils');
|
||||
const log = require('../../services/log');
|
||||
const repository = require('../../services/repository');
|
||||
const noteService = require('../../services/notes');
|
||||
const attributeService = require('../../services/attributes');
|
||||
const Branch = require('../../entities/branch');
|
||||
const tar = require('tar-stream');
|
||||
const stream = require('stream');
|
||||
@ -153,10 +154,19 @@ async function importTar(importContext, fileBuffer, importRootNote) {
|
||||
for (const attr of noteMeta.attributes) {
|
||||
attr.noteId = note.noteId;
|
||||
|
||||
if (!attributeService.isAttributeType(attr.type)) {
|
||||
log.error("Unrecognized attribute type " + attr.type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attr.type === 'relation') {
|
||||
attr.value = getNewNoteId(attr.value);
|
||||
}
|
||||
|
||||
if (importContext.safeImport && attributeService.isAttributeDangerous(attr.type, attr.name)) {
|
||||
attr.name = 'disabled-' + attr.name;
|
||||
}
|
||||
|
||||
attributes.push(attr);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user