|
@@ -344,7 +344,7 @@ module.exports = function(crowi) {
|
|
|
grantLabels[GRANT_PUBLIC] = 'Public'; // 公開
|
|
grantLabels[GRANT_PUBLIC] = 'Public'; // 公開
|
|
|
grantLabels[GRANT_RESTRICTED] = 'Anyone with the link'; // リンクを知っている人のみ
|
|
grantLabels[GRANT_RESTRICTED] = 'Anyone with the link'; // リンクを知っている人のみ
|
|
|
//grantLabels[GRANT_SPECIFIED] = 'Specified users only'; // 特定ユーザーのみ
|
|
//grantLabels[GRANT_SPECIFIED] = 'Specified users only'; // 特定ユーザーのみ
|
|
|
- // grantLabels[GRANT_USER_GROUP] = 'Only inside the group'; // 特定グループのみ
|
|
|
|
|
|
|
+ grantLabels[GRANT_USER_GROUP] = 'Only inside the group'; // 特定グループのみ
|
|
|
grantLabels[GRANT_OWNER] = 'Just me'; // 自分のみ
|
|
grantLabels[GRANT_OWNER] = 'Just me'; // 自分のみ
|
|
|
|
|
|
|
|
return grantLabels;
|
|
return grantLabels;
|
|
@@ -507,8 +507,8 @@ module.exports = function(crowi) {
|
|
|
|
|
|
|
|
if (!pageData.isGrantedFor(userData)) {
|
|
if (!pageData.isGrantedFor(userData)) {
|
|
|
PageGroupRelation.isExistsGrantedGroupForPageAndUser(pageData, userData)
|
|
PageGroupRelation.isExistsGrantedGroupForPageAndUser(pageData, userData)
|
|
|
- .then(function(checkResult) {
|
|
|
|
|
- if (!checkResult) {
|
|
|
|
|
|
|
+ .then(isExists => {
|
|
|
|
|
+ if (!isExists) {
|
|
|
return reject(new Error('Page is not granted for the user')); //PAGE_GRANT_ERROR, null);
|
|
return reject(new Error('Page is not granted for the user')); //PAGE_GRANT_ERROR, null);
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
@@ -527,6 +527,7 @@ module.exports = function(crowi) {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+<<<<<<< HEAD
|
|
|
// check if a given page has a local and global tempalte
|
|
// check if a given page has a local and global tempalte
|
|
|
pageSchema.statics.checkIfTemplatesExist = function(path) {
|
|
pageSchema.statics.checkIfTemplatesExist = function(path) {
|
|
|
const Page = this;
|
|
const Page = this;
|
|
@@ -554,7 +555,7 @@ module.exports = function(crowi) {
|
|
|
const Page = this;
|
|
const Page = this;
|
|
|
const templatePath = cutOffLastSlash(path);
|
|
const templatePath = cutOffLastSlash(path);
|
|
|
const pathList = generatePathsOnTree(templatePath, []);
|
|
const pathList = generatePathsOnTree(templatePath, []);
|
|
|
- const regexpList = pathList.map(path => new RegExp(`${path}/_{1,2}template`));
|
|
|
|
|
|
|
+ const regexpList = pathList.map(path => new RegExp(`^${path}/_{1,2}template$`));
|
|
|
|
|
|
|
|
return Page
|
|
return Page
|
|
|
.find({path: {$in: regexpList}})
|
|
.find({path: {$in: regexpList}})
|
|
@@ -576,6 +577,10 @@ module.exports = function(crowi) {
|
|
|
|
|
|
|
|
pathList.push(path);
|
|
pathList.push(path);
|
|
|
const newPath = cutOffLastSlash(path);
|
|
const newPath = cutOffLastSlash(path);
|
|
|
|
|
+<<<<<<< HEAD
|
|
|
|
|
+=======
|
|
|
|
|
+
|
|
|
|
|
+>>>>>>> master
|
|
|
return generatePathsOnTree(newPath, pathList);
|
|
return generatePathsOnTree(newPath, pathList);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -605,7 +610,7 @@ module.exports = function(crowi) {
|
|
|
let templateBody;
|
|
let templateBody;
|
|
|
/**
|
|
/**
|
|
|
* get local template
|
|
* get local template
|
|
|
- * @tempate: applicable only to immediate decendants
|
|
|
|
|
|
|
+ * __tempate: applicable only to immediate decendants
|
|
|
*/
|
|
*/
|
|
|
const localTemplate = assignTemplateByType(templates, templatePath, '__');
|
|
const localTemplate = assignTemplateByType(templates, templatePath, '__');
|
|
|
|
|
|
|
@@ -627,17 +632,10 @@ module.exports = function(crowi) {
|
|
|
|
|
|
|
|
// find page by path
|
|
// find page by path
|
|
|
pageSchema.statics.findPageByPath = function(path) {
|
|
pageSchema.statics.findPageByPath = function(path) {
|
|
|
- var Page = this;
|
|
|
|
|
-
|
|
|
|
|
- return new Promise(function(resolve, reject) {
|
|
|
|
|
- Page.findOne({path: path}, function(err, pageData) {
|
|
|
|
|
- if (err || pageData === null) {
|
|
|
|
|
- return reject(err);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return resolve(pageData);
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ if (path == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return this.findOne({path});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
pageSchema.statics.findListByPageIds = function(ids, options) {
|
|
pageSchema.statics.findListByPageIds = function(ids, options) {
|
|
@@ -865,6 +863,7 @@ module.exports = function(crowi) {
|
|
|
{grant: GRANT_RESTRICTED, grantedUsers: userData._id},
|
|
{grant: GRANT_RESTRICTED, grantedUsers: userData._id},
|
|
|
{grant: GRANT_SPECIFIED, grantedUsers: userData._id},
|
|
{grant: GRANT_SPECIFIED, grantedUsers: userData._id},
|
|
|
{grant: GRANT_OWNER, grantedUsers: userData._id},
|
|
{grant: GRANT_OWNER, grantedUsers: userData._id},
|
|
|
|
|
+ {grant: GRANT_USER_GROUP},
|
|
|
], })
|
|
], })
|
|
|
.and({
|
|
.and({
|
|
|
$or: pathCondition
|
|
$or: pathCondition
|
|
@@ -899,10 +898,11 @@ module.exports = function(crowi) {
|
|
|
pageSchema.statics.updateGrant = function(page, grant, userData, grantUserGroupId) {
|
|
pageSchema.statics.updateGrant = function(page, grant, userData, grantUserGroupId) {
|
|
|
var Page = this;
|
|
var Page = this;
|
|
|
|
|
|
|
|
- if (grant == GRANT_USER_GROUP && grantUserGroupId == null) {
|
|
|
|
|
- throw new Error('grant userGroupId is not specified');
|
|
|
|
|
- }
|
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
return new Promise(function(resolve, reject) {
|
|
|
|
|
+ if (grant == GRANT_USER_GROUP && grantUserGroupId == null) {
|
|
|
|
|
+ reject('grant userGroupId is not specified');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
page.grant = grant;
|
|
page.grant = grant;
|
|
|
if (grant == GRANT_PUBLIC || grant == GRANT_USER_GROUP) {
|
|
if (grant == GRANT_PUBLIC || grant == GRANT_USER_GROUP) {
|
|
|
page.grantedUsers = [];
|
|
page.grantedUsers = [];
|
|
@@ -936,12 +936,12 @@ module.exports = function(crowi) {
|
|
|
return UserGroupRelation.findByGroupIdAndUser(grantUserGroupId, userData)
|
|
return UserGroupRelation.findByGroupIdAndUser(grantUserGroupId, userData)
|
|
|
.then((relation) => {
|
|
.then((relation) => {
|
|
|
if (relation == null) {
|
|
if (relation == null) {
|
|
|
- return reject(new Error('no relations were exist for group and user.'));
|
|
|
|
|
|
|
+ return new Error('no relations were exist for group and user.');
|
|
|
}
|
|
}
|
|
|
return PageGroupRelation.findOrCreateRelationForPageAndGroup(page, relation.relatedGroup);
|
|
return PageGroupRelation.findOrCreateRelationForPageAndGroup(page, relation.relatedGroup);
|
|
|
})
|
|
})
|
|
|
.catch((err) => {
|
|
.catch((err) => {
|
|
|
- return reject(new Error('No UserGroup is exists. userGroupId : ', grantUserGroupId));
|
|
|
|
|
|
|
+ return new Error('No UserGroup is exists. userGroupId : ', grantUserGroupId);
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
@@ -1014,10 +1014,11 @@ module.exports = function(crowi) {
|
|
|
grant = GRANT_PUBLIC;
|
|
grant = GRANT_PUBLIC;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return new Promise(function(resolve, reject) {
|
|
|
|
|
- Page.findOne({path: path}, function(err, pageData) {
|
|
|
|
|
|
|
+ let savedPage = undefined;
|
|
|
|
|
+ return Page.findOne({path: path})
|
|
|
|
|
+ .then(pageData => {
|
|
|
if (pageData) {
|
|
if (pageData) {
|
|
|
- return reject(new Error('Cannot create new page to existed path'));
|
|
|
|
|
|
|
+ throw new Error('Cannot create new page to existed path');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var newPage = new Page();
|
|
var newPage = new Page();
|
|
@@ -1032,28 +1033,22 @@ module.exports = function(crowi) {
|
|
|
newPage.grantedUsers = [];
|
|
newPage.grantedUsers = [];
|
|
|
newPage.grantedUsers.push(user);
|
|
newPage.grantedUsers.push(user);
|
|
|
|
|
|
|
|
- newPage.save(function(err, newPage) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- return reject(err);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (newPage.grant == Page.GRANT_USER_GROUP && grantUserGroupId != null) {
|
|
|
|
|
- Page.updateGrantUserGroup(newPage, grant, grantUserGroupId, user)
|
|
|
|
|
- .catch((err) => {
|
|
|
|
|
- return reject(err);
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- var newRevision = Revision.prepareRevision(newPage, body, user, {format: format});
|
|
|
|
|
- Page.pushRevision(newPage, newRevision, user).then(function(data) {
|
|
|
|
|
- resolve(data);
|
|
|
|
|
- pageEvent.emit('create', data, user);
|
|
|
|
|
- }).catch(function(err) {
|
|
|
|
|
- debug('Push Revision Error on create page', err);
|
|
|
|
|
- return reject(err);
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ return newPage.save();
|
|
|
|
|
+ })
|
|
|
|
|
+ .then((newPage) => {
|
|
|
|
|
+ savedPage = newPage;
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ const newRevision = Revision.prepareRevision(savedPage, body, user, {format: format});
|
|
|
|
|
+ return Page.pushRevision(savedPage, newRevision, user);
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ return Page.updateGrantUserGroup(savedPage, grant, grantUserGroupId, user);
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ pageEvent.emit('create', savedPage, user);
|
|
|
|
|
+ return savedPage;
|
|
|
});
|
|
});
|
|
|
- });
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
pageSchema.statics.updatePage = function(pageData, body, user, options) {
|
|
pageSchema.statics.updatePage = function(pageData, body, user, options) {
|
|
@@ -1065,25 +1060,23 @@ module.exports = function(crowi) {
|
|
|
// update existing page
|
|
// update existing page
|
|
|
var newRevision = Revision.prepareRevision(pageData, body, user);
|
|
var newRevision = Revision.prepareRevision(pageData, body, user);
|
|
|
|
|
|
|
|
- return new Promise(function(resolve, reject) {
|
|
|
|
|
- Page.pushRevision(pageData, newRevision, user)
|
|
|
|
|
- .then(function(revision) {
|
|
|
|
|
- if (grant != pageData.grant) {
|
|
|
|
|
- return Page.updateGrant(pageData, grant, user, grantUserGroupId).then(function(data) {
|
|
|
|
|
- debug('Page grant update:', data);
|
|
|
|
|
- resolve(data);
|
|
|
|
|
- pageEvent.emit('update', data, user);
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- resolve(pageData);
|
|
|
|
|
- pageEvent.emit('update', pageData, user);
|
|
|
|
|
- }
|
|
|
|
|
- }).catch(function(err) {
|
|
|
|
|
- debug('Error on update', err);
|
|
|
|
|
- debug('Error on update', err.stack);
|
|
|
|
|
|
|
+ let savedPage = undefined;
|
|
|
|
|
+ return Page.pushRevision(pageData, newRevision, user)
|
|
|
|
|
+ .then((revision) => {
|
|
|
|
|
+ // fetch Page
|
|
|
|
|
+ return Page.findPageByPath(revision.path).populate('revision');
|
|
|
|
|
+ })
|
|
|
|
|
+ .then((page) => {
|
|
|
|
|
+ savedPage = page;
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ return Page.updateGrant(savedPage, grant, user, grantUserGroupId);
|
|
|
|
|
+ })
|
|
|
|
|
+ .then((data) => {
|
|
|
|
|
+ debug('Page grant update:', data);
|
|
|
|
|
+ pageEvent.emit('update', savedPage, user);
|
|
|
|
|
+ return savedPage;
|
|
|
});
|
|
});
|
|
|
- });
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
pageSchema.statics.deletePage = function(pageData, user, options) {
|
|
pageSchema.statics.deletePage = function(pageData, user, options) {
|
|
@@ -1091,20 +1084,13 @@ module.exports = function(crowi) {
|
|
|
, newPath = Page.getDeletedPageName(pageData.path)
|
|
, newPath = Page.getDeletedPageName(pageData.path)
|
|
|
;
|
|
;
|
|
|
if (Page.isDeletableName(pageData.path)) {
|
|
if (Page.isDeletableName(pageData.path)) {
|
|
|
- return new Promise(function(resolve, reject) {
|
|
|
|
|
- Page.updatePageProperty(pageData, {status: STATUS_DELETED, lastUpdateUser: user})
|
|
|
|
|
- .then(function(data) {
|
|
|
|
|
- pageData.status = STATUS_DELETED;
|
|
|
|
|
-
|
|
|
|
|
- // ページ名が /trash/ 以下に存在する場合、おかしなことになる
|
|
|
|
|
- // が、 /trash 以下にページが有るのは、個別に作っていたケースのみ。
|
|
|
|
|
- // 一応しばらく前から uncreatable pages になっているのでこれでいいことにする
|
|
|
|
|
- debug('Deleted the page, and rename it', pageData.path, newPath);
|
|
|
|
|
- return Page.rename(pageData, newPath, user, {createRedirectPage: true});
|
|
|
|
|
- }).then(function(pageData) {
|
|
|
|
|
- resolve(pageData);
|
|
|
|
|
- }).catch(reject);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ return Page.rename(pageData, newPath, user, {createRedirectPage: true})
|
|
|
|
|
+ .then((updatedPageData) => {
|
|
|
|
|
+ return Page.updatePageProperty(updatedPageData, {status: STATUS_DELETED, lastUpdateUser: user});
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ return pageData;
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
else {
|
|
else {
|
|
|
return Promise.reject('Page is not deletable.');
|
|
return Promise.reject('Page is not deletable.');
|
|
@@ -1117,18 +1103,15 @@ module.exports = function(crowi) {
|
|
|
, options = options || {}
|
|
, options = options || {}
|
|
|
;
|
|
;
|
|
|
|
|
|
|
|
- return new Promise(function(resolve, reject) {
|
|
|
|
|
- Page
|
|
|
|
|
- .generateQueryToListWithDescendants(path, user, options)
|
|
|
|
|
|
|
+ return Page.generateQueryToListWithDescendants(path, user, options)
|
|
|
.then(function(pages) {
|
|
.then(function(pages) {
|
|
|
- Promise.all(pages.map(function(page) {
|
|
|
|
|
|
|
+ return Promise.all(pages.map(function(page) {
|
|
|
return Page.deletePage(page, user, options);
|
|
return Page.deletePage(page, user, options);
|
|
|
- }))
|
|
|
|
|
- .then(function(data) {
|
|
|
|
|
- return resolve(pageData);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ }));
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(function(data) {
|
|
|
|
|
+ return pageData;
|
|
|
});
|
|
});
|
|
|
- });
|
|
|
|
|
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -1171,6 +1154,7 @@ module.exports = function(crowi) {
|
|
|
return new Promise(function(resolve, reject) {
|
|
return new Promise(function(resolve, reject) {
|
|
|
Page
|
|
Page
|
|
|
.generateQueryToListWithDescendants(path, user, options)
|
|
.generateQueryToListWithDescendants(path, user, options)
|
|
|
|
|
+ .exec()
|
|
|
.then(function(pages) {
|
|
.then(function(pages) {
|
|
|
Promise.all(pages.map(function(page) {
|
|
Promise.all(pages.map(function(page) {
|
|
|
return Page.revertDeletedPage(page, user, options);
|
|
return Page.revertDeletedPage(page, user, options);
|
|
@@ -1191,6 +1175,7 @@ module.exports = function(crowi) {
|
|
|
, Attachment = crowi.model('Attachment')
|
|
, Attachment = crowi.model('Attachment')
|
|
|
, Comment = crowi.model('Comment')
|
|
, Comment = crowi.model('Comment')
|
|
|
, Revision = crowi.model('Revision')
|
|
, Revision = crowi.model('Revision')
|
|
|
|
|
+ , PageGroupRelation = crowi.model('PageGroupRelation')
|
|
|
, Page = this
|
|
, Page = this
|
|
|
, pageId = pageData._id
|
|
, pageId = pageData._id
|
|
|
;
|
|
;
|
|
@@ -1210,6 +1195,8 @@ module.exports = function(crowi) {
|
|
|
return Page.removePageById(pageId);
|
|
return Page.removePageById(pageId);
|
|
|
}).then(function(done) {
|
|
}).then(function(done) {
|
|
|
return Page.removeRedirectOriginPageByPath(pageData.path);
|
|
return Page.removeRedirectOriginPageByPath(pageData.path);
|
|
|
|
|
+ }).then(function(done) {
|
|
|
|
|
+ return PageGroupRelation.removeAllByPage(pageData);
|
|
|
}).then(function(done) {
|
|
}).then(function(done) {
|
|
|
pageEvent.emit('delete', pageData, user); // update as renamed page
|
|
pageEvent.emit('delete', pageData, user); // update as renamed page
|
|
|
resolve(pageData);
|
|
resolve(pageData);
|
|
@@ -1297,25 +1284,22 @@ module.exports = function(crowi) {
|
|
|
, createRedirectPage = options.createRedirectPage || 0
|
|
, createRedirectPage = options.createRedirectPage || 0
|
|
|
, moveUnderTrees = options.moveUnderTrees || 0;
|
|
, moveUnderTrees = options.moveUnderTrees || 0;
|
|
|
|
|
|
|
|
- return new Promise(function(resolve, reject) {
|
|
|
|
|
- // pageData の path を変更
|
|
|
|
|
- Page.updatePageProperty(pageData, {updatedAt: Date.now(), path: newPagePath, lastUpdateUser: user})
|
|
|
|
|
- .then(function(data) {
|
|
|
|
|
|
|
+ return Page.updatePageProperty(pageData, {updatedAt: Date.now(), path: newPagePath, lastUpdateUser: user}) // pageData の path を変更
|
|
|
|
|
+ .then((data) => {
|
|
|
// reivisions の path を変更
|
|
// reivisions の path を変更
|
|
|
return Revision.updateRevisionListByPath(path, {path: newPagePath}, {});
|
|
return Revision.updateRevisionListByPath(path, {path: newPagePath}, {});
|
|
|
- }).then(function(data) {
|
|
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(function(data) {
|
|
|
pageData.path = newPagePath;
|
|
pageData.path = newPagePath;
|
|
|
|
|
|
|
|
if (createRedirectPage) {
|
|
if (createRedirectPage) {
|
|
|
var body = 'redirect ' + newPagePath;
|
|
var body = 'redirect ' + newPagePath;
|
|
|
- Page.create(path, body, user, {redirectTo: newPagePath}).then(resolve).catch(reject);
|
|
|
|
|
- }
|
|
|
|
|
- else {
|
|
|
|
|
- resolve(data);
|
|
|
|
|
|
|
+ Page.create(path, body, user, {redirectTo: newPagePath});
|
|
|
}
|
|
}
|
|
|
pageEvent.emit('update', pageData, user); // update as renamed page
|
|
pageEvent.emit('update', pageData, user); // update as renamed page
|
|
|
|
|
+
|
|
|
|
|
+ return pageData;
|
|
|
});
|
|
});
|
|
|
- });
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
pageSchema.statics.renameRecursively = function(pageData, newPagePathPrefix, user, options) {
|
|
pageSchema.statics.renameRecursively = function(pageData, newPagePathPrefix, user, options) {
|
|
@@ -1323,20 +1307,17 @@ module.exports = function(crowi) {
|
|
|
, path = pageData.path
|
|
, path = pageData.path
|
|
|
, pathRegExp = new RegExp('^' + escapeStringRegexp(path), 'i');
|
|
, pathRegExp = new RegExp('^' + escapeStringRegexp(path), 'i');
|
|
|
|
|
|
|
|
- return new Promise(function(resolve, reject) {
|
|
|
|
|
- Page
|
|
|
|
|
- .generateQueryToListWithDescendants(path, user, options)
|
|
|
|
|
|
|
+ return Page.generateQueryToListWithDescendants(path, user, options)
|
|
|
.then(function(pages) {
|
|
.then(function(pages) {
|
|
|
- Promise.all(pages.map(function(page) {
|
|
|
|
|
- newPagePath = page.path.replace(pathRegExp, newPagePathPrefix);
|
|
|
|
|
|
|
+ return Promise.all(pages.map(function(page) {
|
|
|
|
|
+ const newPagePath = page.path.replace(pathRegExp, newPagePathPrefix);
|
|
|
return Page.rename(page, newPagePath, user, options);
|
|
return Page.rename(page, newPagePath, user, options);
|
|
|
- }))
|
|
|
|
|
- .then(function() {
|
|
|
|
|
- pageData.path = newPagePathPrefix;
|
|
|
|
|
- return resolve();
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ }));
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(function() {
|
|
|
|
|
+ pageData.path = newPagePathPrefix;
|
|
|
|
|
+ return pageData;
|
|
|
});
|
|
});
|
|
|
- });
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
pageSchema.statics.getHistories = function() {
|
|
pageSchema.statics.getHistories = function() {
|