|
|
@@ -21,7 +21,7 @@ import { getPageSchema, extractToAncestorsPaths, populateDataToShowRevision } fr
|
|
|
import { PageRedirectModel } from './page-redirect';
|
|
|
|
|
|
const { addTrailingSlash, normalizePath } = pathUtils;
|
|
|
-const { isTopPage, collectAncestorPaths } = pagePathUtils;
|
|
|
+const { isTopPage, collectAncestorPaths, hasSlash } = pagePathUtils;
|
|
|
|
|
|
const logger = loggerFactory('growi:models:page');
|
|
|
/*
|
|
|
@@ -59,8 +59,6 @@ export interface PageModel extends Model<PageDocument> {
|
|
|
findByIdsAndViewer(pageIds: ObjectIdLike[], user, userGroups?, includeEmpty?: boolean): Promise<PageDocument[]>
|
|
|
findByPathAndViewer(path: string | null, user, userGroups?, useFindOne?: boolean, includeEmpty?: boolean): Promise<PageDocument | PageDocument[] | null>
|
|
|
findTargetAndAncestorsByPathOrId(pathOrId: string): Promise<TargetAndAncestorsResult>
|
|
|
- findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups?): Promise<PageDocument[]>
|
|
|
- findAncestorsChildrenByPathAndViewer(path: string, user, userGroups?): Promise<Record<string, PageDocument[]>>
|
|
|
findRecentUpdatedPages(path: string, user, option, includeEmpty?: boolean): Promise<PaginatedPages>
|
|
|
generateGrantCondition(
|
|
|
user, userGroups, showAnyoneKnowsLink?: boolean, showPagesRestrictedByOwner?: boolean, showPagesRestrictedByGroup?: boolean,
|
|
|
@@ -116,22 +114,6 @@ const schema = new Schema<PageDocument, PageModel>({
|
|
|
schema.plugin(mongoosePaginate);
|
|
|
schema.plugin(uniqueValidator);
|
|
|
|
|
|
-const hasSlash = (str: string): boolean => {
|
|
|
- return str.includes('/');
|
|
|
-};
|
|
|
-
|
|
|
-/*
|
|
|
- * Generate RegExp instance for one level lower path
|
|
|
- */
|
|
|
-const generateChildrenRegExp = (path: string): RegExp => {
|
|
|
- // https://regex101.com/r/laJGzj/1
|
|
|
- // ex. /any_level1
|
|
|
- if (isTopPage(path)) return new RegExp(/^\/[^/]+$/);
|
|
|
-
|
|
|
- // https://regex101.com/r/mrDJrx/1
|
|
|
- // ex. /parent/any_child OR /any_level1
|
|
|
- return new RegExp(`^${path}(\\/[^/]+)\\/?$`);
|
|
|
-};
|
|
|
|
|
|
export class PageQueryBuilder {
|
|
|
|
|
|
@@ -363,6 +345,18 @@ export class PageQueryBuilder {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ // add viewer condition to PageQueryBuilder instance
|
|
|
+ async addViewerCondition(user, userGroups = null): Promise<PageQueryBuilder> {
|
|
|
+ let relatedUserGroups = userGroups;
|
|
|
+ if (user != null && relatedUserGroups == null) {
|
|
|
+ const UserGroupRelation: any = mongoose.model('UserGroupRelation');
|
|
|
+ relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.addConditionToFilteringByViewer(user, relatedUserGroups, false);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
addConditionToFilteringByViewer(user, userGroups, showAnyoneKnowsLink = false, showPagesRestrictedByOwner = false, showPagesRestrictedByGroup = false) {
|
|
|
const condition = generateGrantCondition(user, userGroups, showAnyoneKnowsLink, showPagesRestrictedByOwner, showPagesRestrictedByGroup);
|
|
|
|
|
|
@@ -667,17 +661,6 @@ schema.statics.getParentAndFillAncestors = async function(path: string, user): P
|
|
|
return createdParent;
|
|
|
};
|
|
|
|
|
|
-// Utility function to add viewer condition to PageQueryBuilder instance
|
|
|
-const addViewerCondition = async(queryBuilder: PageQueryBuilder, user, userGroups = null): Promise<void> => {
|
|
|
- let relatedUserGroups = userGroups;
|
|
|
- if (user != null && relatedUserGroups == null) {
|
|
|
- const UserGroupRelation: any = mongoose.model('UserGroupRelation');
|
|
|
- relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
|
|
|
- }
|
|
|
-
|
|
|
- queryBuilder.addConditionToFilteringByViewer(user, relatedUserGroups, false);
|
|
|
-};
|
|
|
-
|
|
|
/*
|
|
|
* Find pages by ID and viewer.
|
|
|
*/
|
|
|
@@ -685,7 +668,7 @@ schema.statics.findByIdsAndViewer = async function(pageIds: string[], user, user
|
|
|
const baseQuery = this.find({ _id: { $in: pageIds } });
|
|
|
const queryBuilder = new PageQueryBuilder(baseQuery, includeEmpty);
|
|
|
|
|
|
- await addViewerCondition(queryBuilder, user, userGroups);
|
|
|
+ await queryBuilder.addViewerCondition(user, userGroups);
|
|
|
|
|
|
return queryBuilder.query.exec();
|
|
|
};
|
|
|
@@ -703,7 +686,7 @@ schema.statics.findByPathAndViewer = async function(
|
|
|
const baseQuery = useFindOne ? this.findOne({ path }) : this.find({ path });
|
|
|
const queryBuilder = new PageQueryBuilder(baseQuery, includeEmpty);
|
|
|
|
|
|
- await addViewerCondition(queryBuilder, user, userGroups);
|
|
|
+ await queryBuilder.addViewerCondition(user, userGroups);
|
|
|
|
|
|
return queryBuilder.query.exec();
|
|
|
};
|
|
|
@@ -730,7 +713,7 @@ schema.statics.findRecentUpdatedPages = async function(
|
|
|
|
|
|
queryBuilder.addConditionToListWithDescendants(path, options);
|
|
|
queryBuilder.populateDataToList(User.USER_FIELDS_EXCEPT_CONFIDENTIAL);
|
|
|
- await addViewerCondition(queryBuilder, user);
|
|
|
+ await queryBuilder.addViewerCondition(user);
|
|
|
const pages = await Page.paginate(queryBuilder.query.clone(), {
|
|
|
lean: true, sort: sortOpt, offset: options.offset, limit: options.limit,
|
|
|
});
|
|
|
@@ -763,7 +746,7 @@ schema.statics.findTargetAndAncestorsByPathOrId = async function(pathOrId: strin
|
|
|
|
|
|
// Do not populate
|
|
|
const queryBuilder = new PageQueryBuilder(this.find(), true);
|
|
|
- await addViewerCondition(queryBuilder, user, userGroups);
|
|
|
+ await queryBuilder.addViewerCondition(user, userGroups);
|
|
|
|
|
|
const _targetAndAncestors: PageDocument[] = await queryBuilder
|
|
|
.addConditionAsMigrated()
|
|
|
@@ -783,67 +766,6 @@ schema.statics.findTargetAndAncestorsByPathOrId = async function(pathOrId: strin
|
|
|
return { targetAndAncestors, rootPage };
|
|
|
};
|
|
|
|
|
|
-/*
|
|
|
- * Find all children by parent's path or id. Using id should be prioritized
|
|
|
- */
|
|
|
-schema.statics.findChildrenByParentPathOrIdAndViewer = async function(parentPathOrId: string, user, userGroups = null): Promise<PageDocument[]> {
|
|
|
- let queryBuilder: PageQueryBuilder;
|
|
|
- if (hasSlash(parentPathOrId)) {
|
|
|
- const path = parentPathOrId;
|
|
|
- const regexp = generateChildrenRegExp(path);
|
|
|
- queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp } }), true);
|
|
|
- }
|
|
|
- else {
|
|
|
- const parentId = parentPathOrId;
|
|
|
- queryBuilder = new PageQueryBuilder(this.find({ parent: parentId } as any), true); // TODO: improve type
|
|
|
- }
|
|
|
- await addViewerCondition(queryBuilder, user, userGroups);
|
|
|
-
|
|
|
- return queryBuilder
|
|
|
- .addConditionToSortPagesByAscPath()
|
|
|
- .query
|
|
|
- .lean()
|
|
|
- .exec();
|
|
|
-};
|
|
|
-
|
|
|
-schema.statics.findAncestorsChildrenByPathAndViewer = async function(path: string, user, userGroups = null): Promise<Record<string, PageDocument[]>> {
|
|
|
- const ancestorPaths = isTopPage(path) ? ['/'] : collectAncestorPaths(path); // root path is necessary for rendering
|
|
|
- const regexps = ancestorPaths.map(path => new RegExp(generateChildrenRegExp(path))); // cannot use re2
|
|
|
-
|
|
|
- // get pages at once
|
|
|
- const queryBuilder = new PageQueryBuilder(this.find({ path: { $in: regexps } }), true);
|
|
|
- await addViewerCondition(queryBuilder, user, userGroups);
|
|
|
- const _pages = await queryBuilder
|
|
|
- .addConditionAsMigrated()
|
|
|
- .addConditionToMinimizeDataForRendering()
|
|
|
- .addConditionToSortPagesByAscPath()
|
|
|
- .query
|
|
|
- .lean()
|
|
|
- .exec();
|
|
|
- // mark target
|
|
|
- const pages = _pages.map((page: PageDocument & { isTarget?: boolean }) => {
|
|
|
- if (page.path === path) {
|
|
|
- page.isTarget = true;
|
|
|
- }
|
|
|
- return page;
|
|
|
- });
|
|
|
-
|
|
|
- /*
|
|
|
- * If any non-migrated page is found during creating the pathToChildren map, it will stop incrementing at that moment
|
|
|
- */
|
|
|
- const pathToChildren: Record<string, PageDocument[]> = {};
|
|
|
- const sortedPaths = ancestorPaths.sort((a, b) => a.length - b.length); // sort paths by path.length
|
|
|
- sortedPaths.every((path) => {
|
|
|
- const children = pages.filter(page => nodePath.dirname(page.path) === path);
|
|
|
- if (children.length === 0) {
|
|
|
- return false; // break when children do not exist
|
|
|
- }
|
|
|
- pathToChildren[path] = children;
|
|
|
- return true;
|
|
|
- });
|
|
|
-
|
|
|
- return pathToChildren;
|
|
|
-};
|
|
|
|
|
|
/*
|
|
|
* Utils from obsolete-page.js
|