customize-setting.js 28 KB

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