| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- const { customTagUtils } = require('@growi/core');
- const { OptionParser } = customTagUtils;
- const DEFAULT_PAGES_NUM = 50;
- class Lsx {
- /**
- * add depth condition that limit fetched pages
- *
- * @static
- * @param {any} query
- * @param {any} pagePath
- * @param {any} optionsDepth
- * @returns
- *
- * @memberOf Lsx
- */
- static addDepthCondition(query, pagePath, optionsDepth) {
- // when option strings is 'depth=', the option value is true
- if (optionsDepth == null || optionsDepth === true) {
- throw new Error('The value of depth option is invalid.');
- }
- const range = OptionParser.parseRange(optionsDepth);
- const start = range.start;
- const end = range.end;
- if (start < 1 || end < 1) {
- throw new Error(`specified depth is [${start}:${end}] : start and end are must be larger than 1`);
- }
- // count slash
- const slashNum = pagePath.split('/').length - 1;
- const depthStart = slashNum; // start is not affect to fetch page
- const depthEnd = slashNum + end - 1;
- return query.and({
- path: new RegExp(`^(\\/[^\\/]*){${depthStart},${depthEnd}}$`),
- });
- }
- /**
- * add num condition that limit fetched pages
- *
- * @static
- * @param {any} query
- * @param {any} pagePath
- * @param {number|string} optionsNum
- * @returns
- *
- * @memberOf Lsx
- */
- static addNumCondition(query, pagePath, optionsNum) {
- // when option strings is 'num=', the option value is true
- if (optionsNum == null || optionsNum === true) {
- throw new Error('The value of num option is invalid.');
- }
- if (typeof optionsNum === 'number') {
- return query.limit(optionsNum);
- }
- const range = OptionParser.parseRange(optionsNum);
- const start = range.start;
- const end = range.end;
- if (start < 1 || end < 1) {
- throw new Error(`specified num is [${start}:${end}] : start and end are must be larger than 1`);
- }
- const skip = start - 1;
- const limit = end - skip;
- return query.skip(skip).limit(limit);
- }
- /**
- * add filter condition that filter fetched pages
- *
- * @static
- * @param {any} query
- * @param {any} pagePath
- * @param {any} optionsFilter
- * @param {boolean} isExceptFilter
- * @returns
- *
- * @memberOf Lsx
- */
- static addFilterCondition(query, pagePath, optionsFilter, isExceptFilter = false) {
- // when option strings is 'filter=', the option value is true
- if (optionsFilter == null || optionsFilter === true) {
- throw new Error('filter option require value in regular expression.');
- }
- let filterPath = '';
- if (optionsFilter.charAt(0) === '^') {
- // move '^' to the first of path
- filterPath = new RegExp(`^${pagePath}${optionsFilter.slice(1, optionsFilter.length)}`);
- }
- else {
- filterPath = new RegExp(`^${pagePath}.*${optionsFilter}`);
- }
- if (isExceptFilter) {
- return query.and({
- path: { $not: filterPath },
- });
- }
- return query.and({
- path: filterPath,
- });
- }
- static addExceptCondition(query, pagePath, optionsFilter) {
- return this.addFilterCondition(query, pagePath, optionsFilter, true);
- }
- /**
- * add sort condition(sort key & sort order)
- *
- * If only the reverse option is specified, the sort key is 'path'.
- * If only the sort key is specified, the sort order is the ascending order.
- *
- * @static
- * @param {any} query
- * @param {string} pagePath
- * @param {string} optionsSort
- * @param {string} optionsReverse
- * @returns
- *
- * @memberOf Lsx
- */
- static addSortCondition(query, pagePath, optionsSortArg, optionsReverse) {
- // init sort key
- const optionsSort = optionsSortArg ?? 'path';
- // the default sort order
- const isReversed = optionsReverse === 'true';
- if (optionsSort !== 'path' && optionsSort !== 'createdAt' && optionsSort !== 'updatedAt') {
- throw new Error(`The specified value '${optionsSort}' for the sort option is invalid. It must be 'path', 'createdAt' or 'updatedAt'.`);
- }
- const sortOption = {};
- sortOption[optionsSort] = isReversed ? -1 : 1;
- return query.sort(sortOption);
- }
- }
- module.exports = (crowi, app) => {
- const Page = crowi.model('Page');
- const ApiResponse = crowi.require('../util/apiResponse');
- const actions = {};
- /**
- *
- * @param {*} pagePath
- * @param {*} user
- *
- * @return {Promise<Query>} query
- */
- async function generateBaseQueryBuilder(pagePath, user) {
- let baseQuery = Page.find();
- if (Page.PageQueryBuilder == null) {
- if (Page.generateQueryToListWithDescendants != null) { // for Backward compatibility (<= GROWI v3.2.x)
- baseQuery = Page.generateQueryToListWithDescendants(pagePath, user, {});
- }
- else if (Page.generateQueryToListByStartWith != null) { // for Backward compatibility (<= crowi-plus v2.0.7)
- baseQuery = Page.generateQueryToListByStartWith(pagePath, user, {});
- }
- // return dummy PageQueryBuilder object
- return Promise.resolve({ query: baseQuery });
- }
- const builder = new Page.PageQueryBuilder(baseQuery);
- if (builder.addConditionToListOnlyDescendants == null) { // for Backward compatibility (<= GROWI v4.0.x)
- builder.addConditionToListWithDescendants(pagePath);
- }
- else {
- builder.addConditionToListOnlyDescendants(pagePath);
- }
- builder
- .addConditionToExcludeTrashed();
- return Page.addConditionToFilteringByViewerForList(builder, user);
- }
- actions.listPages = async(req, res) => {
- const user = req.user;
- let pagePath;
- let options;
- try {
- pagePath = req.query.pagePath;
- options = JSON.parse(req.query.options);
- }
- catch (error) {
- return res.status(400).send(error);
- }
- const builder = await generateBaseQueryBuilder(pagePath, user);
- // count viewers of `/`
- let toppageViewersCount;
- try {
- const aggRes = await Page.aggregate([
- { $match: { path: '/' } },
- { $project: { count: { $size: '$seenUsers' } } },
- ]);
- toppageViewersCount = aggRes.length > 0
- ? aggRes[0].count
- : 1;
- }
- catch (error) {
- return res.status(500).send(error);
- }
- let query = builder.query;
- try {
- // depth
- if (options.depth != null) {
- query = Lsx.addDepthCondition(query, pagePath, options.depth);
- }
- // filter
- if (options.filter != null) {
- query = Lsx.addFilterCondition(query, pagePath, options.filter);
- }
- if (options.except != null) {
- query = Lsx.addExceptCondition(query, pagePath, options.except);
- }
- // num
- const optionsNum = options.num || DEFAULT_PAGES_NUM;
- query = Lsx.addNumCondition(query, pagePath, optionsNum);
- // sort
- query = Lsx.addSortCondition(query, pagePath, options.sort, options.reverse);
- const pages = await query.exec();
- res.status(200).send({ pages, toppageViewersCount });
- }
- catch (error) {
- return res.status(500).send(error);
- }
- };
- return actions;
- };
|