customize-setting.js 28 KB

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