customize-setting.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  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. * CustomizeHeader:
  70. * description: CustomizeHeader
  71. * type: object
  72. * properties:
  73. * customizeHeader:
  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('isSidebarDrawerMode').isBoolean(),
  105. body('isSidebarClosedAtDockMode').isBoolean(),
  106. ],
  107. function: [
  108. body('isEnabledTimeline').isBoolean(),
  109. body('isEnabledAttachTitleHeader').isBoolean(),
  110. body('pageLimitationS').isInt().isInt({ min: 1, max: 1000 }),
  111. body('pageLimitationM').isInt().isInt({ min: 1, max: 1000 }),
  112. body('pageLimitationL').isInt().isInt({ min: 1, max: 1000 }),
  113. body('pageLimitationXL').isInt().isInt({ min: 1, max: 1000 }),
  114. body('isEnabledStaleNotification').isBoolean(),
  115. body('isAllReplyShown').isBoolean(),
  116. body('isSearchScopeChildrenAsDefault').isBoolean(),
  117. ],
  118. customizeTitle: [
  119. body('customizeTitle').isString(),
  120. ],
  121. customizeHeader: [
  122. body('customizeHeader').isString(),
  123. ],
  124. highlight: [
  125. body('highlightJsStyle').isString().isIn([
  126. 'github', 'github-gist', 'atom-one-light', 'xcode', 'vs', 'atom-one-dark', 'hybrid', 'monokai', 'tomorrow-night', 'vs2015',
  127. ]),
  128. body('highlightJsStyleBorder').isBoolean(),
  129. ],
  130. customizeCss: [
  131. body('customizeCss').isString(),
  132. ],
  133. customizeScript: [
  134. body('customizeScript').isString(),
  135. ],
  136. logo: [
  137. body('isDefaultLogo').isBoolean().optional({ nullable: true }),
  138. body('customizedLogoSrc').isString().optional({ nullable: true }),
  139. ],
  140. };
  141. /**
  142. * @swagger
  143. *
  144. * /customize-setting:
  145. * get:
  146. * tags: [CustomizeSetting]
  147. * operationId: getCustomizeSetting
  148. * summary: /customize-setting
  149. * description: Get customize parameters
  150. * responses:
  151. * 200:
  152. * description: params of customize
  153. * content:
  154. * application/json:
  155. * schema:
  156. * properties:
  157. * customizeParams:
  158. * type: object
  159. * description: customize params
  160. */
  161. router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
  162. const customizeParams = {
  163. isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
  164. isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
  165. pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'),
  166. pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'),
  167. pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
  168. pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
  169. isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'),
  170. isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'),
  171. isSearchScopeChildrenAsDefault: await crowi.configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault'),
  172. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  173. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  174. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  175. customizeHeader: await crowi.configManager.getConfig('crowi', 'customize:header'),
  176. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  177. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  178. };
  179. return res.apiv3({ customizeParams });
  180. });
  181. /**
  182. * @swagger
  183. *
  184. * /customize-setting/layout:
  185. * get:
  186. * tags: [CustomizeSetting]
  187. * operationId: getLayoutCustomizeSetting
  188. * summary: /customize-setting/layout
  189. * description: Get layout
  190. * responses:
  191. * 200:
  192. * description: Succeeded to get layout
  193. * content:
  194. * application/json:
  195. * schema:
  196. * $ref: '#/components/schemas/CustomizeLayout'
  197. */
  198. router.get('/layout', loginRequiredStrictly, adminRequired, async(req, res) => {
  199. try {
  200. const isContainerFluid = await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid');
  201. return res.apiv3({ isContainerFluid });
  202. }
  203. catch (err) {
  204. const msg = 'Error occurred in getting layout';
  205. logger.error('Error', err);
  206. return res.apiv3Err(new ErrorV3(msg, 'get-layout-failed'));
  207. }
  208. });
  209. /**
  210. * @swagger
  211. *
  212. * /customize-setting/layout:
  213. * put:
  214. * tags: [CustomizeSetting]
  215. * operationId: updateLayoutCustomizeSetting
  216. * summary: /customize-setting/layout
  217. * description: Update layout
  218. * requestBody:
  219. * required: true
  220. * content:
  221. * application/json:
  222. * schema:
  223. * $ref: '#/components/schemas/CustomizeLayout'
  224. * responses:
  225. * 200:
  226. * description: Succeeded to update layout
  227. * content:
  228. * application/json:
  229. * schema:
  230. * $ref: '#/components/schemas/CustomizeLayout'
  231. */
  232. router.put('/layout', loginRequiredStrictly, adminRequired, addActivity, validator.layout, apiV3FormValidator, async(req, res) => {
  233. const requestParams = {
  234. 'customize:isContainerFluid': req.body.isContainerFluid,
  235. };
  236. try {
  237. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  238. const customizedParams = {
  239. isContainerFluid: await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid'),
  240. };
  241. const parameters = { action: SupportedAction.ACTION_ADMIN_LAYOUT_UPDATE };
  242. activityEvent.emit('update', res.locals.activity._id, parameters);
  243. return res.apiv3({ customizedParams });
  244. }
  245. catch (err) {
  246. const msg = 'Error occurred in updating layout';
  247. logger.error('Error', err);
  248. return res.apiv3Err(new ErrorV3(msg, 'update-layout-failed'));
  249. }
  250. });
  251. router.get('/theme', loginRequiredStrictly, adminRequired, async(req, res) => {
  252. try {
  253. const currentTheme = await crowi.configManager.getConfig('crowi', 'customize:theme');
  254. // retrieve plugin manifests
  255. const GrowiPluginModel = mongoose.model('GrowiPlugin');
  256. const themePlugins = await GrowiPluginModel.findEnabledPluginsIncludingAnyTypes([GrowiPluginResourceType.Theme]);
  257. const pluginThemesMetadatas = themePlugins
  258. .map(themePlugin => themePlugin.meta.themes)
  259. .flat();
  260. return res.apiv3({ currentTheme, pluginThemesMetadatas });
  261. }
  262. catch (err) {
  263. const msg = 'Error occurred in getting theme';
  264. logger.error('Error', err);
  265. return res.apiv3Err(new ErrorV3(msg, 'get-theme-failed'));
  266. }
  267. });
  268. /**
  269. * @swagger
  270. *
  271. * /customize-setting/theme:
  272. * put:
  273. * tags: [CustomizeSetting]
  274. * operationId: updateThemeCustomizeSetting
  275. * summary: /customize-setting/theme
  276. * description: Update theme
  277. * requestBody:
  278. * required: true
  279. * content:
  280. * application/json:
  281. * schema:
  282. * $ref: '#/components/schemas/CustomizeTheme'
  283. * responses:
  284. * 200:
  285. * description: Succeeded to update theme
  286. * content:
  287. * application/json:
  288. * schema:
  289. * $ref: '#/components/schemas/CustomizeTheme'
  290. */
  291. router.put('/theme', loginRequiredStrictly, adminRequired, addActivity, validator.theme, apiV3FormValidator, async(req, res) => {
  292. const requestParams = {
  293. 'customize:theme': req.body.theme,
  294. };
  295. try {
  296. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  297. const customizedParams = {
  298. theme: await crowi.configManager.getConfig('crowi', 'customize:theme'),
  299. };
  300. const parameters = { action: SupportedAction.ACTION_ADMIN_THEME_UPDATE };
  301. activityEvent.emit('update', res.locals.activity._id, parameters);
  302. return res.apiv3({ customizedParams });
  303. }
  304. catch (err) {
  305. const msg = 'Error occurred in updating theme';
  306. logger.error('Error', err);
  307. return res.apiv3Err(new ErrorV3(msg, 'update-theme-failed'));
  308. }
  309. });
  310. // sidebar
  311. router.get('/sidebar', loginRequiredStrictly, adminRequired, async(req, res) => {
  312. try {
  313. const isSidebarDrawerMode = await crowi.configManager.getConfig('crowi', 'customize:isSidebarDrawerMode');
  314. const isSidebarClosedAtDockMode = await crowi.configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode');
  315. return res.apiv3({ isSidebarDrawerMode, isSidebarClosedAtDockMode });
  316. }
  317. catch (err) {
  318. const msg = 'Error occurred in getting sidebar';
  319. logger.error('Error', err);
  320. return res.apiv3Err(new ErrorV3(msg, 'get-sidebar-failed'));
  321. }
  322. });
  323. router.put('/sidebar', loginRequiredStrictly, adminRequired, validator.sidebar, apiV3FormValidator, addActivity, async(req, res) => {
  324. const requestParams = {
  325. 'customize:isSidebarDrawerMode': req.body.isSidebarDrawerMode,
  326. 'customize:isSidebarClosedAtDockMode': req.body.isSidebarClosedAtDockMode,
  327. };
  328. try {
  329. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  330. const customizedParams = {
  331. isSidebarDrawerMode: await crowi.configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
  332. isSidebarClosedAtDockMode: await crowi.configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
  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. /**
  402. * @swagger
  403. *
  404. * /customize-setting/highlight:
  405. * put:
  406. * tags: [CustomizeSetting]
  407. * operationId: updateHighlightCustomizeSetting
  408. * summary: /customize-setting/highlight
  409. * description: Update highlight
  410. * requestBody:
  411. * required: true
  412. * content:
  413. * application/json:
  414. * schema:
  415. * $ref: '#/components/schemas/CustomizeHighlight'
  416. * responses:
  417. * 200:
  418. * description: Succeeded to update highlight
  419. * content:
  420. * application/json:
  421. * schema:
  422. * $ref: '#/components/schemas/CustomizeHighlight'
  423. */
  424. router.put('/highlight', loginRequiredStrictly, adminRequired, addActivity, validator.highlight, apiV3FormValidator, async(req, res) => {
  425. const requestParams = {
  426. 'customize:highlightJsStyle': req.body.highlightJsStyle,
  427. 'customize:highlightJsStyleBorder': req.body.highlightJsStyleBorder,
  428. };
  429. try {
  430. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  431. const customizedParams = {
  432. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  433. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  434. };
  435. const parameters = { action: SupportedAction.ACTION_ADMIN_CODE_HIGHLIGHT_UPDATE };
  436. activityEvent.emit('update', res.locals.activity._id, parameters);
  437. return res.apiv3({ customizedParams });
  438. }
  439. catch (err) {
  440. const msg = 'Error occurred in updating highlight';
  441. logger.error('Error', err);
  442. return res.apiv3Err(new ErrorV3(msg, 'update-highlight-failed'));
  443. }
  444. });
  445. /**
  446. * @swagger
  447. *
  448. * /customize-setting/customizeTitle:
  449. * put:
  450. * tags: [CustomizeSetting]
  451. * operationId: updateCustomizeTitleCustomizeSetting
  452. * summary: /customize-setting/customizeTitle
  453. * description: Update customizeTitle
  454. * requestBody:
  455. * required: true
  456. * content:
  457. * application/json:
  458. * schema:
  459. * $ref: '#/components/schemas/CustomizeTitle'
  460. * responses:
  461. * 200:
  462. * description: Succeeded to update customizeTitle
  463. * content:
  464. * application/json:
  465. * schema:
  466. * $ref: '#/components/schemas/CustomizeTitle'
  467. */
  468. router.put('/customize-title', loginRequiredStrictly, adminRequired, addActivity, validator.customizeTitle, apiV3FormValidator, async(req, res) => {
  469. const requestParams = {
  470. 'customize:title': req.body.customizeTitle,
  471. };
  472. try {
  473. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  474. crowi.customizeService.publishUpdatedMessage();
  475. const customizedParams = {
  476. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  477. };
  478. customizeService.initCustomTitle();
  479. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_TITLE_UPDATE };
  480. activityEvent.emit('update', res.locals.activity._id, parameters);
  481. return res.apiv3({ customizedParams });
  482. }
  483. catch (err) {
  484. const msg = 'Error occurred in updating customizeTitle';
  485. logger.error('Error', err);
  486. return res.apiv3Err(new ErrorV3(msg, 'update-customizeTitle-failed'));
  487. }
  488. });
  489. /**
  490. * @swagger
  491. *
  492. * /customize-setting/customizeHeader:
  493. * put:
  494. * tags: [CustomizeSetting]
  495. * operationId: updateCustomizeHeaderCustomizeSetting
  496. * summary: /customize-setting/customizeHeader
  497. * description: Update customizeHeader
  498. * requestBody:
  499. * required: true
  500. * content:
  501. * application/json:
  502. * schema:
  503. * $ref: '#/components/schemas/CustomizeHeader'
  504. * responses:
  505. * 200:
  506. * description: Succeeded to update customize header
  507. * content:
  508. * application/json:
  509. * schema:
  510. * $ref: '#/components/schemas/CustomizeHeader'
  511. */
  512. router.put('/customize-header', loginRequiredStrictly, adminRequired, addActivity, validator.customizeHeader, apiV3FormValidator, async(req, res) => {
  513. const requestParams = {
  514. 'customize:header': req.body.customizeHeader,
  515. };
  516. try {
  517. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  518. const customizedParams = {
  519. customizeHeader: await crowi.configManager.getConfig('crowi', 'customize:header'),
  520. };
  521. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_HTML_HEADER_UPDATE };
  522. activityEvent.emit('update', res.locals.activity._id, parameters);
  523. return res.apiv3({ customizedParams });
  524. }
  525. catch (err) {
  526. const msg = 'Error occurred in updating customizeHeader';
  527. logger.error('Error', err);
  528. return res.apiv3Err(new ErrorV3(msg, 'update-customizeHeader-failed'));
  529. }
  530. });
  531. /**
  532. * @swagger
  533. *
  534. * /customize-setting/customizeCss:
  535. * put:
  536. * tags: [CustomizeSetting]
  537. * operationId: updateCustomizeCssCustomizeSetting
  538. * summary: /customize-setting/customizeCss
  539. * description: Update customizeCss
  540. * requestBody:
  541. * required: true
  542. * content:
  543. * application/json:
  544. * schema:
  545. * $ref: '#/components/schemas/CustomizeCss'
  546. * responses:
  547. * 200:
  548. * description: Succeeded to update customize css
  549. * content:
  550. * application/json:
  551. * schema:
  552. * $ref: '#/components/schemas/CustomizeCss'
  553. */
  554. router.put('/customize-css', loginRequiredStrictly, adminRequired, addActivity, validator.customizeCss, apiV3FormValidator, async(req, res) => {
  555. const requestParams = {
  556. 'customize:css': req.body.customizeCss,
  557. };
  558. try {
  559. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  560. crowi.customizeService.publishUpdatedMessage();
  561. const customizedParams = {
  562. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  563. };
  564. customizeService.initCustomCss();
  565. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_CSS_UPDATE };
  566. activityEvent.emit('update', res.locals.activity._id, parameters);
  567. return res.apiv3({ customizedParams });
  568. }
  569. catch (err) {
  570. const msg = 'Error occurred in updating customizeCss';
  571. logger.error('Error', err);
  572. return res.apiv3Err(new ErrorV3(msg, 'update-customizeCss-failed'));
  573. }
  574. });
  575. /**
  576. * @swagger
  577. *
  578. * /customize-setting/customizeScript:
  579. * put:
  580. * tags: [CustomizeSetting]
  581. * operationId: updateCustomizeScriptCustomizeSetting
  582. * summary: /customize-setting/customizeScript
  583. * description: Update customizeScript
  584. * requestBody:
  585. * required: true
  586. * content:
  587. * application/json:
  588. * schema:
  589. * $ref: '#/components/schemas/CustomizeScript'
  590. * responses:
  591. * 200:
  592. * description: Succeeded to update customize script
  593. * content:
  594. * application/json:
  595. * schema:
  596. * $ref: '#/components/schemas/CustomizeScript'
  597. */
  598. router.put('/customize-script', loginRequiredStrictly, adminRequired, addActivity, validator.customizeScript, apiV3FormValidator, async(req, res) => {
  599. const requestParams = {
  600. 'customize:script': req.body.customizeScript,
  601. };
  602. try {
  603. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  604. const customizedParams = {
  605. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  606. };
  607. const parameters = { action: SupportedAction.ACTION_ADMIN_CUSTOM_SCRIPT_UPDATE };
  608. activityEvent.emit('update', res.locals.activity._id, parameters);
  609. return res.apiv3({ customizedParams });
  610. }
  611. catch (err) {
  612. const msg = 'Error occurred in updating customizeScript';
  613. logger.error('Error', err);
  614. return res.apiv3Err(new ErrorV3(msg, 'update-customizeScript-failed'));
  615. }
  616. });
  617. router.get('/customize-logo', loginRequiredStrictly, adminRequired, async(req, res) => {
  618. const isDefaultLogo = await crowi.configManager.getConfig('crowi', 'customize:isDefaultLogo');
  619. const customizedLogoSrc = await crowi.configManager.getConfig('crowi', 'customize:customizedLogoSrc');
  620. return res.apiv3({ isDefaultLogo, customizedLogoSrc });
  621. });
  622. router.put('/customize-logo', loginRequiredStrictly, adminRequired, validator.logo, apiV3FormValidator, async(req, res) => {
  623. const {
  624. isDefaultLogo,
  625. } = req.body;
  626. const requestParams = {
  627. 'customize:isDefaultLogo': isDefaultLogo,
  628. };
  629. try {
  630. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  631. const customizedParams = {
  632. isDefaultLogo: await crowi.configManager.getConfig('crowi', 'customize:isDefaultLogo'),
  633. };
  634. return res.apiv3({ customizedParams });
  635. }
  636. catch (err) {
  637. const msg = 'Error occurred in updating customizeLogo';
  638. logger.error('Error', err);
  639. return res.apiv3Err(new ErrorV3(msg, 'update-customizeLogo-failed'));
  640. }
  641. });
  642. router.post('/upload-brand-logo', uploads.single('file'), loginRequiredStrictly,
  643. adminRequired, validator.logo, apiV3FormValidator, async(req, res) => {
  644. if (req.file == null) {
  645. return res.apiv3Err(new ErrorV3('File error.', 'upload-brand-logo-failed'));
  646. }
  647. if (req.user == null) {
  648. return res.apiv3Err(new ErrorV3('param "user" must be set.', 'upload-brand-logo-failed'));
  649. }
  650. const file = req.file;
  651. // check type
  652. const acceptableFileType = /image\/.+/;
  653. if (!file.mimetype.match(acceptableFileType)) {
  654. const msg = 'File type error. Only image files is allowed to set as user picture.';
  655. return res.apiv3Err(new ErrorV3(msg, 'upload-brand-logo-failed'));
  656. }
  657. // Check if previous attachment exists and remove it
  658. const attachments = await Attachment.find({ attachmentType: AttachmentType.BRAND_LOGO });
  659. if (attachments != null) {
  660. await attachmentService.removeAllAttachments(attachments);
  661. }
  662. let attachment;
  663. try {
  664. attachment = await attachmentService.createAttachment(file, req.user, null, AttachmentType.BRAND_LOGO);
  665. const attachmentConfigParams = {
  666. 'customize:customizedLogoSrc': attachment.filePathProxied,
  667. };
  668. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', attachmentConfigParams);
  669. }
  670. catch (err) {
  671. logger.error(err);
  672. return res.apiv3Err(new ErrorV3(err.message, 'upload-brand-logo-failed'));
  673. }
  674. attachment.toObject({ virtuals: true });
  675. return res.apiv3({ attachment });
  676. });
  677. router.delete('/delete-brand-logo', loginRequiredStrictly, adminRequired, async(req, res) => {
  678. const attachments = await Attachment.find({ attachmentType: AttachmentType.BRAND_LOGO });
  679. if (attachments == null) {
  680. return res.apiv3Err(new ErrorV3('attachment not found', 'delete-brand-logo-failed'));
  681. }
  682. try {
  683. await attachmentService.removeAllAttachments(attachments);
  684. // update attachmentId immediately
  685. const attachmentConfigParams = { 'customize:customizedLogoSrc': null };
  686. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', attachmentConfigParams);
  687. }
  688. catch (err) {
  689. logger.error(err);
  690. return res.status(500).apiv3Err(new ErrorV3('Error while deleting logo', 'delete-brand-logo-failed'));
  691. }
  692. return res.apiv3({});
  693. });
  694. return router;
  695. };