Admin/customizeに設置したドロップダウンを更新時にDBに保存されるようにする

CustomizeFunctionSetting.jsx

CustomizeFunctionSetting.jsx
async onClickSubmit() { const { t, adminCustomizeContainer } = this.props; try { await adminCustomizeContainer.updateCustomizeFunction(); toastSuccess(t('toaster.update_successed', { target: t('admin:customize_setting.function') })); } catch (err) { toastError(err); } }
  • 更新ボタンを押すとupdateCustomizeFunctionが呼ばれる
  • いじったファイルは3つ
    1. CustomizeContainer (src/client/js/services)
      1. retrieveCustomizeData()
      2. updateCustomizeFunction()
    2. config.js (src/server/models)
      1. localConfig
    3. customize-setting.js (src/server/routes/apiv3)
      1. validator > function
      2. router.get(/) > customizeParams
      3. router.put('/function') > requestParams & tryの中

1. CustomizeContainer

Update Function

AdminCustomizeContainer.js
/** * Update function * @memberOf AdminCustomizeContainer */ async updateCustomizeFunction() { try { const response = await this.appContainer.apiv3.put('/customize-setting/function', { isEnabledTimeline: this.state.isEnabledTimeline, isSavedStatesOfTabChanges: this.state.isSavedStatesOfTabChanges, isEnabledAttachTitleHeader: this.state.isEnabledAttachTitleHeader, pageLimitationS: this.state.pageLimitationS, pageLimitationM: this.state.pageLimitationM, pageLimitationL: this.state.pageLimitationL, pageLimitationXL: this.state.pageLimitationXL, // TODO implement for pageListLimitForModal isEnabledStaleNotification: this.state.isEnabledStaleNotification, isAllReplyShown: this.state.isAllReplyShown, }); const { customizedParams } = response.data; this.setState({ isEnabledTimeline: customizedParams.isEnabledTimeline, isSavedStatesOfTabChanges: customizedParams.isSavedStatesOfTabChanges, isEnabledAttachTitleHeader: customizedParams.isEnabledAttachTitleHeader, pageLimitationS: customizedParams.pageLimitationS, pageLimitationM: customizedParams.pageLimitationM, pageLimitationL: customizedParams.pageLimitationL, pageLimitationXL: customizedParams.pageLimitationXL, // TODO implement for pageListLimitForModal isEnabledStaleNotification: customizedParams.isEnabledStaleNotification, isAllReplyShown: customizedParams.isAllReplyShown, }); } catch (err) { logger.error(err); throw new Error('Failed to update data'); } }

Retrieve (取得する)

AdminCustomizeContainer.js
/** * retrieve customize data */ async retrieveCustomizeData() { try { const response = await this.appContainer.apiv3.get('/customize-setting/'); const { customizeParams } = response.data; this.setState({ currentTheme: customizeParams.themeType, isEnabledTimeline: customizeParams.isEnabledTimeline, isSavedStatesOfTabChanges: customizeParams.isSavedStatesOfTabChanges, isEnabledAttachTitleHeader: customizeParams.isEnabledAttachTitleHeader, pageLimitationS: customizeParams.pageLimitationS, pageLimitationM: customizeParams.pageLimitationM, pageLimitationL: customizeParams.pageLimitationL, pageLimitationXL: customizeParams.pageLimitationXL, // TODO implement for pageListLimitForModal isEnabledStaleNotification: customizeParams.isEnabledStaleNotification, isAllReplyShown: customizeParams.isAllReplyShown, currentHighlightJsStyleId: customizeParams.styleName, isHighlightJsStyleBorderEnabled: customizeParams.styleBorder, currentCustomizeTitle: customizeParams.customizeTitle, currentCustomizeHeader: customizeParams.customizeHeader, currentCustomizeCss: customizeParams.customizeCss, currentCustomizeScript: customizeParams.customizeScript, }); // search style name from object for display this.setState({ currentHighlightJsStyleName: this.state.highlightJsCssSelectorOptions[customizeParams.styleName].name }); } catch (err) { this.setState({ retrieveError: err }); logger.error(err); throw new Error('Failed to fetch data'); } }

2. config.js

  • src/server/models/config.js

getLocalconfig

config.js
configSchema.statics.getLocalconfig = function() { const env = process.env; const localConfig = { crowi: { title: crowi.appService.getAppTitle(), url: crowi.appService.getSiteUrl(), confidential: crowi.appService.getAppConfidential(), }, upload: { image: crowi.fileUploadService.getIsUploadable(), file: crowi.fileUploadService.getFileUploadEnabled(), }, registrationWhiteList: crowi.configManager.getConfig('crowi', 'security:registrationWhiteList'), layoutType: crowi.configManager.getConfig('crowi', 'customize:layout'), themeType: crowi.configManager.getConfig('crowi', 'customize:theme'), isEnabledLinebreaks: crowi.configManager.getConfig('markdown', 'markdown:isEnabledLinebreaks'), isEnabledLinebreaksInComments: crowi.configManager.getConfig('markdown', 'markdown:isEnabledLinebreaksInComments'), pageBreakSeparator: crowi.configManager.getConfig('markdown', 'markdown:presentation:pageBreakSeparator'), pageBreakCustomSeparator: crowi.configManager.getConfig('markdown', 'markdown:presentation:pageBreakCustomSeparator'), isEnabledXssPrevention: crowi.configManager.getConfig('markdown', 'markdown:xss:isEnabledPrevention'), isEnabledTimeline: crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'), isAllReplyShown: crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'), xssOption: crowi.configManager.getConfig('markdown', 'markdown:xss:option'), tagWhiteList: crowi.xssService.getTagWhiteList(), attrWhiteList: crowi.xssService.getAttrWhiteList(), highlightJsStyle: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'), highlightJsStyleBorder: crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'), customizeTitle: crowi.configManager.getConfig('crowi', 'customize:title'), customizeHeader: crowi.configManager.getConfig('crowi', 'customize:header'), customizeCss: crowi.configManager.getConfig('crowi', 'customize:css'), isSavedStatesOfTabChanges: crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'), isEnabledAttachTitleHeader: crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'), customizeScript: crowi.configManager.getConfig('crowi', 'customize:script'), hasSlackConfig: crowi.slackNotificationService.hasSlackConfig(), env: { PLANTUML_URI: env.PLANTUML_URI || null, BLOCKDIAG_URI: env.BLOCKDIAG_URI || null, DRAWIO_URI: env.DRAWIO_URI || null, HACKMD_URI: env.HACKMD_URI || null, MATHJAX: env.MATHJAX || null, NO_CDN: env.NO_CDN || null, }, pageLimitationS: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'), pageLimitationM: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'), pageLimitationL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'), pageLimitationXL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'), // TODO implement for pageListLimitForModal isEnabledStaleNotification: crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'), isAclEnabled: crowi.aclService.isAclEnabled(), isSearchServiceConfigured: crowi.searchService.isConfigured, isSearchServiceReachable: crowi.searchService.isReachable, globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'), }; return localConfig; }; const Config = mongoose.model('Config', configSchema); return Config; };

3. customize-setting.js

function

customize-setting.js
const validator = { themeAssetPath: [ query('themeName').isString().isIn([ 'default', 'nature', 'mono-blue', 'wood', 'island', 'christmas', 'antarctic', 'future', 'halloween', 'spring', ]), ], theme: [ body('themeType').isString().isIn([ 'default', 'nature', 'mono-blue', 'wood', 'island', 'christmas', 'antarctic', 'future', 'halloween', 'spring', 'kibela', ]), ], function: [ body('isEnabledTimeline').isBoolean(), body('isSavedStatesOfTabChanges').isBoolean(), body('isEnabledAttachTitleHeader').isBoolean(), // TODO implement for pageListLimitForModal body('pageLimitationS').isInt().isInt({ min: 1, max: 1000 }), body('pageLimitationM').isInt().isInt({ min: 1, max: 1000 }), body('pageLimitationL').isInt().isInt({ min: 1, max: 1000 }), body('pageLimitationXL').isInt().isInt({ min: 1, max: 1000 }), body('isEnabledStaleNotification').isBoolean(), body('isAllReplyShown').isBoolean(), ],

router.get

customize-setting.js
/** * @swagger * * /customize-setting: * get: * tags: [CustomizeSetting] * operationId: getCustomizeSetting * summary: /customize-setting * description: Get customize parameters * responses: * 200: * description: params of customize * content: * application/json: * schema: * properties: * customizeParams: * type: object * description: customize params */ router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => { const customizeParams = { layoutType: await crowi.configManager.getConfig('crowi', 'customize:layout'), themeType: await crowi.configManager.getConfig('crowi', 'customize:theme'), isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'), isSavedStatesOfTabChanges: await crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'), isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'), pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'), pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'), pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'), pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'), // TODO implement for pageListLimitForModal isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'), isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'), styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'), styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'), customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'), customizeHeader: await crowi.configManager.getConfig('crowi', 'customize:header'), customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'), customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'), }; return res.apiv3({ customizeParams }); });

