page.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215
  1. module.exports = function(crowi, app) {
  2. 'use strict';
  3. var debug = require('debug')('crowi:routes:page')
  4. , Page = crowi.model('Page')
  5. , User = crowi.model('User')
  6. , Config = crowi.model('Config')
  7. , config = crowi.getConfig()
  8. , Revision = crowi.model('Revision')
  9. , Bookmark = crowi.model('Bookmark')
  10. , UserGroupRelation = crowi.model('UserGroupRelation')
  11. , ApiResponse = require('../util/apiResponse')
  12. , interceptorManager = crowi.getInterceptorManager()
  13. , actions = {};
  14. // register page events
  15. var pageEvent = crowi.event('page');
  16. pageEvent.on('update', function(page, user) {
  17. crowi.getIo().sockets.emit('page edited', {page, user});
  18. });
  19. function getPathFromRequest(req) {
  20. var path = '/' + (req.params[0] || '');
  21. return path.replace(/\.md$/, '');
  22. }
  23. function isUserPage(path) {
  24. if (path.match(/^\/user\/[^\/]+\/?$/)) {
  25. return true;
  26. }
  27. return false;
  28. }
  29. // TODO: total とかでちゃんと計算する
  30. function generatePager(options) {
  31. var next = null,
  32. prev = null,
  33. offset = parseInt(options.offset, 10),
  34. limit = parseInt(options.limit, 10),
  35. length = options.length || 0;
  36. if (offset > 0) {
  37. prev = offset - limit;
  38. if (prev < 0) {
  39. prev = 0;
  40. }
  41. }
  42. if (length < limit) {
  43. next = null;
  44. } else {
  45. next = offset + limit;
  46. }
  47. return {
  48. prev: prev,
  49. next: next,
  50. offset: offset,
  51. };
  52. }
  53. /**
  54. * switch action by behaviorType
  55. */
  56. actions.pageListShowWrapper = function(req, res) {
  57. const behaviorType = Config.behaviorType(config);
  58. if ('crowi-plus' === behaviorType) {
  59. return actions.pageListShowForCrowiPlus(req, res);
  60. }
  61. else {
  62. return actions.pageListShow(req, res);
  63. }
  64. }
  65. /**
  66. * switch action by behaviorType
  67. */
  68. actions.pageShowWrapper = function(req, res) {
  69. const behaviorType = Config.behaviorType(config);
  70. if ('crowi-plus' === behaviorType) {
  71. return actions.pageShowForCrowiPlus(req, res);
  72. }
  73. else {
  74. return actions.pageShow(req, res);
  75. }
  76. }
  77. /**
  78. * switch action by behaviorType
  79. */
  80. actions.trashPageListShowWrapper = function(req, res) {
  81. const behaviorType = Config.behaviorType(config);
  82. if ('crowi-plus' === behaviorType) {
  83. // redirect to '/trash'
  84. return res.redirect('/trash');
  85. }
  86. // official Crowi behavior for '/trash/*'
  87. else {
  88. return actions.deletedPageListShow(req, res);
  89. }
  90. }
  91. /**
  92. * switch action by behaviorType
  93. */
  94. actions.trashPageShowWrapper = function(req, res) {
  95. const behaviorType = Config.behaviorType(config);
  96. if ('crowi-plus' === behaviorType) {
  97. // official Crowi behavior for '/trash/*'
  98. return actions.deletedPageListShow(req, res);
  99. }
  100. else {
  101. // redirect to '/trash/'
  102. return res.redirect('/trash/');
  103. }
  104. }
  105. /**
  106. * switch action by behaviorType
  107. */
  108. actions.deletedPageListShowWrapper = function(req, res) {
  109. const behaviorType = Config.behaviorType(config);
  110. if ('crowi-plus' === behaviorType) {
  111. const path = '/trash' + getPathFromRequest(req);
  112. return res.redirect(path);
  113. }
  114. // official Crowi behavior for '/trash/*'
  115. else {
  116. return actions.deletedPageListShow(req, res);
  117. }
  118. }
  119. actions.pageListShow = function(req, res) {
  120. var path = getPathFromRequest(req);
  121. var limit = 50;
  122. var offset = parseInt(req.query.offset) || 0;
  123. var SEENER_THRESHOLD = 10;
  124. // add slash if root
  125. path = path + (path == '/' ? '' : '/');
  126. debug('Page list show', path);
  127. // index page
  128. var pagerOptions = {
  129. offset: offset,
  130. limit : limit
  131. };
  132. var queryOptions = {
  133. offset: offset,
  134. limit : limit + 1,
  135. isPopulateRevisionBody: Config.isEnabledTimeline(config),
  136. };
  137. var renderVars = {
  138. page: null,
  139. path: path,
  140. pages: [],
  141. tree: [],
  142. };
  143. Page.hasPortalPage(path, req.user, req.query.revision)
  144. .then(function(portalPage) {
  145. renderVars.page = portalPage;
  146. if (portalPage) {
  147. return Revision.findRevisionList(portalPage.path, {});
  148. } else {
  149. return Promise.resolve([]);
  150. }
  151. }).then(function(tree) {
  152. renderVars.tree = tree;
  153. return Page.findListByStartWith(path, req.user, queryOptions);
  154. }).then(function(pageList) {
  155. if (pageList.length > limit) {
  156. pageList.pop();
  157. }
  158. pagerOptions.length = pageList.length;
  159. renderVars.viewConfig = {
  160. seener_threshold: SEENER_THRESHOLD,
  161. };
  162. renderVars.pager = generatePager(pagerOptions);
  163. renderVars.pages = pageList;
  164. res.render('customlayout-selector/page_list', renderVars);
  165. }).catch(function(err) {
  166. debug('Error on rendering pageListShow', err);
  167. });
  168. };
  169. actions.pageListShowForCrowiPlus = function(req, res) {
  170. var path = getPathFromRequest(req);
  171. // omit the slash of the last
  172. path = path.replace((/\/$/), '');
  173. // redirect
  174. return res.redirect(path);
  175. }
  176. actions.pageShowForCrowiPlus = function(req, res) {
  177. var path = getPathFromRequest(req);
  178. var limit = 50;
  179. var offset = parseInt(req.query.offset) || 0;
  180. var SEENER_THRESHOLD = 10;
  181. // index page
  182. var pagerOptions = {
  183. offset: offset,
  184. limit : limit
  185. };
  186. var queryOptions = {
  187. offset: offset,
  188. limit : limit + 1,
  189. isPopulateRevisionBody: Config.isEnabledTimeline(config),
  190. includeDeletedPage: path.startsWith('/trash/'),
  191. };
  192. var renderVars = {
  193. path: path,
  194. page: null,
  195. revision: {},
  196. author: false,
  197. pages: [],
  198. tree: [],
  199. userRelatedGroups: [],
  200. };
  201. var pageTeamplate = 'customlayout-selector/page';
  202. var isRedirect = false;
  203. Page.findPage(path, req.user, req.query.revision)
  204. .then(function(page) {
  205. debug('Page found', page._id, page.path);
  206. // redirect
  207. if (page.redirectTo) {
  208. debug(`Redirect to '${page.redirectTo}'`);
  209. isRedirect = true;
  210. return res.redirect(encodeURI(page.redirectTo + '?redirectFrom=' + page.path));
  211. }
  212. renderVars.page = page;
  213. if (page) {
  214. renderVars.path = page.path;
  215. renderVars.revision = page.revision;
  216. renderVars.author = page.revision.author;
  217. return Revision.findRevisionList(page.path, {})
  218. .then(function(tree) {
  219. renderVars.tree = tree;
  220. return Promise.resolve();
  221. }).then(function() {
  222. var userPage = isUserPage(page.path);
  223. var userData = null;
  224. if (userPage) {
  225. // change template
  226. pageTeamplate = 'customlayout-selector/user_page';
  227. return User.findUserByUsername(User.getUsernameByPath(page.path))
  228. .then(function(data) {
  229. if (data === null) {
  230. throw new Error('The user not found.');
  231. }
  232. userData = data;
  233. renderVars.pageUser = userData;
  234. return Bookmark.findByUser(userData, {limit: 10, populatePage: true, requestUser: req.user});
  235. }).then(function(bookmarkList) {
  236. renderVars.bookmarkList = bookmarkList;
  237. return Page.findListByCreator(userData, {limit: 10}, req.user);
  238. }).then(function(createdList) {
  239. renderVars.createdList = createdList;
  240. return Promise.resolve();
  241. }).catch(function(err) {
  242. debug('Error on finding user related entities', err);
  243. // pass
  244. });
  245. }
  246. else {
  247. return Promise.resolve();
  248. }
  249. });
  250. } else {
  251. return Promise.resolve();
  252. }
  253. })
  254. // page not exists
  255. .catch(function(err) {
  256. debug('Page not found', path);
  257. // change template
  258. pageTeamplate = 'customlayout-selector/not_found';
  259. })
  260. // get list pages
  261. .then(function() {
  262. if (!isRedirect) {
  263. Page.findListWithDescendants(path, req.user, queryOptions)
  264. .then(function(pageList) {
  265. if (pageList.length > limit) {
  266. pageList.pop();
  267. }
  268. pagerOptions.length = pageList.length;
  269. renderVars.viewConfig = {
  270. seener_threshold: SEENER_THRESHOLD,
  271. };
  272. renderVars.pager = generatePager(pagerOptions);
  273. renderVars.pages = pageList;
  274. return Promise.resolve();
  275. })
  276. .then(function() {
  277. return interceptorManager.process('beforeRenderPage', req, res, renderVars);
  278. })
  279. .then(function() {
  280. res.render(req.query.presentation ? 'page_presentation' : pageTeamplate, renderVars);
  281. })
  282. .catch(function(err) {
  283. console.log(err);
  284. debug('Error on rendering pageListShowForCrowiPlus', err);
  285. });
  286. }
  287. })
  288. .then(function() {
  289. return UserGroupRelation.findAllRelationForUser(req.user);
  290. }).then(function (groupRelations) {
  291. debug('findPage : relatedGroups ', groupRelations);
  292. renderVars.userRelatedGroups = groupRelations.map(relation => relation.relatedGroup);
  293. debug('findPage : groups ', renderVars.userRelatedGroups);
  294. return Promise.resolve();
  295. });
  296. }
  297. actions.deletedPageListShow = function(req, res) {
  298. var path = '/trash' + getPathFromRequest(req);
  299. var limit = 50;
  300. var offset = parseInt(req.query.offset) || 0;
  301. // index page
  302. var pagerOptions = {
  303. offset: offset,
  304. limit : limit
  305. };
  306. var queryOptions = {
  307. offset: offset,
  308. limit : limit + 1,
  309. includeDeletedPage: true,
  310. };
  311. var renderVars = {
  312. page: null,
  313. path: path,
  314. pages: [],
  315. };
  316. Page.findListWithDescendants(path, req.user, queryOptions)
  317. .then(function(pageList) {
  318. if (pageList.length > limit) {
  319. pageList.pop();
  320. }
  321. pagerOptions.length = pageList.length;
  322. renderVars.pager = generatePager(pagerOptions);
  323. renderVars.pages = pageList;
  324. res.render('customlayout-selector/page_list', renderVars);
  325. }).catch(function(err) {
  326. debug('Error on rendering deletedPageListShow', err);
  327. });
  328. };
  329. actions.search = function(req, res) {
  330. // spec: ?q=query&sort=sort_order&author=author_filter
  331. var query = req.query.q;
  332. var search = require('../util/search')(crowi);
  333. search.searchPageByKeyword(query)
  334. .then(function(pages) {
  335. debug('pages', pages);
  336. if (pages.hits.total <= 0) {
  337. return Promise.resolve([]);
  338. }
  339. var ids = pages.hits.hits.map(function(page) {
  340. return page._id;
  341. });
  342. return Page.findListByPageIds(ids);
  343. }).then(function(pages) {
  344. res.render('customlayout-selector/page_list', {
  345. path: '/',
  346. pages: pages,
  347. pager: generatePager({offset: 0, limit: 50})
  348. });
  349. }).catch(function(err) {
  350. debug('search error', err);
  351. });
  352. };
  353. function renderPage(pageData, req, res) {
  354. // create page
  355. if (!pageData) {
  356. return res.render('page', {
  357. author: {},
  358. page: false,
  359. });
  360. }
  361. if (pageData.redirectTo) {
  362. return res.redirect(encodeURI(pageData.redirectTo + '?redirectFrom=' + pageData.path));
  363. }
  364. var renderVars = {
  365. path: pageData.path,
  366. page: pageData,
  367. revision: pageData.revision || {},
  368. author: pageData.revision.author || false,
  369. };
  370. var userPage = isUserPage(pageData.path);
  371. var userData = null;
  372. Revision.findRevisionList(pageData.path, {})
  373. .then(function(tree) {
  374. renderVars.tree = tree;
  375. return Promise.resolve();
  376. }).then(function() {
  377. if (userPage) {
  378. return User.findUserByUsername(User.getUsernameByPath(pageData.path))
  379. .then(function(data) {
  380. if (data === null) {
  381. throw new Error('The user not found.');
  382. }
  383. userData = data;
  384. renderVars.pageUser = userData;
  385. return Bookmark.findByUser(userData, {limit: 10, populatePage: true, requestUser: req.user});
  386. }).then(function(bookmarkList) {
  387. renderVars.bookmarkList = bookmarkList;
  388. return Page.findListByCreator(userData, {limit: 10}, req.user);
  389. }).then(function(createdList) {
  390. renderVars.createdList = createdList;
  391. return Promise.resolve();
  392. }).catch(function(err) {
  393. debug('Error on finding user related entities', err);
  394. // pass
  395. });
  396. } else {
  397. return Promise.resolve();
  398. }
  399. }).then(function() {
  400. return interceptorManager.process('beforeRenderPage', req, res, renderVars);
  401. }).then(function() {
  402. var defaultPageTeamplate = 'customlayout-selector/page';
  403. if (userData) {
  404. defaultPageTeamplate = 'customlayout-selector/user_page';
  405. }
  406. res.render(req.query.presentation ? 'page_presentation' : defaultPageTeamplate, renderVars);
  407. }).catch(function(err) {
  408. debug('Error: renderPage()', err);
  409. if (err) {
  410. res.redirect('/');
  411. }
  412. });
  413. }
  414. actions.pageShow = function(req, res) {
  415. var path = path || getPathFromRequest(req);
  416. var options = {};
  417. // FIXME: せっかく getPathFromRequest になってるのにここが生 params[0] だとダサイ
  418. var isMarkdown = req.params[0].match(/.+\.md$/) || false;
  419. res.locals.path = path;
  420. Page.findPage(path, req.user, req.query.revision)
  421. .then(function(page) {
  422. debug('Page found', page._id, page.path);
  423. if (isMarkdown) {
  424. res.set('Content-Type', 'text/plain');
  425. return res.send(page.revision.body);
  426. }
  427. return renderPage(page, req, res);
  428. }).catch(function(err) {
  429. const normalizedPath = Page.normalizePath(path);
  430. if (normalizedPath !== path) {
  431. return res.redirect(normalizedPath);
  432. }
  433. // pageShow は /* にマッチしてる最後の砦なので、creatableName でない routing は
  434. // これ以前に定義されているはずなので、こうしてしまって問題ない。
  435. if (!Page.isCreatableName(path)) {
  436. // 削除済みページの場合 /trash 以下に移動しているので creatableName になっていないので、表示を許可
  437. debug('Page is not creatable name.', path);
  438. res.redirect('/');
  439. return ;
  440. }
  441. if (req.query.revision) {
  442. return res.redirect(encodeURI(path));
  443. }
  444. if (isMarkdown) {
  445. return res.redirect('/');
  446. }
  447. Page.hasPortalPage(path + '/', req.user)
  448. .then(function(page) {
  449. if (page) {
  450. return res.redirect(encodeURI(path) + '/');
  451. } else {
  452. var fixed = Page.fixToCreatableName(path)
  453. if (fixed !== path) {
  454. debug('fixed page name', fixed)
  455. res.redirect(encodeURI(fixed));
  456. return ;
  457. }
  458. // if guest user
  459. if (!req.user) {
  460. res.redirect('/');
  461. }
  462. // render editor
  463. debug('Catch pageShow', err);
  464. return renderPage(null, req, res);
  465. }
  466. }).catch(function(err) {
  467. debug('Error on rendering pageShow (redirect to portal)', err);
  468. });
  469. });
  470. };
  471. actions.pageEdit = function(req, res) {
  472. var pageForm = req.body.pageForm;
  473. var body = pageForm.body;
  474. var currentRevision = pageForm.currentRevision;
  475. var grant = pageForm.grant;
  476. var path = pageForm.path;
  477. var grantUserGroupId = pageForm.grantUserGroupId
  478. // TODO: make it pluggable
  479. var notify = pageForm.notify || {};
  480. debug('notify: ', notify);
  481. var redirectPath = encodeURI(path);
  482. var pageData = {};
  483. var updateOrCreate;
  484. var previousRevision = false;
  485. // set to render
  486. res.locals.pageForm = pageForm;
  487. // 削除済みページはここで編集不可判定される
  488. if (!Page.isCreatableName(path)) {
  489. res.redirect(redirectPath);
  490. return ;
  491. }
  492. var ignoreNotFound = true;
  493. Page.findPage(path, req.user, null, ignoreNotFound)
  494. .then(function(data) {
  495. pageData = data;
  496. if (!req.form.isValid) {
  497. debug('Form data not valid');
  498. throw new Error('Form data not valid.');
  499. }
  500. if (data && !data.isUpdatable(currentRevision)) {
  501. debug('Conflict occured');
  502. req.form.errors.push('page_edit.notice.conflict');
  503. throw new Error('Conflict.');
  504. }
  505. if (data) {
  506. previousRevision = data.revision;
  507. return Page.updatePage(data, body, req.user, { grant: grant, grantUserGroupId: grantUserGroupId});
  508. } else {
  509. // new page
  510. updateOrCreate = 'create';
  511. return Page.create(path, body, req.user, { grant: grant, grantUserGroupId: grantUserGroupId});
  512. }
  513. }).then(function(data) {
  514. // data is a saved page data.
  515. pageData = data;
  516. if (!data) {
  517. throw new Error('Data not found');
  518. }
  519. // TODO: move to events
  520. if (notify.slack) {
  521. if (notify.slack.on && notify.slack.channel) {
  522. data.updateSlackChannel(notify.slack.channel).then(function(){}).catch(function(){});
  523. if (crowi.slack) {
  524. notify.slack.channel.split(',').map(function(chan) {
  525. var message = crowi.slack.prepareSlackMessage(pageData, req.user, chan, updateOrCreate, previousRevision);
  526. crowi.slack.post(message.channel, message.text, message).then(function(){}).catch(function(){});
  527. });
  528. }
  529. }
  530. }
  531. return res.redirect(redirectPath);
  532. }).catch(function(err) {
  533. debug('Page create or edit error.', err);
  534. if (pageData && !req.form.isValid) {
  535. return renderPage(pageData, req, res);
  536. }
  537. return res.redirect(redirectPath);
  538. });
  539. };
  540. // app.get( '/users/:username([^/]+)/bookmarks' , loginRequired(crowi, app) , page.userBookmarkList);
  541. actions.userBookmarkList = function(req, res) {
  542. var username = req.params.username;
  543. var limit = 50;
  544. var offset = parseInt(req.query.offset) || 0;
  545. var user;
  546. var renderVars = {};
  547. var pagerOptions = { offset: offset, limit : limit };
  548. var queryOptions = { offset: offset, limit : limit + 1, populatePage: true, requestUser: req.user};
  549. User.findUserByUsername(username)
  550. .then(function(user) {
  551. if (user === null) {
  552. throw new Error('The user not found.');
  553. }
  554. renderVars.pageUser = user;
  555. return Bookmark.findByUser(user, queryOptions);
  556. }).then(function(bookmarks) {
  557. if (bookmarks.length > limit) {
  558. bookmarks.pop();
  559. }
  560. pagerOptions.length = bookmarks.length;
  561. renderVars.pager = generatePager(pagerOptions);
  562. renderVars.bookmarks = bookmarks;
  563. return res.render('user/bookmarks', renderVars);
  564. }).catch(function(err) {
  565. debug('Error on rendereing bookmark', err);
  566. res.redirect('/');
  567. });
  568. };
  569. // app.get( '/users/:username([^/]+)/recent-create' , loginRequired(crowi, app) , page.userRecentCreatedList);
  570. actions.userRecentCreatedList = function(req, res) {
  571. var username = req.params.username;
  572. var limit = 50;
  573. var offset = parseInt(req.query.offset) || 0;
  574. var user;
  575. var renderVars = {};
  576. var pagerOptions = { offset: offset, limit : limit };
  577. var queryOptions = { offset: offset, limit : limit + 1};
  578. User.findUserByUsername(username)
  579. .then(function(user) {
  580. if (user === null) {
  581. throw new Error('The user not found.');
  582. }
  583. renderVars.pageUser = user;
  584. return Page.findListByCreator(user, queryOptions, req.user);
  585. }).then(function(pages) {
  586. if (pages.length > limit) {
  587. pages.pop();
  588. }
  589. pagerOptions.length = pages.length;
  590. renderVars.pager = generatePager(pagerOptions);
  591. renderVars.pages = pages;
  592. return res.render('user/recent-create', renderVars);
  593. }).catch(function(err) {
  594. debug('Error on rendereing recent-created', err);
  595. res.redirect('/');
  596. });
  597. };
  598. var api = actions.api = {};
  599. /**
  600. * redirector
  601. */
  602. api.redirector = function(req, res){
  603. var id = req.params.id;
  604. Page.findPageById(id)
  605. .then(function(pageData) {
  606. if (pageData.grant == Page.GRANT_RESTRICTED && !pageData.isGrantedFor(req.user)) {
  607. return Page.pushToGrantedUsers(pageData, req.user);
  608. }
  609. return Promise.resolve(pageData);
  610. }).then(function(page) {
  611. return res.redirect(encodeURI(page.path));
  612. }).catch(function(err) {
  613. return res.redirect('/');
  614. });
  615. };
  616. /**
  617. * @api {get} /pages.list List pages by user
  618. * @apiName ListPage
  619. * @apiGroup Page
  620. *
  621. * @apiParam {String} path
  622. * @apiParam {String} user
  623. */
  624. api.list = function(req, res) {
  625. var username = req.query.user || null;
  626. var path = req.query.path || null;
  627. var limit = 50;
  628. var offset = parseInt(req.query.offset) || 0;
  629. var pagerOptions = { offset: offset, limit : limit };
  630. var queryOptions = { offset: offset, limit : limit + 1};
  631. // Accepts only one of these
  632. if (username === null && path === null) {
  633. return res.json(ApiResponse.error('Parameter user or path is required.'));
  634. }
  635. if (username !== null && path !== null) {
  636. return res.json(ApiResponse.error('Parameter user or path is required.'));
  637. }
  638. var pageFetcher;
  639. if (path === null) {
  640. pageFetcher = User.findUserByUsername(username)
  641. .then(function(user) {
  642. if (user === null) {
  643. throw new Error('The user not found.');
  644. }
  645. return Page.findListByCreator(user, queryOptions, req.user);
  646. });
  647. } else {
  648. pageFetcher = Page.findListByStartWith(path, req.user, queryOptions);
  649. }
  650. pageFetcher
  651. .then(function(pages) {
  652. if (pages.length > limit) {
  653. pages.pop();
  654. }
  655. pagerOptions.length = pages.length;
  656. var result = {};
  657. result.pages = pages;
  658. return res.json(ApiResponse.success(result));
  659. }).catch(function(err) {
  660. return res.json(ApiResponse.error(err));
  661. });
  662. };
  663. /**
  664. * @api {post} /pages.create Create new page
  665. * @apiName CreatePage
  666. * @apiGroup Page
  667. *
  668. * @apiParam {String} body
  669. * @apiParam {String} path
  670. * @apiParam {String} grant
  671. */
  672. api.create = function(req, res){
  673. var body = req.body.body || null;
  674. var pagePath = req.body.path || null;
  675. var grant = req.body.grant || null;
  676. var grantUserGroupId = req.body.grantUserGroupId || null;
  677. if (body === null || pagePath === null) {
  678. return res.json(ApiResponse.error('Parameters body and path are required.'));
  679. }
  680. var ignoreNotFound = true;
  681. Page.findPage(pagePath, req.user, null, ignoreNotFound)
  682. .then(function(data) {
  683. if (data !== null) {
  684. throw new Error('Page exists');
  685. }
  686. return Page.create(pagePath, body, req.user, { grant: grant, grantUserGroupId: grantUserGroupId});
  687. }).then(function(data) {
  688. if (!data) {
  689. throw new Error('Failed to create page.');
  690. }
  691. var result = { page: data.toObject() };
  692. result.page.lastUpdateUser = User.filterToPublicFields(data.lastUpdateUser);
  693. result.page.creator = User.filterToPublicFields(data.creator);
  694. return res.json(ApiResponse.success(result));
  695. }).catch(function(err) {
  696. return res.json(ApiResponse.error(err));
  697. });;
  698. };
  699. /**
  700. * @api {post} /pages.update Update page
  701. * @apiName UpdatePage
  702. * @apiGroup Page
  703. *
  704. * @apiParam {String} body
  705. * @apiParam {String} page_id
  706. * @apiParam {String} revision_id
  707. * @apiParam {String} grant
  708. *
  709. * In the case of the page exists:
  710. * - If revision_id is specified => update the page,
  711. * - If revision_id is not specified => force update by the new contents.
  712. */
  713. api.update = function(req, res){
  714. var pageBody = req.body.body || null;
  715. var pageId = req.body.page_id || null;
  716. var revisionId = req.body.revision_id || null;
  717. var grant = req.body.grant || null;
  718. var grantUserGroupId = req.body.grantUserGroupId || null;
  719. if (pageId === null || pageBody === null) {
  720. return res.json(ApiResponse.error('page_id and body are required.'));
  721. }
  722. Page.findPageByIdAndGrantedUser(pageId, req.user)
  723. .then(function(pageData) {
  724. if (pageData && revisionId !== null && !pageData.isUpdatable(revisionId)) {
  725. throw new Error('Revision error.');
  726. };
  727. var grantOption = {grant: pageData.grant};
  728. if (grant !== null) {
  729. grantOption.grant = grant;
  730. }
  731. if (grantUserGroupId != null) {
  732. grantOption.grantUserGroupId = grantUserGroupId;
  733. }
  734. return Page.updatePage(pageData, pageBody, req.user, grantOption);
  735. }).then(function(pageData) {
  736. var result = {
  737. page: pageData.toObject(),
  738. };
  739. result.page.lastUpdateUser = User.filterToPublicFields(result.page.lastUpdateUser);
  740. return res.json(ApiResponse.success(result));
  741. }).catch(function(err) {
  742. debug('error on _api/pages.update', err);
  743. return res.json(ApiResponse.error(err));
  744. });
  745. };
  746. /**
  747. * @api {get} /pages.get Get page data
  748. * @apiName GetPage
  749. * @apiGroup Page
  750. *
  751. * @apiParam {String} page_id
  752. * @apiParam {String} path
  753. * @apiParam {String} revision_id
  754. */
  755. api.get = function(req, res){
  756. const pagePath = req.query.path || null;
  757. const pageId = req.query.page_id || null; // TODO: handling
  758. const revisionId = req.query.revision_id || null;
  759. if (!pageId && !pagePath) {
  760. return res.json(ApiResponse.error(new Error('Parameter path or page_id is required.')));
  761. }
  762. let pageFinder;
  763. if (pageId) { // prioritized
  764. pageFinder = Page.findPageByIdAndGrantedUser(pageId, req.user);
  765. } else if (pagePath) {
  766. pageFinder = Page.findPage(pagePath, req.user, revisionId);
  767. }
  768. pageFinder.then(function(pageData) {
  769. var result = {};
  770. result.page = pageData;
  771. return res.json(ApiResponse.success(result));
  772. }).catch(function(err) {
  773. return res.json(ApiResponse.error(err));
  774. });
  775. };
  776. /**
  777. * @api {post} /pages.seen Mark as seen user
  778. * @apiName SeenPage
  779. * @apiGroup Page
  780. *
  781. * @apiParam {String} page_id Page Id.
  782. */
  783. api.seen = function(req, res){
  784. var pageId = req.body.page_id;
  785. if (!pageId) {
  786. return res.json(ApiResponse.error('page_id required'));
  787. }
  788. Page.findPageByIdAndGrantedUser(pageId, req.user)
  789. .then(function(page) {
  790. return page.seen(req.user);
  791. }).then(function(user) {
  792. var result = {};
  793. result.seenUser = user;
  794. return res.json(ApiResponse.success(result));
  795. }).catch(function(err) {
  796. debug('Seen user update error', err);
  797. return res.json(ApiResponse.error(err));
  798. });
  799. };
  800. /**
  801. * @api {post} /likes.add Like page
  802. * @apiName LikePage
  803. * @apiGroup Page
  804. *
  805. * @apiParam {String} page_id Page Id.
  806. */
  807. api.like = function(req, res){
  808. var id = req.body.page_id;
  809. Page.findPageByIdAndGrantedUser(id, req.user)
  810. .then(function(pageData) {
  811. return pageData.like(req.user);
  812. }).then(function(data) {
  813. var result = {page: data};
  814. return res.json(ApiResponse.success(result));
  815. }).catch(function(err) {
  816. debug('Like failed', err);
  817. return res.json(ApiResponse.error({}));
  818. });
  819. };
  820. /**
  821. * @api {post} /likes.remove Unlike page
  822. * @apiName UnlikePage
  823. * @apiGroup Page
  824. *
  825. * @apiParam {String} page_id Page Id.
  826. */
  827. api.unlike = function(req, res){
  828. var id = req.body.page_id;
  829. Page.findPageByIdAndGrantedUser(id, req.user)
  830. .then(function(pageData) {
  831. return pageData.unlike(req.user);
  832. }).then(function(data) {
  833. var result = {page: data};
  834. return res.json(ApiResponse.success(result));
  835. }).catch(function(err) {
  836. debug('Unlike failed', err);
  837. return res.json(ApiResponse.error({}));
  838. });
  839. };
  840. /**
  841. * @api {get} /pages.updatePost
  842. * @apiName Get UpdatePost setting list
  843. * @apiGroup Page
  844. *
  845. * @apiParam {String} path
  846. */
  847. api.getUpdatePost = function(req, res) {
  848. var path = req.query.path;
  849. var UpdatePost = crowi.model('UpdatePost');
  850. if (!path) {
  851. return res.json(ApiResponse.error({}));
  852. }
  853. UpdatePost.findSettingsByPath(path)
  854. .then(function(data) {
  855. data = data.map(function(e) {
  856. return e.channel;
  857. });
  858. debug('Found updatePost data', data);
  859. var result = {updatePost: data};
  860. return res.json(ApiResponse.success(result));
  861. }).catch(function(err) {
  862. debug('Error occured while get setting', err);
  863. return res.json(ApiResponse.error({}));
  864. });
  865. };
  866. /**
  867. * @api {post} /pages.remove Remove page
  868. * @apiName RemovePage
  869. * @apiGroup Page
  870. *
  871. * @apiParam {String} page_id Page Id.
  872. * @apiParam {String} revision_id
  873. */
  874. api.remove = function(req, res){
  875. var pageId = req.body.page_id;
  876. var previousRevision = req.body.revision_id || null;
  877. // get completely flag
  878. const isCompletely = (req.body.completely !== undefined);
  879. // get recursively flag
  880. const isRecursively = (req.body.recursively !== undefined);
  881. Page.findPageByIdAndGrantedUser(pageId, req.user)
  882. .then(function(pageData) {
  883. debug('Delete page', pageData._id, pageData.path);
  884. if (isCompletely) {
  885. if (isRecursively) {
  886. return Page.completelyDeletePageRecursively(pageData, req.user);
  887. }
  888. else {
  889. return Page.completelyDeletePage(pageData, req.user);
  890. }
  891. }
  892. // else
  893. if (!pageData.isUpdatable(previousRevision)) {
  894. throw new Error('Someone could update this page, so couldn\'t delete.');
  895. }
  896. if (isRecursively) {
  897. return Page.deletePageRecursively(pageData, req.user);
  898. }
  899. else {
  900. return Page.deletePage(pageData, req.user);
  901. }
  902. }).then(function(data) {
  903. debug('Page deleted', data.path);
  904. var result = {};
  905. result.page = data;
  906. return res.json(ApiResponse.success(result));
  907. }).catch(function(err) {
  908. debug('Error occured while get setting', err, err.stack);
  909. return res.json(ApiResponse.error('Failed to delete page.'));
  910. });
  911. };
  912. /**
  913. * @api {post} /pages.revertRemove Revert removed page
  914. * @apiName RevertRemovePage
  915. * @apiGroup Page
  916. *
  917. * @apiParam {String} page_id Page Id.
  918. */
  919. api.revertRemove = function(req, res){
  920. var pageId = req.body.page_id;
  921. // get recursively flag
  922. const isRecursively = (req.body.recursively !== undefined);
  923. Page.findPageByIdAndGrantedUser(pageId, req.user)
  924. .then(function(pageData) {
  925. if (isRecursively) {
  926. return Page.revertDeletedPageRecursively(pageData, req.user);
  927. }
  928. else {
  929. return Page.revertDeletedPage(pageData, req.user);
  930. }
  931. }).then(function(data) {
  932. debug('Complete to revert deleted page', data.path);
  933. var result = {};
  934. result.page = data;
  935. return res.json(ApiResponse.success(result));
  936. }).catch(function(err) {
  937. debug('Error occured while get setting', err, err.stack);
  938. return res.json(ApiResponse.error('Failed to revert deleted page.'));
  939. });
  940. };
  941. /**
  942. * @api {post} /pages.rename Rename page
  943. * @apiName RenamePage
  944. * @apiGroup Page
  945. *
  946. * @apiParam {String} page_id Page Id.
  947. * @apiParam {String} path
  948. * @apiParam {String} revision_id
  949. * @apiParam {String} new_path
  950. * @apiParam {Bool} create_redirect
  951. */
  952. api.rename = function(req, res){
  953. var pageId = req.body.page_id;
  954. var previousRevision = req.body.revision_id || null;
  955. var newPagePath = Page.normalizePath(req.body.new_path);
  956. var options = {
  957. createRedirectPage: req.body.create_redirect || 0,
  958. moveUnderTrees: req.body.move_trees || 0,
  959. };
  960. var isRecursiveMove = req.body.move_recursively || 0;
  961. var page = {};
  962. if (!Page.isCreatableName(newPagePath)) {
  963. return res.json(ApiResponse.error(`このページ名は作成できません (${newPagePath})`));
  964. }
  965. Page.findPageByPath(newPagePath)
  966. .then(function(page) {
  967. // if page found, cannot cannot rename to that path
  968. return res.json(ApiResponse.error(`このページ名は作成できません (${newPagePath})。ページが存在します。`));
  969. }).catch(function(err) {
  970. Page.findPageById(pageId)
  971. .then(function(pageData) {
  972. page = pageData;
  973. if (!pageData.isUpdatable(previousRevision)) {
  974. throw new Error('Someone could update this page, so couldn\'t delete.');
  975. }
  976. if (isRecursiveMove) {
  977. return Page.renameRecursively(pageData, newPagePath, req.user, options);
  978. }
  979. else {
  980. return Page.rename(pageData, newPagePath, req.user, options);
  981. }
  982. }).then(function() {
  983. var result = {};
  984. result.page = page;
  985. return res.json(ApiResponse.success(result));
  986. }).catch(function(err) {
  987. return res.json(ApiResponse.error('Failed to update page.'));
  988. });
  989. });
  990. };
  991. /**
  992. * @api {post} /pages.duplicate Duplicate page
  993. * @apiName DuplicatePage
  994. * @apiGroup Page
  995. *
  996. * @apiParam {String} page_id Page Id.
  997. * @apiParam {String} new_path
  998. */
  999. api.duplicate = function (req, res) {
  1000. var pageId = req.body.page_id;
  1001. var newPagePath = Page.normalizePath(req.body.new_path);
  1002. var page = {};
  1003. Page.findPageById(pageId)
  1004. .then(function (pageData) {
  1005. req.body.path = newPagePath;
  1006. req.body.body = pageData.revision.body;
  1007. req.body.grant = pageData.grant;
  1008. return api.create(req, res);
  1009. });
  1010. };
  1011. /**
  1012. * @api {post} /pages.unlink Remove the redirecting page
  1013. * @apiName UnlinkPage
  1014. * @apiGroup Page
  1015. *
  1016. * @apiParam {String} page_id Page Id.
  1017. * @apiParam {String} revision_id
  1018. */
  1019. api.unlink = function(req, res){
  1020. var pageId = req.body.page_id;
  1021. Page.findPageByIdAndGrantedUser(pageId, req.user)
  1022. .then(function(pageData) {
  1023. debug('Unlink page', pageData._id, pageData.path);
  1024. return Page.removeRedirectOriginPageByPath(pageData.path)
  1025. .then(() => pageData);
  1026. }).then(function(data) {
  1027. debug('Redirect Page deleted', data.path);
  1028. var result = {};
  1029. result.page = data;
  1030. return res.json(ApiResponse.success(result));
  1031. }).catch(function(err) {
  1032. debug('Error occured while get setting', err, err.stack);
  1033. return res.json(ApiResponse.error('Failed to delete redirect page.'));
  1034. });
  1035. };
  1036. return actions;
  1037. };