page.js 30 KB

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