customize-setting.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /* eslint-disable no-unused-vars */
  2. import loggerFactory from '~/utils/logger';
  3. const logger = loggerFactory('growi:routes:apiv3:customize-setting');
  4. const express = require('express');
  5. const router = express.Router();
  6. const { body, query } = require('express-validator');
  7. const ErrorV3 = require('../../models/vo/error-apiv3');
  8. /**
  9. * @swagger
  10. * tags:
  11. * name: CustomizeSetting
  12. */
  13. /**
  14. * @swagger
  15. *
  16. * components:
  17. * schemas:
  18. * CustomizeLayout:
  19. * description: CustomizeLayout
  20. * type: object
  21. * properties:
  22. * isContainerFluid:
  23. * type: boolean
  24. * CustomizeTheme:
  25. * description: CustomizeTheme
  26. * type: object
  27. * properties:
  28. * themeType:
  29. * type: string
  30. * CustomizeFunction:
  31. * description: CustomizeFunction
  32. * type: object
  33. * properties:
  34. * isEnabledTimeline:
  35. * type: boolean
  36. * isSavedStatesOfTabChanges:
  37. * type: boolean
  38. * isEnabledAttachTitleHeader:
  39. * type: boolean
  40. * pageLimitationS:
  41. * type: number
  42. * pageLimitationM:
  43. * type: number
  44. * isEnabledStaleNotification:
  45. * type: boolean
  46. * isAllReplyShown:
  47. * type: boolean
  48. * isSearchScopeChildrenAsDefault:
  49. * type: boolean
  50. * CustomizeHighlight:
  51. * description: CustomizeHighlight
  52. * type: object
  53. * properties:
  54. * styleName:
  55. * type: string
  56. * styleBorder:
  57. * type: boolean
  58. * CustomizeTitle:
  59. * description: CustomizeTitle
  60. * type: object
  61. * properties:
  62. * customizeTitle:
  63. * type: string
  64. * CustomizeHeader:
  65. * description: CustomizeHeader
  66. * type: object
  67. * properties:
  68. * customizeHeader:
  69. * type: string
  70. * CustomizeCss:
  71. * description: CustomizeCss
  72. * type: object
  73. * properties:
  74. * customizeCss:
  75. * type: string
  76. * CustomizeScript:
  77. * description: CustomizeScript
  78. * type: object
  79. * properties:
  80. * customizeScript:
  81. * type: string
  82. */
  83. module.exports = (crowi) => {
  84. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  85. const adminRequired = require('../../middlewares/admin-required')(crowi);
  86. const csrf = require('../../middlewares/csrf')(crowi);
  87. const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
  88. const { customizeService } = crowi;
  89. const validator = {
  90. layout: [
  91. body('isContainerFluid').isBoolean(),
  92. ],
  93. themeAssetPath: [
  94. query('themeName').isString(),
  95. ],
  96. theme: [
  97. body('themeType').isString(),
  98. ],
  99. function: [
  100. body('isEnabledTimeline').isBoolean(),
  101. body('isSavedStatesOfTabChanges').isBoolean(),
  102. body('isEnabledAttachTitleHeader').isBoolean(),
  103. body('pageLimitationS').isInt().isInt({ min: 1, max: 1000 }),
  104. body('pageLimitationM').isInt().isInt({ min: 1, max: 1000 }),
  105. body('pageLimitationL').isInt().isInt({ min: 1, max: 1000 }),
  106. body('pageLimitationXL').isInt().isInt({ min: 1, max: 1000 }),
  107. body('isEnabledStaleNotification').isBoolean(),
  108. body('isAllReplyShown').isBoolean(),
  109. body('isSearchScopeChildrenAsDefault').isBoolean(),
  110. ],
  111. customizeTitle: [
  112. body('customizeTitle').isString(),
  113. ],
  114. customizeHeader: [
  115. body('customizeHeader').isString(),
  116. ],
  117. highlight: [
  118. body('highlightJsStyle').isString().isIn([
  119. 'github', 'github-gist', 'atom-one-light', 'xcode', 'vs', 'atom-one-dark', 'hybrid', 'monokai', 'tomorrow-night', 'vs2015',
  120. ]),
  121. body('highlightJsStyleBorder').isBoolean(),
  122. ],
  123. customizeCss: [
  124. body('customizeCss').isString(),
  125. ],
  126. customizeScript: [
  127. body('customizeScript').isString(),
  128. ],
  129. };
  130. /**
  131. * @swagger
  132. *
  133. * /customize-setting:
  134. * get:
  135. * tags: [CustomizeSetting]
  136. * operationId: getCustomizeSetting
  137. * summary: /customize-setting
  138. * description: Get customize parameters
  139. * responses:
  140. * 200:
  141. * description: params of customize
  142. * content:
  143. * application/json:
  144. * schema:
  145. * properties:
  146. * customizeParams:
  147. * type: object
  148. * description: customize params
  149. */
  150. router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
  151. const customizeParams = {
  152. themeType: await crowi.configManager.getConfig('crowi', 'customize:theme'),
  153. isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
  154. isSavedStatesOfTabChanges: await crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'),
  155. isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
  156. pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'),
  157. pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'),
  158. pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
  159. pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
  160. isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'),
  161. isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'),
  162. isSearchScopeChildrenAsDefault: await crowi.configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault'),
  163. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  164. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  165. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  166. customizeHeader: await crowi.configManager.getConfig('crowi', 'customize:header'),
  167. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  168. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  169. };
  170. return res.apiv3({ customizeParams });
  171. });
  172. /**
  173. * @swagger
  174. *
  175. * /customize-setting/layout:
  176. * get:
  177. * tags: [CustomizeSetting]
  178. * operationId: getLayoutCustomizeSetting
  179. * summary: /customize-setting/layout
  180. * description: Get layout
  181. * responses:
  182. * 200:
  183. * description: Succeeded to get layout
  184. * content:
  185. * application/json:
  186. * schema:
  187. * $ref: '#/components/schemas/CustomizeLayout'
  188. */
  189. router.get('/layout', loginRequiredStrictly, adminRequired, async(req, res) => {
  190. try {
  191. const isContainerFluid = await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid');
  192. return res.apiv3({ isContainerFluid });
  193. }
  194. catch (err) {
  195. const msg = 'Error occurred in getting layout';
  196. logger.error('Error', err);
  197. return res.apiv3Err(new ErrorV3(msg, 'get-layout-failed'));
  198. }
  199. });
  200. /**
  201. * @swagger
  202. *
  203. * /customize-setting/layout:
  204. * put:
  205. * tags: [CustomizeSetting]
  206. * operationId: updateLayoutCustomizeSetting
  207. * summary: /customize-setting/layout
  208. * description: Update layout
  209. * requestBody:
  210. * required: true
  211. * content:
  212. * application/json:
  213. * schema:
  214. * $ref: '#/components/schemas/CustomizeLayout'
  215. * responses:
  216. * 200:
  217. * description: Succeeded to update layout
  218. * content:
  219. * application/json:
  220. * schema:
  221. * $ref: '#/components/schemas/CustomizeLayout'
  222. */
  223. router.put('/layout', loginRequiredStrictly, adminRequired, csrf, validator.layout, apiV3FormValidator, async(req, res) => {
  224. const requestParams = {
  225. 'customize:isContainerFluid': req.body.isContainerFluid,
  226. };
  227. try {
  228. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  229. const customizedParams = {
  230. isContainerFluid: await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid'),
  231. };
  232. return res.apiv3({ customizedParams });
  233. }
  234. catch (err) {
  235. const msg = 'Error occurred in updating layout';
  236. logger.error('Error', err);
  237. return res.apiv3Err(new ErrorV3(msg, 'update-layout-failed'));
  238. }
  239. });
  240. /**
  241. * @swagger
  242. *
  243. * /customize-setting/theme/asset-path:
  244. * put:
  245. * tags: [CustomizeSetting]
  246. * operationId: getThemeAssetPath
  247. * summary: /customize-setting/theme/asset-path
  248. * description: Get theme asset path
  249. * parameters:
  250. * - name: themeName
  251. * in: query
  252. * required: true
  253. * schema:
  254. * type: string
  255. * responses:
  256. * 200:
  257. * description: Succeeded to get theme asset path
  258. * content:
  259. * application/json:
  260. * schema:
  261. * properties:
  262. * assetPath:
  263. * type: string
  264. */
  265. router.get('/theme/asset-path', loginRequiredStrictly, adminRequired, validator.themeAssetPath, apiV3FormValidator, async(req, res) => {
  266. const { themeName } = req.query;
  267. const webpackAssetKey = `styles/theme-${themeName}.css`;
  268. const assetPath = res.locals.webpack_asset(webpackAssetKey);
  269. if (assetPath == null) {
  270. return res.apiv3Err(new ErrorV3(`The asset for '${webpackAssetKey}' is undefined.`, 'invalid-asset'));
  271. }
  272. return res.apiv3({ assetPath });
  273. });
  274. /**
  275. * @swagger
  276. *
  277. * /customize-setting/theme:
  278. * put:
  279. * tags: [CustomizeSetting]
  280. * operationId: updateThemeCustomizeSetting
  281. * summary: /customize-setting/theme
  282. * description: Update theme
  283. * requestBody:
  284. * required: true
  285. * content:
  286. * application/json:
  287. * schema:
  288. * $ref: '#/components/schemas/CustomizeTheme'
  289. * responses:
  290. * 200:
  291. * description: Succeeded to update theme
  292. * content:
  293. * application/json:
  294. * schema:
  295. * $ref: '#/components/schemas/CustomizeTheme'
  296. */
  297. router.put('/theme', loginRequiredStrictly, adminRequired, csrf, validator.theme, apiV3FormValidator, async(req, res) => {
  298. const requestParams = {
  299. 'customize:theme': req.body.themeType,
  300. };
  301. try {
  302. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  303. const customizedParams = {
  304. themeType: await crowi.configManager.getConfig('crowi', 'customize:theme'),
  305. };
  306. return res.apiv3({ customizedParams });
  307. }
  308. catch (err) {
  309. const msg = 'Error occurred in updating theme';
  310. logger.error('Error', err);
  311. return res.apiv3Err(new ErrorV3(msg, 'update-theme-failed'));
  312. }
  313. });
  314. /**
  315. * @swagger
  316. *
  317. * /customize-setting/function:
  318. * put:
  319. * tags: [CustomizeSetting]
  320. * operationId: updateFunctionCustomizeSetting
  321. * summary: /customize-setting/function
  322. * description: Update function
  323. * requestBody:
  324. * required: true
  325. * content:
  326. * application/json:
  327. * schema:
  328. * $ref: '#/components/schemas/CustomizeFunction'
  329. * responses:
  330. * 200:
  331. * description: Succeeded to update function
  332. * content:
  333. * application/json:
  334. * schema:
  335. * $ref: '#/components/schemas/CustomizeFunction'
  336. */
  337. router.put('/function', loginRequiredStrictly, adminRequired, csrf, validator.function, apiV3FormValidator, async(req, res) => {
  338. const requestParams = {
  339. 'customize:isEnabledTimeline': req.body.isEnabledTimeline,
  340. 'customize:isSavedStatesOfTabChanges': req.body.isSavedStatesOfTabChanges,
  341. 'customize:isEnabledAttachTitleHeader': req.body.isEnabledAttachTitleHeader,
  342. 'customize:showPageLimitationS': req.body.pageLimitationS,
  343. 'customize:showPageLimitationM': req.body.pageLimitationM,
  344. 'customize:showPageLimitationL': req.body.pageLimitationL,
  345. 'customize:showPageLimitationXL': req.body.pageLimitationXL,
  346. 'customize:isEnabledStaleNotification': req.body.isEnabledStaleNotification,
  347. 'customize:isAllReplyShown': req.body.isAllReplyShown,
  348. 'customize:isSearchScopeChildrenAsDefault': req.body.isSearchScopeChildrenAsDefault,
  349. };
  350. try {
  351. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  352. const customizedParams = {
  353. isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
  354. isSavedStatesOfTabChanges: await crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'),
  355. isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
  356. pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'),
  357. pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'),
  358. pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
  359. pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
  360. isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'),
  361. isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'),
  362. isSearchScopeChildrenAsDefault: await crowi.configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault'),
  363. };
  364. return res.apiv3({ customizedParams });
  365. }
  366. catch (err) {
  367. const msg = 'Error occurred in updating function';
  368. logger.error('Error', err);
  369. return res.apiv3Err(new ErrorV3(msg, 'update-function-failed'));
  370. }
  371. });
  372. /**
  373. * @swagger
  374. *
  375. * /customize-setting/highlight:
  376. * put:
  377. * tags: [CustomizeSetting]
  378. * operationId: updateHighlightCustomizeSetting
  379. * summary: /customize-setting/highlight
  380. * description: Update highlight
  381. * requestBody:
  382. * required: true
  383. * content:
  384. * application/json:
  385. * schema:
  386. * $ref: '#/components/schemas/CustomizeHighlight'
  387. * responses:
  388. * 200:
  389. * description: Succeeded to update highlight
  390. * content:
  391. * application/json:
  392. * schema:
  393. * $ref: '#/components/schemas/CustomizeHighlight'
  394. */
  395. router.put('/highlight', loginRequiredStrictly, adminRequired, csrf, validator.highlight, apiV3FormValidator, async(req, res) => {
  396. const requestParams = {
  397. 'customize:highlightJsStyle': req.body.highlightJsStyle,
  398. 'customize:highlightJsStyleBorder': req.body.highlightJsStyleBorder,
  399. };
  400. try {
  401. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  402. const customizedParams = {
  403. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  404. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  405. };
  406. return res.apiv3({ customizedParams });
  407. }
  408. catch (err) {
  409. const msg = 'Error occurred in updating highlight';
  410. logger.error('Error', err);
  411. return res.apiv3Err(new ErrorV3(msg, 'update-highlight-failed'));
  412. }
  413. });
  414. /**
  415. * @swagger
  416. *
  417. * /customize-setting/customizeTitle:
  418. * put:
  419. * tags: [CustomizeSetting]
  420. * operationId: updateCustomizeTitleCustomizeSetting
  421. * summary: /customize-setting/customizeTitle
  422. * description: Update customizeTitle
  423. * requestBody:
  424. * required: true
  425. * content:
  426. * application/json:
  427. * schema:
  428. * $ref: '#/components/schemas/CustomizeTitle'
  429. * responses:
  430. * 200:
  431. * description: Succeeded to update customizeTitle
  432. * content:
  433. * application/json:
  434. * schema:
  435. * $ref: '#/components/schemas/CustomizeTitle'
  436. */
  437. router.put('/customize-title', loginRequiredStrictly, adminRequired, csrf, validator.customizeTitle, apiV3FormValidator, async(req, res) => {
  438. const requestParams = {
  439. 'customize:title': req.body.customizeTitle,
  440. };
  441. try {
  442. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  443. crowi.customizeService.publishUpdatedMessage();
  444. const customizedParams = {
  445. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  446. };
  447. customizeService.initCustomTitle();
  448. return res.apiv3({ customizedParams });
  449. }
  450. catch (err) {
  451. const msg = 'Error occurred in updating customizeTitle';
  452. logger.error('Error', err);
  453. return res.apiv3Err(new ErrorV3(msg, 'update-customizeTitle-failed'));
  454. }
  455. });
  456. /**
  457. * @swagger
  458. *
  459. * /customize-setting/customizeHeader:
  460. * put:
  461. * tags: [CustomizeSetting]
  462. * operationId: updateCustomizeHeaderCustomizeSetting
  463. * summary: /customize-setting/customizeHeader
  464. * description: Update customizeHeader
  465. * requestBody:
  466. * required: true
  467. * content:
  468. * application/json:
  469. * schema:
  470. * $ref: '#/components/schemas/CustomizeHeader'
  471. * responses:
  472. * 200:
  473. * description: Succeeded to update customize header
  474. * content:
  475. * application/json:
  476. * schema:
  477. * $ref: '#/components/schemas/CustomizeHeader'
  478. */
  479. router.put('/customize-header', loginRequiredStrictly, adminRequired, csrf, validator.customizeHeader, apiV3FormValidator, async(req, res) => {
  480. const requestParams = {
  481. 'customize:header': req.body.customizeHeader,
  482. };
  483. try {
  484. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  485. const customizedParams = {
  486. customizeHeader: await crowi.configManager.getConfig('crowi', 'customize:header'),
  487. };
  488. return res.apiv3({ customizedParams });
  489. }
  490. catch (err) {
  491. const msg = 'Error occurred in updating customizeHeader';
  492. logger.error('Error', err);
  493. return res.apiv3Err(new ErrorV3(msg, 'update-customizeHeader-failed'));
  494. }
  495. });
  496. /**
  497. * @swagger
  498. *
  499. * /customize-setting/customizeCss:
  500. * put:
  501. * tags: [CustomizeSetting]
  502. * operationId: updateCustomizeCssCustomizeSetting
  503. * summary: /customize-setting/customizeCss
  504. * description: Update customizeCss
  505. * requestBody:
  506. * required: true
  507. * content:
  508. * application/json:
  509. * schema:
  510. * $ref: '#/components/schemas/CustomizeCss'
  511. * responses:
  512. * 200:
  513. * description: Succeeded to update customize css
  514. * content:
  515. * application/json:
  516. * schema:
  517. * $ref: '#/components/schemas/CustomizeCss'
  518. */
  519. router.put('/customize-css', loginRequiredStrictly, adminRequired, csrf, validator.customizeCss, apiV3FormValidator, async(req, res) => {
  520. const requestParams = {
  521. 'customize:css': req.body.customizeCss,
  522. };
  523. try {
  524. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  525. crowi.customizeService.publishUpdatedMessage();
  526. const customizedParams = {
  527. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  528. };
  529. customizeService.initCustomCss();
  530. return res.apiv3({ customizedParams });
  531. }
  532. catch (err) {
  533. const msg = 'Error occurred in updating customizeCss';
  534. logger.error('Error', err);
  535. return res.apiv3Err(new ErrorV3(msg, 'update-customizeCss-failed'));
  536. }
  537. });
  538. /**
  539. * @swagger
  540. *
  541. * /customize-setting/customizeScript:
  542. * put:
  543. * tags: [CustomizeSetting]
  544. * operationId: updateCustomizeScriptCustomizeSetting
  545. * summary: /customize-setting/customizeScript
  546. * description: Update customizeScript
  547. * requestBody:
  548. * required: true
  549. * content:
  550. * application/json:
  551. * schema:
  552. * $ref: '#/components/schemas/CustomizeScript'
  553. * responses:
  554. * 200:
  555. * description: Succeeded to update customize script
  556. * content:
  557. * application/json:
  558. * schema:
  559. * $ref: '#/components/schemas/CustomizeScript'
  560. */
  561. router.put('/customize-script', loginRequiredStrictly, adminRequired, csrf, validator.customizeScript, apiV3FormValidator, async(req, res) => {
  562. const requestParams = {
  563. 'customize:script': req.body.customizeScript,
  564. };
  565. try {
  566. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  567. const customizedParams = {
  568. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  569. };
  570. return res.apiv3({ customizedParams });
  571. }
  572. catch (err) {
  573. const msg = 'Error occurred in updating customizeScript';
  574. logger.error('Error', err);
  575. return res.apiv3Err(new ErrorV3(msg, 'update-customizeScript-failed'));
  576. }
  577. });
  578. return router;
  579. };