diff --git a/packages/lib/models/utils/readOnly.ts b/packages/lib/models/utils/readOnly.ts index fe28a8bff..7eb8aabff 100644 --- a/packages/lib/models/utils/readOnly.ts +++ b/packages/lib/models/utils/readOnly.ts @@ -1,3 +1,4 @@ +import Logger from '@joplin/utils/Logger'; import { ModelType } from '../../BaseModel'; import { ErrorCode } from '../../errors'; import JoplinError from '../../JoplinError'; @@ -5,6 +6,8 @@ import { State as ShareState } from '../../services/share/reducer'; import ItemChange from '../ItemChange'; import Setting from '../Setting'; +const logger = Logger.create('models/utils/readOnly'); + export interface ItemSlice { id?: string; share_id: string; @@ -43,6 +46,18 @@ export const checkIfItemCanBeChanged = (itemType: ModelType, changeSource: numbe export const checkIfItemCanBeAddedToFolder = async (itemType: ModelType, Folder: any, changeSource: number, shareState: ShareState, parentId: string) => { if (needsReadOnlyChecks(itemType, changeSource, shareState) && parentId) { const parentFolder = await Folder.load(parentId, { fields: ['id', 'share_id'] }); + + if (!parentFolder) { + // Historically it's always been possible to set the parent_id of a + // note to a folder that does not exist - this is to support + // synchronisation, where items are downloaded in random order. It + // is not ideal to skip the check here, but if for some reason the + // folder turns out to be read-only the issue will be resolved + // during sync. + logger.warn('checkIfItemCanBeAddedToFolder: Trying to add an item to a folder that does not exist - skipping check'); + return; + } + if (itemIsReadOnlySync(itemType, changeSource, parentFolder, Setting.value('sync.userId'), shareState)) { throw new JoplinError('Cannot add an item as a child of a read-only item', ErrorCode.IsReadOnly); } diff --git a/packages/lib/utils/userFetcher.ts b/packages/lib/utils/userFetcher.ts index 4385fcd59..b004cda79 100644 --- a/packages/lib/utils/userFetcher.ts +++ b/packages/lib/utils/userFetcher.ts @@ -24,6 +24,13 @@ const userFetcher = async () => { const fileApi = await syncTarget.fileApi(); const api = fileApi.driver().api(); + if (api.userId) { + // That can happen if we don't have a session yet or if it has been + // cleared + logger.info('Skipping fetching user because user ID is not available'); + return; + } + const owner: UserApiResponse = await api.exec('GET', `api/users/${api.userId}`); logger.info('Got user:', owner);