bookmark-folder.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import { isValidObjectId } from '@growi/core/src/utils/objectid-utils';
  2. import monggoose, {
  3. Types, Document, Model, Schema,
  4. } from 'mongoose';
  5. import { IBookmarkFolder, BookmarkFolderItems } from '~/interfaces/bookmark-info';
  6. import { IPageHasId } from '~/interfaces/page';
  7. import loggerFactory from '../../utils/logger';
  8. import { getOrCreateModel } from '../util/mongoose-utils';
  9. import { InvalidParentBookmarkFolderError } from './errors';
  10. const logger = loggerFactory('growi:models:bookmark-folder');
  11. const Bookmark = monggoose.model('Bookmark');
  12. export interface BookmarkFolderDocument extends Document {
  13. _id: Types.ObjectId
  14. name: string
  15. owner: Types.ObjectId
  16. parent?: [this]
  17. bookmarks?: [Types.ObjectId]
  18. }
  19. export interface BookmarkFolderModel extends Model<BookmarkFolderDocument>{
  20. createByParameters(params: IBookmarkFolder): BookmarkFolderDocument
  21. findFolderAndChildren(user: Types.ObjectId | string, parentId?: Types.ObjectId | string): BookmarkFolderItems[]
  22. findChildFolderById(parentBookmarkFolder: Types.ObjectId | string): Promise<BookmarkFolderDocument[]>
  23. deleteFolderAndChildren(bookmarkFolderId: Types.ObjectId | string): {deletedCount: number}
  24. updateBookmarkFolder(bookmarkFolderId: string, name: string, parent: string): BookmarkFolderDocument | null
  25. insertOrUpdateBookmarkedPage(pageId: IPageHasId, userId: Types.ObjectId | string, folderId: string)
  26. }
  27. const bookmarkFolderSchema = new Schema<BookmarkFolderDocument, BookmarkFolderModel>({
  28. name: { type: String },
  29. owner: { type: Schema.Types.ObjectId, ref: 'User', index: true },
  30. parent: { type: Schema.Types.ObjectId, ref: 'BookmarkFolder', required: false },
  31. bookmarks: {
  32. type: [{
  33. type: Schema.Types.ObjectId, ref: 'Bookmark', required: false,
  34. }],
  35. required: false,
  36. default: [],
  37. },
  38. }, {
  39. toObject: { virtuals: true },
  40. });
  41. bookmarkFolderSchema.virtual('children', {
  42. ref: 'BookmarkFolder',
  43. localField: '_id',
  44. foreignField: 'parent',
  45. });
  46. bookmarkFolderSchema.statics.createByParameters = async function(params: IBookmarkFolder): Promise<BookmarkFolderDocument> {
  47. const { name, owner, parent } = params;
  48. let bookmarkFolder;
  49. if (parent == null) {
  50. bookmarkFolder = await this.create({ name, owner }) as unknown as BookmarkFolderDocument;
  51. }
  52. else {
  53. // Check if parent folder id is valid and parent folder exists
  54. const isParentFolderIdValid = isValidObjectId(parent as string);
  55. if (!isParentFolderIdValid) {
  56. throw new InvalidParentBookmarkFolderError('Parent folder id is invalid');
  57. }
  58. const parentFolder = await this.findById(parent);
  59. if (parentFolder == null) {
  60. throw new InvalidParentBookmarkFolderError('Parent folder not found');
  61. }
  62. bookmarkFolder = await this.create({ name, owner, parent: parentFolder._id }) as unknown as BookmarkFolderDocument;
  63. }
  64. return bookmarkFolder;
  65. };
  66. bookmarkFolderSchema.statics.findFolderAndChildren = async function(
  67. userId: Types.ObjectId | string,
  68. parentId?: Types.ObjectId | string,
  69. ): Promise<BookmarkFolderItems[]> {
  70. const parentFolder = await this.findById(parentId) as unknown as BookmarkFolderDocument;
  71. const bookmarkFolders = await this.find({ owner: userId, parent: parentFolder })
  72. .populate({ path: 'children' })
  73. .populate({
  74. path: 'bookmarks',
  75. model: 'Bookmark',
  76. populate: {
  77. path: 'page',
  78. model: 'Page',
  79. },
  80. }).exec() as unknown as BookmarkFolderItems[];
  81. return bookmarkFolders;
  82. };
  83. bookmarkFolderSchema.statics.findChildFolderById = async function(parentFolderId: Types.ObjectId | string): Promise<BookmarkFolderDocument[]> {
  84. const parentFolder = await this.findById(parentFolderId) as unknown as BookmarkFolderDocument;
  85. const childFolders = await this.find({ parent: parentFolder });
  86. return childFolders;
  87. };
  88. bookmarkFolderSchema.statics.deleteFolderAndChildren = async function(boookmarkFolderId: Types.ObjectId | string): Promise<{deletedCount: number}> {
  89. // Delete parent and all children folder
  90. const bookmarkFolder = await this.findByIdAndDelete(boookmarkFolderId);
  91. let deletedCount = 0;
  92. if (bookmarkFolder != null) {
  93. // Delete all child recursively and update deleted count
  94. const childFolders = await this.find({ parent: bookmarkFolder });
  95. await Promise.all(childFolders.map(async(child) => {
  96. const deletedChildFolder = await this.deleteFolderAndChildren(child._id);
  97. deletedCount += deletedChildFolder.deletedCount;
  98. }));
  99. const deletedChild = await this.deleteMany({ parent: bookmarkFolder });
  100. deletedCount += deletedChild.deletedCount + 1;
  101. }
  102. return { deletedCount };
  103. };
  104. bookmarkFolderSchema.statics.updateBookmarkFolder = async function(bookmarkFolderId: string, name: string, parent: string):
  105. Promise<BookmarkFolderDocument | null> {
  106. const parentFolder = await this.findById(parent);
  107. const updateFields = {
  108. name, parent: parentFolder?._id || null,
  109. };
  110. const bookmarkFolder = await this.findByIdAndUpdate(bookmarkFolderId, { $set: updateFields }, { new: true });
  111. return bookmarkFolder;
  112. };
  113. bookmarkFolderSchema.statics.insertOrUpdateBookmarkedPage = async function(pageId: IPageHasId, userId: Types.ObjectId | string, folderId: string):
  114. Promise<BookmarkFolderDocument | null> {
  115. const bookmarkedPage = await Bookmark.findOneAndUpdate({ page: pageId, user: userId }, { page: pageId, user: userId }, { new: true, upsert: true });
  116. const bookmarkFolder = await this.findByIdAndUpdate(folderId, { $addToSet: { bookmarks: bookmarkedPage } }, { new: true, upsert: true });
  117. return bookmarkFolder;
  118. };
  119. export default getOrCreateModel<BookmarkFolderDocument, BookmarkFolderModel>('BookmarkFolder', bookmarkFolderSchema);