From c9ccf797daacdfb539879aa032997ce0d16ca2c3 Mon Sep 17 00:00:00 2001 From: azivner Date: Thu, 23 Nov 2017 21:50:12 -0500 Subject: [PATCH] cycle check when cloning notes --- routes/api/notes_move.js | 43 +++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/routes/api/notes_move.js b/routes/api/notes_move.js index 33c550716..19ab21064 100644 --- a/routes/api/notes_move.js +++ b/routes/api/notes_move.js @@ -93,12 +93,17 @@ router.put('/:childNoteId/cloneTo/:parentNoteId', auth.checkApiAuth, async (req, const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND note_pid = ?', [childNoteId, parentNoteId]); if (existing && !existing.is_deleted) { - res.send({ + return res.send({ success: false, message: 'This note already exists in target parent note.' }); + } - return; + if (!await checkCycle(parentNoteId, childNoteId)) { + return res.send({ + success: false, + message: 'Cloning note here would create cycle.' + }); } const maxNotePos = await sql.getSingleValue('SELECT MAX(note_pos) FROM notes_tree WHERE note_pid = ? AND is_deleted = 0', [parentNoteId]); @@ -132,19 +137,23 @@ router.put('/:noteId/cloneAfter/:afterNoteTreeId', async (req, res, next) => { const afterNote = await sql.getSingleResult("SELECT * FROM notes_tree WHERE note_tree_id = ?", [afterNoteTreeId]); if (!afterNote) { - res.status(500).send("After note " + afterNoteTreeId + " doesn't exist."); - return; + return res.status(500).send("After note " + afterNoteTreeId + " doesn't exist."); + } + + if (!await checkCycle(afterNote.note_pid, noteId)) { + return res.send({ + success: false, + message: 'Cloning note here would create cycle.' + }); } const existing = await sql.getSingleValue('SELECT * FROM notes_tree WHERE note_id = ? AND note_pid = ?', [noteId, afterNote.note_pid]); if (existing && !existing.is_deleted) { - res.send({ + return res.send({ success: false, message: 'This note already exists in target parent note.' }); - - return; } await sql.doInTransaction(async () => { @@ -174,6 +183,26 @@ router.put('/:noteId/cloneAfter/:afterNoteTreeId', async (req, res, next) => { }); }); +async function checkCycle(parentNoteId, childNoteId) { + if (parentNoteId === 'root') { + return true; + } + + if (parentNoteId === childNoteId) { + return false; + } + + const parentNoteIds = await sql.getFlattenedResults("note_pid", "SELECT DISTINCT note_pid FROM notes_tree WHERE note_id = ?", [parentNoteId]); + + for (const pid of parentNoteIds) { + if (!await checkCycle(pid, childNoteId)) { + return false; + } + } + + return true; +} + router.put('/:noteTreeId/expanded/:expanded', async (req, res, next) => { const noteTreeId = req.params.noteTreeId; const expanded = req.params.expanded;