You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-23 22:36:32 +02:00
Desktop, Mobile: Fixes #13258: Prevent new notes from being created in trashed or missing notebooks in certain cases (#13575)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { utils, CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
|
||||
export const newNoteEnabledConditions = 'oneFolderSelected && !inConflictFolder && !folderIsReadOnly && !folderIsTrash';
|
||||
|
||||
@@ -14,7 +14,7 @@ export const declaration: CommandDeclaration = {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context: CommandContext, body = '', isTodo = false) => {
|
||||
const folderId = Setting.value('activeFolderId');
|
||||
const folderId = await Folder.getValidActiveFolder();
|
||||
if (!folderId) return;
|
||||
|
||||
const defaultValues = Note.previewFieldsWithDefaultValues({ includeTimestamps: false });
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/
|
||||
import Logger from '@joplin/utils/Logger';
|
||||
import goToNote, { GotoNoteOptions } from './util/goToNote';
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
|
||||
const logger = Logger.create('newNoteCommand');
|
||||
|
||||
@@ -13,7 +13,7 @@ export const declaration: CommandDeclaration = {
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async (_context: CommandContext, body = '', todo = false, options: GotoNoteOptions = null) => {
|
||||
const folderId = Setting.value('activeFolderId');
|
||||
const folderId = await Folder.getValidActiveFolder();
|
||||
if (!folderId) {
|
||||
logger.warn('Not creating new note -- no active folder ID.');
|
||||
return;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FolderEntity } from '../services/database/types';
|
||||
import { createNTestNotes, setupDatabaseAndSynchronizer, sleep, switchClient, checkThrowAsync, createFolderTree, simulateReadOnlyShareEnv, expectThrow, withWarningSilenced } from '../testing/test-utils';
|
||||
import Folder from './Folder';
|
||||
import Note from './Note';
|
||||
import Setting from './Setting';
|
||||
|
||||
async function allItems() {
|
||||
const folders = await Folder.all();
|
||||
@@ -440,4 +441,40 @@ describe('models/Folder', () => {
|
||||
expect(Folder.atLeastOneRealFolderExists(folders)).toBe(false);
|
||||
});
|
||||
|
||||
it('should get active folder when activeFolderId is valid', async () => {
|
||||
const activeFolder = await Folder.save({ title: 'folder' });
|
||||
Setting.setValue('activeFolderId', activeFolder.id);
|
||||
|
||||
const validFolder = await Folder.getValidActiveFolder();
|
||||
expect(validFolder).toBe(activeFolder.id);
|
||||
});
|
||||
|
||||
it('should get default folder when activeFolderId is trashed', async () => {
|
||||
const defaultFolder = await Folder.save({ title: 'default' });
|
||||
const activeFolder = await Folder.save({ title: 'folder' });
|
||||
await Folder.delete(activeFolder.id, { toTrash: true });
|
||||
Setting.setValue('activeFolderId', activeFolder.id);
|
||||
|
||||
const validFolder = await Folder.getValidActiveFolder();
|
||||
expect(validFolder).toBe(defaultFolder.id);
|
||||
});
|
||||
|
||||
it('should get no folder when activeFolderId is undefined', async () => {
|
||||
Setting.setValue('activeFolderId', undefined);
|
||||
|
||||
const validFolder = await Folder.getValidActiveFolder();
|
||||
expect(validFolder).toBeNull();
|
||||
});
|
||||
|
||||
it('should get no folder when activeFolderId is trashed and there are no other not trashed folders', async () => {
|
||||
const activeFolder = await Folder.save({ title: 'folder' });
|
||||
const otherFolder = await Folder.save({ title: 'other' });
|
||||
await Folder.delete(activeFolder.id, { toTrash: true });
|
||||
await Folder.delete(otherFolder.id, { toTrash: true });
|
||||
Setting.setValue('activeFolderId', activeFolder.id);
|
||||
|
||||
const validFolder = await Folder.getValidActiveFolder();
|
||||
expect(validFolder).toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ import { getTrashFolder } from '../services/trash';
|
||||
import getConflictFolderId from './utils/getConflictFolderId';
|
||||
import getTrashFolderId from '../services/trash/getTrashFolderId';
|
||||
import { getCollator } from './utils/getCollator';
|
||||
import Setting from './Setting';
|
||||
const { substrWithEllipsis } = require('../string-utils.js');
|
||||
|
||||
const logger = Logger.create('models/Folder');
|
||||
@@ -918,7 +919,7 @@ export default class Folder extends BaseItem {
|
||||
}
|
||||
|
||||
public static defaultFolder() {
|
||||
return this.modelSelectOne('SELECT * FROM folders ORDER BY created_time DESC LIMIT 1');
|
||||
return this.modelSelectOne('SELECT * FROM folders WHERE deleted_time = 0 ORDER BY created_time DESC LIMIT 1');
|
||||
}
|
||||
|
||||
public static async canNestUnder(folderId: string, targetFolderId: string) {
|
||||
@@ -1076,4 +1077,18 @@ export default class Folder extends BaseItem {
|
||||
return this.getRealFolders(folders).length > 0;
|
||||
}
|
||||
|
||||
public static async getValidActiveFolder() {
|
||||
const folderId = Setting.value('activeFolderId');
|
||||
if (!folderId) return null;
|
||||
|
||||
const folder = await Folder.load(folderId);
|
||||
if (!folder || !!folder.deleted_time) {
|
||||
const defaultFolder = await Folder.defaultFolder();
|
||||
if (!defaultFolder) return null;
|
||||
return defaultFolder.id;
|
||||
}
|
||||
|
||||
return folderId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user