1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-11-29 22:48:10 +02:00

All: Resolves #483: Add trash folder (#9671)

This commit is contained in:
Laurent Cozic
2024-03-02 14:25:27 +00:00
committed by GitHub
parent 07fbd547dc
commit f19b1c5364
112 changed files with 2322 additions and 966 deletions

View File

@@ -5,18 +5,20 @@ import JoplinError from '../../JoplinError';
import { State as ShareState } from '../../services/share/reducer';
import ItemChange from '../ItemChange';
import Setting from '../Setting';
import { checkObjectHasProperties } from '@joplin/utils/object';
const logger = Logger.create('models/utils/readOnly');
export interface ItemSlice {
id?: string;
share_id: string;
deleted_time: number;
}
// This function can be called to wrap any read-only-related code. It should be
// fast and allows an early exit for cases that don't apply, for example if not
// This function can be called to wrap code that related to share permission read-only checks. It
// should be fast and allows an early exit for cases that don't apply, for example if not
// synchronising with Joplin Cloud or if not sharing any notebook.
export const needsReadOnlyChecks = (itemType: ModelType, changeSource: number, shareState: ShareState, disableReadOnlyCheck = false) => {
export const needsShareReadOnlyChecks = (itemType: ModelType, changeSource: number, shareState: ShareState, disableReadOnlyCheck = false) => {
if (disableReadOnlyCheck) return false;
if (Setting.value('sync.target') !== 10) return false;
if (changeSource === ItemChange.SOURCE_SYNC) return false;
@@ -35,16 +37,16 @@ export const checkIfItemsCanBeChanged = (itemType: ModelType, changeSource: numb
};
export const checkIfItemCanBeChanged = (itemType: ModelType, changeSource: number, item: ItemSlice, shareState: ShareState) => {
if (!needsReadOnlyChecks(itemType, changeSource, shareState)) return;
if (!needsShareReadOnlyChecks(itemType, changeSource, shareState)) return;
if (!item) return;
if (itemIsReadOnlySync(itemType, changeSource, item, Setting.value('sync.userId'), shareState)) {
if (itemIsReadOnlySync(itemType, changeSource, item, Setting.value('sync.userId'), shareState, true)) {
throw new JoplinError(`Cannot change or delete a read-only item: ${item.id}`, ErrorCode.IsReadOnly);
}
};
export const checkIfItemCanBeAddedToFolder = async (itemType: ModelType, Folder: any, changeSource: number, shareState: ShareState, parentId: string) => {
if (needsReadOnlyChecks(itemType, changeSource, shareState) && parentId) {
if (needsShareReadOnlyChecks(itemType, changeSource, shareState) && parentId) {
const parentFolder = await Folder.load(parentId, { fields: ['id', 'share_id'] });
if (!parentFolder) {
@@ -58,16 +60,27 @@ export const checkIfItemCanBeAddedToFolder = async (itemType: ModelType, Folder:
return;
}
if (itemIsReadOnlySync(itemType, changeSource, parentFolder, Setting.value('sync.userId'), shareState)) {
if (itemIsReadOnlySync(itemType, changeSource, parentFolder, Setting.value('sync.userId'), shareState, true)) {
throw new JoplinError('Cannot add an item as a child of a read-only item', ErrorCode.IsReadOnly);
}
}
};
export const itemIsReadOnlySync = (itemType: ModelType, changeSource: number, item: ItemSlice, userId: string, shareState: ShareState): boolean => {
if (!needsReadOnlyChecks(itemType, changeSource, shareState)) return false;
// Originally all these functions were there to handle share permissions - a note, folder or
// resource that is not editable would be read-only. However this particular function now is also
// used to tell if a note is read-only because it is in the trash.
//
// But this requires access to more properties, `deleted_time` in particular, which are not needed
// for share-related checks (and does not exist on Resource objects). So this is why there's this
// extra `sharePermissionCheckOnly` boolean to do the check for one case or the other. A bit of a
// hack but good enough for now.
export const itemIsReadOnlySync = (itemType: ModelType, changeSource: number, item: ItemSlice, userId: string, shareState: ShareState, sharePermissionCheckOnly = false): boolean => {
checkObjectHasProperties(item, sharePermissionCheckOnly ? ['share_id'] : ['share_id', 'deleted_time']);
if (!('share_id' in item)) throw new Error('share_id property is missing');
// Item is in trash
if (!sharePermissionCheckOnly && item.deleted_time) return true;
if (!needsShareReadOnlyChecks(itemType, changeSource, shareState)) return false;
// Item is not shared
if (!item.share_id) return false;
@@ -84,8 +97,8 @@ export const itemIsReadOnlySync = (itemType: ModelType, changeSource: number, it
};
export const itemIsReadOnly = async (BaseItem: any, itemType: ModelType, changeSource: number, itemId: string, userId: string, shareState: ShareState): Promise<boolean> => {
if (!needsReadOnlyChecks(itemType, changeSource, shareState)) return false;
const item: ItemSlice = await BaseItem.loadItem(itemType, itemId, { fields: ['id', 'share_id'] });
// if (!needsShareReadOnlyChecks(itemType, changeSource, shareState)) return false;
const item: ItemSlice = await BaseItem.loadItem(itemType, itemId, { fields: ['id', 'share_id', 'deleted_time'] });
if (!item) throw new JoplinError(`No such item: ${itemType}: ${itemId}`, ErrorCode.NotFound);
return itemIsReadOnlySync(itemType, changeSource, item, userId, shareState);
};