pull/6600/head
Laurent Cozic 2022-06-20 17:56:16 +01:00
parent 9047605637
commit a43d4593c5
6 changed files with 244 additions and 201 deletions

View File

@ -351,7 +351,7 @@ export default class JoplinDatabase extends Database {
// must be set in the synchronizer too. // must be set in the synchronizer too.
// Note: v16 and v17 don't do anything. They were used to debug an issue. // Note: v16 and v17 don't do anything. They were used to debug an issue.
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]; const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42];
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion); let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
@ -910,6 +910,10 @@ export default class JoplinDatabase extends Database {
queries.push('ALTER TABLE `folders` ADD COLUMN icon TEXT NOT NULL DEFAULT ""'); queries.push('ALTER TABLE `folders` ADD COLUMN icon TEXT NOT NULL DEFAULT ""');
} }
if (targetVersion == 42) {
queries.push('ALTER TABLE `notes` ADD COLUMN is_shared_recursive INT NOT NULL DEFAULT 0');
}
const updateVersionQuery = { sql: 'UPDATE version SET version = ?', params: [targetVersion] }; const updateVersionQuery = { sql: 'UPDATE version SET version = ?', params: [targetVersion] };
queries.push(updateVersionQuery); queries.push(updateVersionQuery);

View File

@ -348,24 +348,55 @@ export default class Folder extends BaseItem {
} }
public static async updateNoteShareIds() { public static async updateNoteShareIds() {
// Find all the notes where the share_id is not the same as the {
// parent share_id because we only need to update those. // Find all the notes where the share_id is not the same as the
const rows = await this.db().selectAll(` // parent share_id because we only need to update those.
SELECT notes.id, folders.share_id, notes.parent_id const rows = await this.db().selectAll(`
FROM notes SELECT notes.id, folders.share_id, notes.parent_id
LEFT JOIN folders ON notes.parent_id = folders.id FROM notes
WHERE notes.share_id != folders.share_id LEFT JOIN folders ON notes.parent_id = folders.id
`); WHERE notes.share_id != folders.share_id
`);
logger.debug('updateNoteShareIds: notes to update:', rows.length); logger.debug('updateNoteShareIds: notes to update:', rows.length);
for (const row of rows) { for (const row of rows) {
await Note.save({ await Note.save({
id: row.id, id: row.id,
share_id: row.share_id || '', share_id: row.share_id || '',
parent_id: row.parent_id, parent_id: row.parent_id,
updated_time: Date.now(), updated_time: Date.now(),
}, { autoTimestamp: false }); }, { autoTimestamp: false });
}
}
// Also applies recursively published notes
{
// Find all the notes where the share_id is not the same as the
// parent share_id because we only need to update those.
const rows = await this.db().selectAll(`
SELECT notes.id, folders.share_id, notes.parent_id, notes.body
FROM notes
WHERE notes.is_shared = 1 AND notes.is_shared_recursive = 1
`);
logger.debug('updateNoteShareIds: notes recursively published:', rows.length);
// for (const row of rows) {
// // await Note.save({
// // id: row.id,
// // share_id: row.share_id || '',
// // parent_id: row.parent_id,
// // updated_time: Date.now(),
// // }, { autoTimestamp: false });
// }
// TODO: should start from root notes - don't set is_recursive on children?
// TODO: when a child note is moved out of parent, how to know that is_shared should be cleared? - need share_root prop?
// or based on Share API objects? (passed to this function?)
// TODO: also unshare notes that have been unlinked (is_shared != parent.is_shared)
// TODO: avoid infinite loops
} }
} }
@ -625,7 +656,7 @@ export default class Folder extends BaseItem {
rootFolders.push(folder); rootFolders.push(folder);
} else { } else {
if (!idToFolders[folder.parent_id]) { if (!idToFolders[folder.parent_id]) {
// It means the notebook is refering a folder that doesn't exist. In theory it shouldn't happen // It means the notebook is referring a folder that doesn't exist. In theory it shouldn't happen
// but sometimes does - https://github.com/laurent22/joplin/issues/1068#issuecomment-450594708 // but sometimes does - https://github.com/laurent22/joplin/issues/1068#issuecomment-450594708
rootFolders.push(folder); rootFolders.push(folder);
} else { } else {

View File

@ -52,6 +52,10 @@ export const defaultFolderIcon = () => {
// AUTO-GENERATED BY packages/tools/generate-database-types.js // AUTO-GENERATED BY packages/tools/generate-database-types.js
/* /*
@ -59,225 +63,226 @@ export const defaultFolderIcon = () => {
* Rerun sql-ts to regenerate this file. * Rerun sql-ts to regenerate this file.
*/ */
export interface AlarmEntity { export interface AlarmEntity {
"id"?: number | null 'id'?: number | null;
"note_id"?: string 'note_id'?: string;
"trigger_time"?: number 'trigger_time'?: number;
"type_"?: number 'type_'?: number;
} }
export interface DeletedItemEntity { export interface DeletedItemEntity {
"id"?: number | null 'id'?: number | null;
"item_type"?: number 'item_type'?: number;
"item_id"?: string 'item_id'?: string;
"deleted_time"?: number 'deleted_time'?: number;
"sync_target"?: number 'sync_target'?: number;
"type_"?: number 'type_'?: number;
} }
export interface FolderEntity { export interface FolderEntity {
"id"?: string | null 'id'?: string | null;
"title"?: string 'title'?: string;
"created_time"?: number 'created_time'?: number;
"updated_time"?: number 'updated_time'?: number;
"user_created_time"?: number 'user_created_time'?: number;
"user_updated_time"?: number 'user_updated_time'?: number;
"encryption_cipher_text"?: string 'encryption_cipher_text'?: string;
"encryption_applied"?: number 'encryption_applied'?: number;
"parent_id"?: string 'parent_id'?: string;
"is_shared"?: number 'is_shared'?: number;
"share_id"?: string 'share_id'?: string;
"master_key_id"?: string 'master_key_id'?: string;
"icon"?: string 'icon'?: string;
"type_"?: number 'type_'?: number;
} }
export interface ItemChangeEntity { export interface ItemChangeEntity {
"id"?: number | null 'id'?: number | null;
"item_type"?: number 'item_type'?: number;
"item_id"?: string 'item_id'?: string;
"type"?: number 'type'?: number;
"created_time"?: number 'created_time'?: number;
"source"?: number 'source'?: number;
"before_change_item"?: string 'before_change_item'?: string;
"type_"?: number 'type_'?: number;
} }
export interface KeyValueEntity { export interface KeyValueEntity {
"id"?: number | null 'id'?: number | null;
"key"?: string 'key'?: string;
"value"?: string 'value'?: string;
"type"?: number 'type'?: number;
"updated_time"?: number 'updated_time'?: number;
"type_"?: number 'type_'?: number;
} }
export interface MigrationEntity { export interface MigrationEntity {
"id"?: number | null 'id'?: number | null;
"number"?: number 'number'?: number;
"updated_time"?: number 'updated_time'?: number;
"created_time"?: number 'created_time'?: number;
"type_"?: number 'type_'?: number;
} }
export interface NoteResourceEntity { export interface NoteResourceEntity {
"id"?: number | null 'id'?: number | null;
"note_id"?: string 'note_id'?: string;
"resource_id"?: string 'resource_id'?: string;
"is_associated"?: number 'is_associated'?: number;
"last_seen_time"?: number 'last_seen_time'?: number;
"type_"?: number 'type_'?: number;
} }
export interface NoteTagEntity { export interface NoteTagEntity {
"id"?: string | null 'id'?: string | null;
"note_id"?: string 'note_id'?: string;
"tag_id"?: string 'tag_id'?: string;
"created_time"?: number 'created_time'?: number;
"updated_time"?: number 'updated_time'?: number;
"user_created_time"?: number 'user_created_time'?: number;
"user_updated_time"?: number 'user_updated_time'?: number;
"encryption_cipher_text"?: string 'encryption_cipher_text'?: string;
"encryption_applied"?: number 'encryption_applied'?: number;
"is_shared"?: number 'is_shared'?: number;
"type_"?: number 'type_'?: number;
} }
export interface NoteEntity { export interface NoteEntity {
"id"?: string | null 'id'?: string | null;
"parent_id"?: string 'parent_id'?: string;
"title"?: string 'title'?: string;
"body"?: string 'body'?: string;
"created_time"?: number 'created_time'?: number;
"updated_time"?: number 'updated_time'?: number;
"is_conflict"?: number 'is_conflict'?: number;
"latitude"?: number 'latitude'?: number;
"longitude"?: number 'longitude'?: number;
"altitude"?: number 'altitude'?: number;
"author"?: string 'author'?: string;
"source_url"?: string 'source_url'?: string;
"is_todo"?: number 'is_todo'?: number;
"todo_due"?: number 'todo_due'?: number;
"todo_completed"?: number 'todo_completed'?: number;
"source"?: string 'source'?: string;
"source_application"?: string 'source_application'?: string;
"application_data"?: string 'application_data'?: string;
"order"?: number 'order'?: number;
"user_created_time"?: number 'user_created_time'?: number;
"user_updated_time"?: number 'user_updated_time'?: number;
"encryption_cipher_text"?: string 'encryption_cipher_text'?: string;
"encryption_applied"?: number 'encryption_applied'?: number;
"markup_language"?: number 'markup_language'?: number;
"is_shared"?: number 'is_shared'?: number;
"share_id"?: string 'share_id'?: string;
"conflict_original_id"?: string 'conflict_original_id'?: string;
"master_key_id"?: string 'master_key_id'?: string;
"type_"?: number 'is_shared_recursive'?: number;
'type_'?: number;
} }
export interface NotesNormalizedEntity { export interface NotesNormalizedEntity {
"id"?: string 'id'?: string;
"title"?: string 'title'?: string;
"body"?: string 'body'?: string;
"user_created_time"?: number 'user_created_time'?: number;
"user_updated_time"?: number 'user_updated_time'?: number;
"is_todo"?: number 'is_todo'?: number;
"todo_completed"?: number 'todo_completed'?: number;
"parent_id"?: string 'parent_id'?: string;
"latitude"?: number 'latitude'?: number;
"longitude"?: number 'longitude'?: number;
"altitude"?: number 'altitude'?: number;
"source_url"?: string 'source_url'?: string;
"todo_due"?: number 'todo_due'?: number;
"type_"?: number 'type_'?: number;
} }
export interface ResourceLocalStateEntity { export interface ResourceLocalStateEntity {
"id"?: number | null 'id'?: number | null;
"resource_id"?: string 'resource_id'?: string;
"fetch_status"?: number 'fetch_status'?: number;
"fetch_error"?: string 'fetch_error'?: string;
"type_"?: number 'type_'?: number;
} }
export interface ResourceEntity { export interface ResourceEntity {
"id"?: string | null 'id'?: string | null;
"title"?: string 'title'?: string;
"mime"?: string 'mime'?: string;
"filename"?: string 'filename'?: string;
"created_time"?: number 'created_time'?: number;
"updated_time"?: number 'updated_time'?: number;
"user_created_time"?: number 'user_created_time'?: number;
"user_updated_time"?: number 'user_updated_time'?: number;
"file_extension"?: string 'file_extension'?: string;
"encryption_cipher_text"?: string 'encryption_cipher_text'?: string;
"encryption_applied"?: number 'encryption_applied'?: number;
"encryption_blob_encrypted"?: number 'encryption_blob_encrypted'?: number;
"size"?: number 'size'?: number;
"is_shared"?: number 'is_shared'?: number;
"share_id"?: string 'share_id'?: string;
"master_key_id"?: string 'master_key_id'?: string;
"type_"?: number 'type_'?: number;
} }
export interface ResourcesToDownloadEntity { export interface ResourcesToDownloadEntity {
"id"?: number | null 'id'?: number | null;
"resource_id"?: string 'resource_id'?: string;
"updated_time"?: number 'updated_time'?: number;
"created_time"?: number 'created_time'?: number;
"type_"?: number 'type_'?: number;
} }
export interface RevisionEntity { export interface RevisionEntity {
"id"?: string | null 'id'?: string | null;
"parent_id"?: string 'parent_id'?: string;
"item_type"?: number 'item_type'?: number;
"item_id"?: string 'item_id'?: string;
"item_updated_time"?: number 'item_updated_time'?: number;
"title_diff"?: string 'title_diff'?: string;
"body_diff"?: string 'body_diff'?: string;
"metadata_diff"?: string 'metadata_diff'?: string;
"encryption_cipher_text"?: string 'encryption_cipher_text'?: string;
"encryption_applied"?: number 'encryption_applied'?: number;
"updated_time"?: number 'updated_time'?: number;
"created_time"?: number 'created_time'?: number;
"type_"?: number 'type_'?: number;
} }
export interface SettingEntity { export interface SettingEntity {
"key"?: string | null 'key'?: string | null;
"value"?: string | null 'value'?: string | null;
"type_"?: number 'type_'?: number;
} }
export interface SyncItemEntity { export interface SyncItemEntity {
"id"?: number | null 'id'?: number | null;
"sync_target"?: number 'sync_target'?: number;
"sync_time"?: number 'sync_time'?: number;
"item_type"?: number 'item_type'?: number;
"item_id"?: string 'item_id'?: string;
"sync_disabled"?: number 'sync_disabled'?: number;
"sync_disabled_reason"?: string 'sync_disabled_reason'?: string;
"force_sync"?: number 'force_sync'?: number;
"item_location"?: number 'item_location'?: number;
"type_"?: number 'type_'?: number;
} }
export interface TableFieldEntity { export interface TableFieldEntity {
"id"?: number | null 'id'?: number | null;
"table_name"?: string 'table_name'?: string;
"field_name"?: string 'field_name'?: string;
"field_type"?: number 'field_type'?: number;
"field_default"?: string | null 'field_default'?: string | null;
"type_"?: number 'type_'?: number;
} }
export interface TagEntity { export interface TagEntity {
"id"?: string | null 'id'?: string | null;
"title"?: string 'title'?: string;
"created_time"?: number 'created_time'?: number;
"updated_time"?: number 'updated_time'?: number;
"user_created_time"?: number 'user_created_time'?: number;
"user_updated_time"?: number 'user_updated_time'?: number;
"encryption_cipher_text"?: string 'encryption_cipher_text'?: string;
"encryption_applied"?: number 'encryption_applied'?: number;
"is_shared"?: number 'is_shared'?: number;
"parent_id"?: string 'parent_id'?: string;
"type_"?: number 'type_'?: number;
} }
export interface TagsWithNoteCountEntity { export interface TagsWithNoteCountEntity {
"id"?: string | null 'id'?: string | null;
"title"?: string | null 'title'?: string | null;
"created_time"?: number | null 'created_time'?: number | null;
"updated_time"?: number | null 'updated_time'?: number | null;
"note_count"?: any | null 'note_count'?: any | null;
"todo_completed_count"?: any | null 'todo_completed_count'?: any | null;
"type_"?: number 'type_'?: number;
} }
export interface VersionEntity { export interface VersionEntity {
"version"?: number 'version'?: number;
"table_fields_version"?: number 'table_fields_version'?: number;
"type_"?: number 'type_'?: number;
} }

View File

@ -241,6 +241,7 @@ export default class ShareService {
id: note.id, id: note.id,
parent_id: note.parent_id, parent_id: note.parent_id,
is_shared: 1, is_shared: 1,
is_shared_recursive: recursive ? 1 : 0,
updated_time: Date.now(), updated_time: Date.now(),
}, { }, {
autoTimestamp: false, autoTimestamp: false,

View File

@ -27,6 +27,7 @@ export interface StateShare {
folder_id: string; folder_id: string;
note_id: string; note_id: string;
master_key_id: string; master_key_id: string;
recursive: number;
user?: StateShareUserUser; user?: StateShareUserUser;
} }

View File

@ -64,6 +64,7 @@ export default class ShareModel extends BaseModel<Share> {
if (object.owner_id) output.owner_id = object.owner_id; if (object.owner_id) output.owner_id = object.owner_id;
if (object.note_id) output.note_id = object.note_id; if (object.note_id) output.note_id = object.note_id;
if (object.master_key_id) output.master_key_id = object.master_key_id; if (object.master_key_id) output.master_key_id = object.master_key_id;
if ('recursive' in object) output.recursive = object.recursive;
return output; return output;
} }