customize-setting.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. /* eslint-disable no-unused-vars */
  2. import { GrowiPluginType } from '@growi/core';
  3. import { ErrorV3 } from '@growi/core/dist/models';
  4. import express from 'express';
  5. import { body } from 'express-validator';
  6. import multer from 'multer';
  7. import { GrowiPlugin } from '~/features/growi-plugin/server/models';
  8. import { SupportedAction } from '~/interfaces/activity';
  9. import { AttachmentType } from '~/server/interfaces/attachment';
  10. import loggerFactory from '~/utils/logger';
  11. import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
  12. import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
  13. const logger = loggerFactory('growi:routes:apiv3:customize-setting');
  14. const router = express.Router();
  15. /**
  16. * @swagger
  17. * tags:
  18. * name: CustomizeSetting
  19. */
  20. /**
  21. * @swagger
  22. *
  23. * components:
  24. * schemas:
  25. * CustomizeLayout:
  26. * description: CustomizeLayout
  27. * type: object
  28. * properties:
  29. * isContainerFluid:
  30. * type: boolean
  31. * CustomizeTheme:
  32. * description: CustomizeTheme
  33. * type: object
  34. * properties:
  35. * themeType:
  36. * type: string
  37. * CustomizeFunction:
  38. * description: CustomizeFunction
  39. * type: object
  40. * properties:
  41. * isEnabledTimeline:
  42. * type: boolean
  43. * isEnabledAttachTitleHeader:
  44. * type: boolean
  45. * pageLimitationS:
  46. * type: number
  47. * pageLimitationM:
  48. * type: number
  49. * isEnabledStaleNotification:
  50. * type: boolean
  51. * isAllReplyShown:
  52. * type: boolean
  53. * isSearchScopeChildrenAsDefault:
  54. * type: boolean
  55. * CustomizeHighlight:
  56. * description: CustomizeHighlight
  57. * type: object
  58. * properties:
  59. * styleName:
  60. * type: string
  61. * styleBorder:
  62. * type: boolean
  63. * CustomizeTitle:
  64. * description: CustomizeTitle
  65. * type: object
  66. * properties:
  67. * customizeTitle:
  68. * type: string
  69. * CustomizeNoscript:
  70. * description: CustomizeNoscript
  71. * type: object
  72. * properties:
  73. * customizeNoscript:
  74. * type: string
  75. * CustomizeCss:
  76. * description: CustomizeCss
  77. * type: object
  78. * properties:
  79. * customizeCss:
  80. * type: string
  81. * CustomizeScript:
  82. * description: CustomizeScript
  83. * type: object
  84. * properties:
  85. * customizeScript:
  86. * type: string
  87. */
  88. module.exports = (crowi) => {
  89. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  90. const adminRequired = require('../../middlewares/admin-required')(crowi);
  91. const addActivity = generateAddActivityMiddleware(crowi);
  92. const activityEvent = crowi.event('activity');
  93. const { customizeService, attachmentService } = crowi;
  94. const Attachment = crowi.model('Attachment');
  95. const uploads = multer({ dest: `${crowi.tmpDir}uploads` });
  96. const validator = {
  97. layout: [
  98. body('isContainerFluid').isBoolean(),
  99. ],
  100. theme: [
  101. body('theme').isString(),
  102. ],
  103. sidebar: [
  104. body('isSidebarCollapsedMode').isBoolean(),
  105. ],
  106. function: [
  107. body('isEnabledTimeline').isBoolean(),
  108. body('isEnabledAttachTitleHeader').isBoolean(),
  109. body('pageLimitationS').isInt().isInt({ min: 1, max: 1000 }),
  110. body('pageLimitationM').isInt().isInt({ min: 1, max: 1000 }),
  111. body('pageLimitationL').isInt().isInt({ min: 1, max: 1000 }),
  112. body('pageLimitationXL').isInt().isInt({ min: 1, max: 1000 }),
  113. body('isEnabledStaleNotification').isBoolean(),
  114. body('isAllReplyShown').isBoolean(),
  115. body('isSearchScopeChildrenAsDefault').isBoolean(),
  116. ],
  117. CustomizePresentation: [
  118. body('isEnabledMarp').isBoolean(),
  119. ],
  120. customizeTitle: [
  121. body('customizeTitle').isString(),
  122. ],
  123. highlight: [
  124. body('highlightJsStyle').isString().isIn([
  125. 'github', 'github-gist', 'atom-one-light', 'xcode', 'vs', 'atom-one-dark', 'hybrid', 'monokai', 'tomorrow-night', 'vs2015',
  126. ]),
  127. body('highlightJsStyleBorder').isBoolean(),
  128. ],
  129. customizeScript: [
  130. body('customizeScript').isString(),
  131. ],
  132. customizeCss: [
  133. body('customizeCss').isString(),
  134. ],
  135. customizeNoscript: [
  136. body('customizeNoscript').isString(),
  137. ],
  138. logo: [
  139. body('isDefaultLogo').isBoolean().optional({ nullable: true }),
  140. body('customizedLogoSrc').isString().optional({ nullable: true }),
  141. ],
  142. };
  143. /**
  144. * @swagger
  145. *
  146. * /customize-setting:
  147. * get:
  148. * tags: [CustomizeSetting]
  149. * operationId: getCustomizeSetting
  150. * summary: /customize-setting
  151. * description: Get customize parameters
  152. * responses:
  153. * 200:
  154. * description: params of customize
  155. * content:
  156. * application/json:
  157. * schema:
  158. * properties:
  159. * customizeParams:
  160. * type: object
  161. * description: customize params
  162. */
  163. router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
  164. const customizeParams = {
  165. isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
  166. isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
  167. pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'),
  168. pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'),
  169. pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
  170. pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
  171. isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'),
  172. isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'),
  173. isSearchScopeChildrenAsDefault: await crowi.configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault'),
  174. isEnabledMarp: await crowi.configManager.getConfig('crowi', 'customize:isEnabledMarp'),
  175. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  176. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  177. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  178. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  179. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  180. customizeNoscript: await crowi.configManager.getConfig('crowi', 'customize:noscript'),
  181. };
  182. return res.apiv3({ customizeParams });
  183. });
  184. /**
  185. * @swagger
  186. *
  187. * /customize-setting/layout:
  188. * get:
  189. * tags: [CustomizeSetting]
  190. * operationId: getLayoutCustomizeSetting
  191. * summary: /customize-setting/layout
  192. * description: Get layout
  193. * responses:
  194. * 200:
  195. * description: Succeeded to get layout
  196. * content:
  197. * application/json:
  198. * schema:
  199. * $ref: '#/components/schemas/CustomizeLayout'
  200. */
  201. router.get('/layout', loginRequiredStrictly, adminRequired, async(req, res) => {
  202. try {
  203. const isContainerFluid = await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid');
  204. return res.apiv3({ isContainerFluid });
  205. }
  206. catch (err) {
  207. const msg = 'Error occurred in getting layout';
  208. logger.error('Error', err);
  209. return res.apiv3Err(new ErrorV3(msg, 'get-layout-failed'));
  210. }
  211. });
  212. /**
  213. * @swagger
  214. *
  215. * /customize-setting/layout:
  216. * put:
  217. * tags: [CustomizeSetting]
  218. * operationId: updateLayoutCustomizeSetting
  219. * summary: /customize-setting/layout
  220. * description: Update layout
  221. * requestBody:
  222. * required: true
  223. * content:
  224. * application/json:
  225. * schema:
  226. * $ref: '#/components/schemas/CustomizeLayout'
  227. * responses:
  228. * 200:
  229. * description: Succeeded to update layout
  230. * content:
  231. * application/json:
  232. * schema:
  233. * $ref: '#/components/schemas/CustomizeLayout'
  234. */
  235. router.put('/layout', loginRequiredStrictly, adminRequired, addActivity, validator.layout, apiV3FormValidator, async(req, res) => {
  236. const requestParams = {
  237. 'customize:isContainerFluid': req.body.isContainerFluid,
  238. };
  239. try {
  240. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  241. const customizedParams = {
  242. isContainerFluid: await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid'),
  243. };
  244. const parameters = { action: SupportedAction.ACTION_ADMIN_LAYOUT_UPDATE };
  245. activityEvent.emit('update', res.locals.activity._id, parameters);
  246. return res.apiv3({ customizedParams });
  247. }
  248. catch (err) {
  249. const msg = 'Error occurred in updating layout';
  250. logger.error('Error', err);
  251. return res.apiv3Err(new ErrorV3(msg, 'update-layout-failed'));
  252. }
  253. });
  254. router.get('/theme', loginRequiredStrictly, adminRequired, async(req, res) => {
  255. try {
  256. const currentTheme = await crowi.configManager.getConfig('crowi', 'customize:theme');
  257. // retrieve plugin manifests
  258. const themePlugins = await GrowiPlugin.findEnabledPluginsByType(GrowiPluginType.Theme);
  259. const pluginThemesMetadatas = themePlugins
  260. .map(themePlugin => themePlugin.meta.themes)
  261. .flat();
  262. return res.apiv3({ currentTheme, pluginThemesMetadatas });
  263. }
  264. catch (err) {
  265. const msg = 'Error occurred in getting theme';
  266. logger.error('Error', err);
  267. return res.apiv3Err(new ErrorV3(msg, 'get-theme-failed'));
  268. }
  269. });
  270. /**
  271. * @swagger
  272. *
  273. * /customize-setting/theme:
  274. * put:
  275. * tags: [CustomizeSetting]
  276. * operationId: updateThemeCustomizeSetting
  277. * summary: /customize-setting/theme
  278. * description: Update theme
  279. * requestBody:
  280. * required: true
  281. * content:
  282. * application/json:
  283. * schema:
  284. * $ref: '#/components/schemas/CustomizeTheme'
  285. * responses:
  286. * 200:
  287. * description: Succeeded to update theme
  288. * content:
  289. * application/json:
  290. * schema:
  291. * $ref: '#/components/schemas/CustomizeTheme'
  292. */
  293. router.put('/theme', loginRequiredStrictly, adminRequired, addActivity, validator.theme, apiV3FormValidator, async(req, res) => {
  294. const requestParams = {
  295. 'customize:theme': req.body.theme,
  296. };
  297. try {
  298. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  299. const customizedParams = {
  300. theme: await crowi.configManager.getConfig('crowi', 'customize:theme'),
  301. };
  302. customizeService.initGrowiTheme();
  303. const parameters = { action: SupportedAction.ACTION_ADMIN_THEME_UPDATE };
  304. activityEvent.emit('update', res.locals.activity._id, parameters);
  305. return res.apiv3({ customizedParams });
  306. }
  307. catch (err) {
  308. const msg = 'Error occurred in updating theme';
  309. logger.error('Error', err);
  310. return res.apiv3Err(new ErrorV3(msg, 'update-theme-failed'));
  311. }
  312. });
  313. // sidebar
  314. router.get('/sidebar', loginRequiredStrictly, adminRequired, async(req, res) => {
  315. try {
  316. const isSidebarCollapsedMode = await crowi.configManager.getConfig('crowi', 'customize:isSidebarCollapsedMode');
  317. return res.apiv3({ isSidebarCollapsedMode });
  318. }
  319. catch (err) {
  320. const msg = 'Error occurred in getting sidebar';
  321. logger.error('Error', err);
  322. return res.apiv3Err(new ErrorV3(msg, 'get-sidebar-failed'));
  323. }
  324. });
  325. router.put('/sidebar', loginRequiredStrictly, adminRequired, validator.sidebar, apiV3FormValidator, addActivity, async(req, res) => {
  326. const requestParams = {
  327. 'customize:isSidebarCollapsedMode': req.body.isSidebarCollapsedMode,
  328. };
  329. try {
  330. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  331. const customizedParams = {
  332. isSidebarCollapsedMode: await crowi.configManager.getConfig('crowi', 'customize:isSidebarCollapsedMode'),
  333. };
  334. activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ADMIN_SIDEBAR_UPDATE });
  335. return res.apiv3({ customizedParams });
  336. }
  337. catch (err) {
  338. const msg = 'Error occurred in updating sidebar';
  339. logger.error('Error', err);
  340. return res.apiv3Err(new ErrorV3(msg, 'update-sidebar-failed'));
  341. }
  342. });
  343. /**
  344. * @swagger
  345. *
  346. * /customize-setting/function:
  347. * put:
  348. * tags: [CustomizeSetting]
  349. * operationId: updateFunctionCustomizeSetting
  350. * summary: /customize-setting/function
  351. * description: Update function
  352. * requestBody:
  353. * required: true
  354. * content:
  355. * application/json:
  356. * schema:
  357. * $ref: '#/components/schemas/CustomizeFunction'
  358. * responses:
  359. * 200:
  360. * description: Succeeded to update function
  361. * content:
  362. * application/json:
  363. * schema:
  364. * $ref: '#/components/schemas/CustomizeFunction'
  365. */
  366. router.put('/function', loginRequiredStrictly, adminRequired, addActivity, validator.function, apiV3FormValidator, async(req, res) => {
  367. const requestParams = {
  368. 'customize:isEnabledTimeline': req.body.isEnabledTimeline,
  369. 'customize:isEnabledAttachTitleHeader': req.body.isEnabledAttachTitleHeader,
  370. 'customize:showPageLimitationS': req.body.pageLimitationS,
  371. 'customize:showPageLimitationM': req.body.pageLimitationM,
  372. 'customize:showPageLimitationL': req.body.pageLimitationL,
  373. 'customize:showPageLimitationXL': req.body.pageLimitationXL,
  374. 'customize:isEnabledStaleNotification': req.body.isEnabledStaleNotification,
  375. 'customize:isAllReplyShown': req.body.isAllReplyShown,
  376. 'customize:isSearchScopeChildrenAsDefault': req.body.isSearchScopeChildrenAsDefault,
  377. };
  378. try {
  379. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  380. const customizedParams = {
  381. isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
  382. isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
  383. pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'),
  384. pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'),
  385. pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
  386. pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
  387. isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'),
  388. isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'),
  389. isSearchScopeChildrenAsDefault: await crowi.configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault'),
  390. };
  391. const parameters = { action: SupportedAction.ACTION_ADMIN_FUNCTION_UPDATE };
  392. activityEvent.emit('update', res.locals.activity._id, parameters);
  393. return res.apiv3({ customizedParams });
  394. }
  395. catch (err) {
  396. const msg = 'Error occurred in updating function';
  397. logger.error('Error', err);
  398. return res.apiv3Err(new ErrorV3(msg, 'update-function-failed'));
  399. }
  400. });
  401. router.put('/presentation', loginRequiredStrictly, adminRequired, addActivity, validator.CustomizePresentation, apiV3FormValidator, async(req, res) => {
  402. const requestParams = {
  403. 'customize:isEnabledMarp': req.body.isEnabledMarp,
  404. };
  405. try {
  406. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  407. const customizedParams = {
  408. isEnabledMarp: await crowi.configManager.getConfig('crowi', 'customize:isEnabledMarp'),
  409. };
  410. const parameters = { action: SupportedAction.ACTION_ADMIN_FUNCTION_UPDATE };
  411. activityEvent.emit('update', res.locals.activity._id, parameters);
  412. return res.apiv3({ customizedParams });
  413. }
  414. catch (err) {
  415. const msg = 'Error occurred in updating presentaion';
  416. logger.error('Error', err);
  417. return res.apiv3Err(new ErrorV3(msg, 'update-presentation-failed'));
  418. }
  419. });
  420. /**
  421. * @swagger
  422. *
  423. * /customize-setting/highlight:
  424. * put:
  425. * tags: [CustomizeSetting]
  426. * operationId: updateHighlightCustomizeSetting
  427. * summary: /customize-setting/highlight
  428. * description: Update highlight
  429. * requestBody:
  430. * required: true
  431. * content:
  432. * application/json:
  433. * schema:
  434. * $ref: '#/components/schemas/CustomizeHighlight'
  435. * responses:
  436. * 200:
  437. * description: Succeeded to update highlight
  438. * content:
  439. * application/json:
  440. * schema:
  441. * $ref: '#/components/schemas/CustomizeHighlight'
  442. */
  443. router.put('/highlight', loginRequiredStrictly, adminRequired, addActivity, validator.highlight, apiV3FormValidator, async(req, res) => {
  444. const requestParams = {
  445. 'customize:highlightJsStyle': req.body.highlightJsStyle,
  446. 'customize:highlightJsStyleBorder': req.body.highlightJsStyleBorder,
  447. };
  448. try {
  449. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  450. const customizedParams = {
  451. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  452. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  453. };
  454. const parameters = { action: SupportedAction.ACTION_ADMIN_CODE_HIGHLIGHT_UPDATE };
  455. activityEvent.emit('update', res.locals.activity._id, parameters);
  456. return res.apiv3({ customizedParams });
  457. }
  458. catch (err) {
  459. const msg = 'Error occurred in updating highlight';
  460. logger.error('Error', err);
  461. return res.apiv3Err(new ErrorV3(msg, 'update-highlight-failed'));
  462. }
  463. });
  464. /**
  465. * @swagger
  466. *
  467. * /customize-setting/customizeTitle:
  468. * put:
  469. * tags: [CustomizeSetting]
  470. * operationId: updateCustomizeTitleCustomizeSetting
  471. * summary: /customize-setting/customizeTitle
  472. * description: Update customizeTitle
  473. * requestBody:
  474. * required: true
  475. * content:
  476. * application/json:
  477. * schema:
  478. * $ref: '#/components/schemas/CustomizeTitle'
  479. * responses:
  480. * 200:
  481. * description: Succeeded to update customizeTitle
  482. * content:
  483. * application/json:
  484. * schema:
  485. * $ref: '#/components/schemas/CustomizeTitle'
  486. */
  487. router.put('/customize-title', loginRequiredStrictly, adminRequired, addActivity, validator.customizeTitle, apiV3FormValidator, async(req, res) => {
  488. const requestParams = {
  489. 'customize:title': req.body.customizeTitle,
  490. };
  491. try {
  492. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  493. crowi.customizeService.publishUpdatedMessage();
  494. const customizedParams = {
  495. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  496. };
  497. customizeService.initCustomTitle();
  498. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_TITLE_UPDATE };
  499. activityEvent.emit('update', res.locals.activity._id, parameters);
  500. return res.apiv3({ customizedParams });
  501. }
  502. catch (err) {
  503. const msg = 'Error occurred in updating customizeTitle';
  504. logger.error('Error', err);
  505. return res.apiv3Err(new ErrorV3(msg, 'update-customizeTitle-failed'));
  506. }
  507. });
  508. /**
  509. * @swagger
  510. *
  511. * /customize-setting/customize-noscript:
  512. * put:
  513. * tags: [CustomizeSetting]
  514. * operationId: updateCustomizeNoscriptCustomizeSetting
  515. * summary: /customize-setting/customize-noscript
  516. * description: Update customizeNoscript
  517. * requestBody:
  518. * required: true
  519. * content:
  520. * application/json:
  521. * schema:
  522. * $ref: '#/components/schemas/CustomizeNoscript'
  523. * responses:
  524. * 200:
  525. * description: Succeeded to update customize header
  526. * content:
  527. * application/json:
  528. * schema:
  529. * $ref: '#/components/schemas/CustomizeNoscript'
  530. */
  531. router.put('/customize-noscript', loginRequiredStrictly, adminRequired, addActivity, validator.customizeNoscript, apiV3FormValidator, async(req, res) => {
  532. const requestParams = {
  533. 'customize:noscript': req.body.customizeNoscript,
  534. };
  535. try {
  536. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  537. const customizedParams = {
  538. customizeNoscript: await crowi.configManager.getConfig('crowi', 'customize:noscript'),
  539. };
  540. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_NOSCRIPT_UPDATE };
  541. activityEvent.emit('update', res.locals.activity._id, parameters);
  542. return res.apiv3({ customizedParams });
  543. }
  544. catch (err) {
  545. const msg = 'Error occurred in updating customizeNoscript';
  546. logger.error('Error', err);
  547. return res.apiv3Err(new ErrorV3(msg, 'update-customizeNoscript-failed'));
  548. }
  549. });
  550. /**
  551. * @swagger
  552. *
  553. * /customize-setting/customizeCss:
  554. * put:
  555. * tags: [CustomizeSetting]
  556. * operationId: updateCustomizeCssCustomizeSetting
  557. * summary: /customize-setting/customizeCss
  558. * description: Update customizeCss
  559. * requestBody:
  560. * required: true
  561. * content:
  562. * application/json:
  563. * schema:
  564. * $ref: '#/components/schemas/CustomizeCss'
  565. * responses:
  566. * 200:
  567. * description: Succeeded to update customize css
  568. * content:
  569. * application/json:
  570. * schema:
  571. * $ref: '#/components/schemas/CustomizeCss'
  572. */
  573. router.put('/customize-css', loginRequiredStrictly, adminRequired, addActivity, validator.customizeCss, apiV3FormValidator, async(req, res) => {
  574. const requestParams = {
  575. 'customize:css': req.body.customizeCss,
  576. };
  577. try {
  578. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  579. crowi.customizeService.publishUpdatedMessage();
  580. const customizedParams = {
  581. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  582. };
  583. customizeService.initCustomCss();
  584. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_CSS_UPDATE };
  585. activityEvent.emit('update', res.locals.activity._id, parameters);
  586. return res.apiv3({ customizedParams });
  587. }
  588. catch (err) {
  589. const msg = 'Error occurred in updating customizeCss';
  590. logger.error('Error', err);
  591. return res.apiv3Err(new ErrorV3(msg, 'update-customizeCss-failed'));
  592. }
  593. });
  594. /**
  595. * @swagger
  596. *
  597. * /customize-setting/customizeScript:
  598. * put:
  599. * tags: [CustomizeSetting]
  600. * operationId: updateCustomizeScriptCustomizeSetting
  601. * summary: /customize-setting/customizeScript
  602. * description: Update customizeScript
  603. * requestBody:
  604. * required: true
  605. * content:
  606. * application/json:
  607. * schema:
  608. * $ref: '#/components/schemas/CustomizeScript'
  609. * responses:
  610. * 200:
  611. * description: Succeeded to update customize script
  612. * content:
  613. * application/json:
  614. * schema:
  615. * $ref: '#/components/schemas/CustomizeScript'
  616. */
  617. router.put('/customize-script', loginRequiredStrictly, adminRequired, addActivity, validator.customizeScript, apiV3FormValidator, async(req, res) => {
  618. const requestParams = {
  619. 'customize:script': req.body.customizeScript,
  620. };
  621. try {
  622. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  623. const customizedParams = {
  624. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  625. };
  626. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_SCRIPT_UPDATE };
  627. activityEvent.emit('update', res.locals.activity._id, parameters);
  628. return res.apiv3({ customizedParams });
  629. }
  630. catch (err) {
  631. const msg = 'Error occurred in updating customizeScript';
  632. logger.error('Error', err);
  633. return res.apiv3Err(new ErrorV3(msg, 'update-customizeScript-failed'));
  634. }
  635. });
  636. router.put('/customize-logo', loginRequiredStrictly, adminRequired, validator.logo, apiV3FormValidator, async(req, res) => {
  637. const {
  638. isDefaultLogo,
  639. } = req.body;
  640. const requestParams = {
  641. 'customize:isDefaultLogo': isDefaultLogo,
  642. };
  643. try {
  644. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  645. const customizedParams = {
  646. isDefaultLogo: await crowi.configManager.getConfig('crowi', 'customize:isDefaultLogo'),
  647. };
  648. return res.apiv3({ customizedParams });
  649. }
  650. catch (err) {
  651. const msg = 'Error occurred in updating customizeLogo';
  652. logger.error('Error', err);
  653. return res.apiv3Err(new ErrorV3(msg, 'update-customizeLogo-failed'));
  654. }
  655. });
  656. router.post('/upload-brand-logo', uploads.single('file'), loginRequiredStrictly,
  657. adminRequired, validator.logo, apiV3FormValidator, async(req, res) => {
  658. if (req.file == null) {
  659. return res.apiv3Err(new ErrorV3('File error.', 'upload-brand-logo-failed'));
  660. }
  661. if (req.user == null) {
  662. return res.apiv3Err(new ErrorV3('param "user" must be set.', 'upload-brand-logo-failed'));
  663. }
  664. const file = req.file;
  665. // check type
  666. const acceptableFileType = /image\/.+/;
  667. if (!file.mimetype.match(acceptableFileType)) {
  668. const msg = 'File type error. Only image files is allowed to set as user picture.';
  669. return res.apiv3Err(new ErrorV3(msg, 'upload-brand-logo-failed'));
  670. }
  671. // Check if previous attachment exists and remove it
  672. const attachments = await Attachment.find({ attachmentType: AttachmentType.BRAND_LOGO });
  673. if (attachments != null) {
  674. await attachmentService.removeAllAttachments(attachments);
  675. }
  676. let attachment;
  677. try {
  678. attachment = await attachmentService.createAttachment(file, req.user, null, AttachmentType.BRAND_LOGO);
  679. }
  680. catch (err) {
  681. logger.error(err);
  682. return res.apiv3Err(new ErrorV3(err.message, 'upload-brand-logo-failed'));
  683. }
  684. attachment.toObject({ virtuals: true });
  685. return res.apiv3({ attachment });
  686. });
  687. router.delete('/delete-brand-logo', loginRequiredStrictly, adminRequired, async(req, res) => {
  688. const attachments = await Attachment.find({ attachmentType: AttachmentType.BRAND_LOGO });
  689. if (attachments == null) {
  690. return res.apiv3Err(new ErrorV3('attachment not found', 'delete-brand-logo-failed'));
  691. }
  692. try {
  693. await attachmentService.removeAllAttachments(attachments);
  694. }
  695. catch (err) {
  696. logger.error(err);
  697. return res.status(500).apiv3Err(new ErrorV3('Error while deleting logo', 'delete-brand-logo-failed'));
  698. }
  699. return res.apiv3({});
  700. });
  701. return router;
  702. };