customize-setting.js 27 KB

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