router.put

customize-setting.js
/** * @swagger * * /customize-setting/function: * put: * tags: [CustomizeSetting] * operationId: updateFunctionCustomizeSetting * summary: /customize-setting/function * description: Update function * requestBody: * required: true * content: * application/json: * schema: * $ref: '#/components/schemas/CustomizeFunction' * responses: * 200: * description: Succeeded to update function * content: * application/json: * schema: * $ref: '#/components/schemas/CustomizeFunction' */ router.put('/function', loginRequiredStrictly, adminRequired, csrf, validator.function, apiV3FormValidator, async(req, res) => { const requestParams = { 'customize:isEnabledTimeline': req.body.isEnabledTimeline, 'customize:isSavedStatesOfTabChanges': req.body.isSavedStatesOfTabChanges, 'customize:isEnabledAttachTitleHeader': req.body.isEnabledAttachTitleHeader, // TODO implement for pageListLimitForModal 'customize:showPageLimitationS': req.body.pageLimitationS, 'customize:showPageLimitationM': req.body.pageLimitationM, 'customize:showPageLimitationL': req.body.pageLimitationL, 'customize:showPageLimitationXL': req.body.pageLimitationXL, 'customize:isEnabledStaleNotification': req.body.isEnabledStaleNotification, 'customize:isAllReplyShown': req.body.isAllReplyShown, }; try { await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams); const customizedParams = { isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'), isSavedStatesOfTabChanges: await crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'), isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'), // TODO implement for pageListLimitForModal pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'), pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'), pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'), pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'), isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'), isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'), }; return res.apiv3({ customizedParams }); } catch (err) { const msg = 'Error occurred in updating function'; logger.error('Error', err); return res.apiv3Err(new ErrorV3(msg, 'update-function-failed')); } });

値がnullの場合

customize-setting.js
pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS') || 20, pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM') || 10, pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL') || 50, pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL') || 20,

UI側で保存

containerでの初期値をnullに設定し、CustomizeFunctionSettingでnullが渡った来た場合の値を設定。

CustomizeFunctionSetting.jsx
<PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_s')} desc={t('admin:customize_setting.function_options.list_num_desc_s')} toggleLabel={adminCustomizeContainer.state.pageLimitationS || 20} dropdownItemSize={[10, 20, 50, 100]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationS} /> <PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_m')} desc={t('admin:customize_setting.function_options.list_num_desc_m')} toggleLabel={adminCustomizeContainer.state.pageLimitationM || 10} dropdownItemSize={[5, 10, 20, 50, 100]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationM} /> <PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_l')} desc={t('admin:customize_setting.function_options.list_num_desc_l')} toggleLabel={adminCustomizeContainer.state.pageLimitationL || 50} dropdownItemSize={[20, 50, 100, 200]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationL} /> <PagingSizeUncontrolledDropdown label={t('admin:customize_setting.function_options.list_num_xl')} desc={t('admin:customize_setting.function_options.list_num_desc_xl')} toggleLabel={adminCustomizeContainer.state.pageLimitationXL || 20} dropdownItemSize={[5, 10, 20, 50, 100]} onChangeDropdownItem={adminCustomizeContainer.switchPageListLimitationXL} />