Просмотр исходного кода

Merge branch 'feat/auditlog' into feat/Add-file-upload-config-activity

hirokei 3 лет назад
Родитель
Сommit
f742b4a430
26 измененных файлов с 209 добавлено и 65 удалено
  1. 1 0
      packages/app/.env.development
  2. 5 1
      packages/app/public/static/locales/en_US/admin/admin.json
  3. 5 1
      packages/app/public/static/locales/ja_JP/admin/admin.json
  4. 5 1
      packages/app/public/static/locales/zh_CN/admin/admin.json
  5. 0 9
      packages/app/src/client/app.jsx
  6. 3 0
      packages/app/src/client/base.jsx
  7. 4 1
      packages/app/src/client/services/ContextExtractor.tsx
  8. 34 0
      packages/app/src/client/services/ShowPageAccessoriesModal.tsx
  9. 26 0
      packages/app/src/components/Admin/AuditLog/AuditLogDisableMode.tsx
  10. 25 3
      packages/app/src/components/Admin/AuditLog/AuditLogSettings.tsx
  11. 10 2
      packages/app/src/components/Admin/AuditLogManagement.tsx
  12. 7 7
      packages/app/src/components/PageAccessoriesModal.tsx
  13. 20 16
      packages/app/src/interfaces/activity.ts
  14. 6 6
      packages/app/src/server/models/activity.ts
  15. 2 0
      packages/app/src/server/models/config.ts
  16. 3 3
      packages/app/src/server/models/in-app-notification.ts
  17. 2 2
      packages/app/src/server/models/subscription.ts
  18. 7 0
      packages/app/src/server/routes/apiv3/activity.ts
  19. 3 1
      packages/app/src/server/routes/apiv3/app-settings.js
  20. 9 4
      packages/app/src/server/service/activity.ts
  21. 6 0
      packages/app/src/server/service/config-loader.ts
  22. 3 3
      packages/app/src/server/service/in-app-notification.ts
  23. 4 1
      packages/app/src/server/service/search-delegator/elasticsearch.ts
  24. 2 0
      packages/app/src/server/views/layout/layout.html
  25. 7 4
      packages/app/src/stores/activity.ts
  26. 10 0
      packages/app/src/stores/context.tsx

+ 1 - 0
packages/app/.env.development

@@ -29,6 +29,7 @@ OGP_URI="http://ogp:8088"
 # SLACKBOT_WITHOUT_PROXY_BOT_TOKEN=''
 # SLACKBOT_WITHOUT_PROXY_BOT_TOKEN=''
 # GROWI_CLOUD_URI='http://growi.cloud'
 # GROWI_CLOUD_URI='http://growi.cloud'
 # GROWI_APP_ID_FOR_GROWI_CLOUD=012345
 # GROWI_APP_ID_FOR_GROWI_CLOUD=012345
+# AUDIT_LOG_ENABLED=false
 # ACTIVITY_EXPIRATION_SECONDS=2592000
 # ACTIVITY_EXPIRATION_SECONDS=2592000
 # AUDIT_LOG_ACTION_GROUP_SIZE=SMALL
 # AUDIT_LOG_ACTION_GROUP_SIZE=SMALL
 # AUDIT_LOG_ADDITIONAL_ACTIONS=
 # AUDIT_LOG_ADDITIONAL_ACTIONS=

+ 5 - 1
packages/app/public/static/locales/en_US/admin/admin.json

@@ -532,6 +532,10 @@
     "return": "Return",
     "return": "Return",
     "activity_expiration_date": "Audit Log expiration date",
     "activity_expiration_date": "Audit Log expiration date",
     "activity_expiration_date_explain": "Created Audit Log are automatically deleted after the number of seconds set in the environment variable from the creation time",
     "activity_expiration_date_explain": "Created Audit Log are automatically deleted after the number of seconds set in the environment variable from the creation time",
-    "fixed_by_env_var": "This is fixed by the env var <code>{{key}}={{value}}</code>."
+    "fixed_by_env_var": "This is fixed by the env var <code>{{key}}={{value}}</code>.",
+    "available_action_list": "Search / View All Available Actions",
+    "available_action_list_explain": "List of actions that can be search / view in the Audit Log",
+    "action_list": "Action List",
+    "disable_mode_explain": "Audit log is currently disabled. To enable it, set the environment variable <code>AUDIT_LOG_ENABLED</code> to true."
   }
   }
 }
 }

+ 5 - 1
packages/app/public/static/locales/ja_JP/admin/admin.json

@@ -531,6 +531,10 @@
     "return": "戻る",
     "return": "戻る",
     "activity_expiration_date": "監査ログの有効期限",
     "activity_expiration_date": "監査ログの有効期限",
     "activity_expiration_date_explain": "作成された監査ログは、作成時間から環境変数に設定した秒数後に自動的に削除されます",
     "activity_expiration_date_explain": "作成された監査ログは、作成時間から環境変数に設定した秒数後に自動的に削除されます",
-    "fixed_by_env_var": "環境変数により固定されています <code>{{key}}={{value}}</code>."
+    "fixed_by_env_var": "環境変数により固定されています <code>{{key}}={{value}}</code>.",
+    "available_action_list": "検索 / 表示 可能なアクション一覧",
+    "available_action_list_explain": "監査ログで 検索 / 表示 可能なアクション一覧です",
+    "action_list": "アクション一覧",
+    "disable_mode_explain": "現在、監査ログは無効になっています。有効にする場合は環境変数 <code>AUDIT_LOG_ENABLED</code> を true に設定してください。"
   }
   }
 }
 }

+ 5 - 1
packages/app/public/static/locales/zh_CN/admin/admin.json

@@ -541,6 +541,10 @@
     "return": "返回",
     "return": "返回",
     "activity_expiration_date": "审计日志的到期日",
     "activity_expiration_date": "审计日志的到期日",
     "activity_expiration_date_explain": "创建的审计日志会在环境变量中设置的从创建时间算起的秒数后自动删除",
     "activity_expiration_date_explain": "创建的审计日志会在环境变量中设置的从创建时间算起的秒数后自动删除",
-    "fixed_by_env_var": "这是由env var 修复的 <code>{{key}}={{value}}</code>."
+    "fixed_by_env_var": "这是由env var 修复的 <code>{{key}}={{value}}</code>.",
+    "available_action_list": "搜索/查看 所有可用的行动",
+    "available_action_list_explain": "可以在审计日志中 搜索/查看 的行动列表",
+    "action_list": "行动清单",
+    "disable_mode_explain": "审计日志当前已禁用。 要启用它,请将环境变量 <code>AUDIT_LOG_ENABLED</code> 设置为 true。"
   }
   }
 }
 }

+ 0 - 9
packages/app/src/client/app.jsx

@@ -49,8 +49,6 @@ import TagPage from '../components/TagPage';
 import TrashPageList from '../components/TrashPageList';
 import TrashPageList from '../components/TrashPageList';
 
 
 import { appContainer, componentMappings } from './base';
 import { appContainer, componentMappings } from './base';
-import { toastError } from './util/apiNotification';
-
 
 
 const logger = loggerFactory('growi:cli:app');
 const logger = loggerFactory('growi:cli:app');
 
 
@@ -128,18 +126,11 @@ if (pageContainer.state.pageId != null) {
 
 
     'recent-created-icon': <RecentlyCreatedIcon />,
     'recent-created-icon': <RecentlyCreatedIcon />,
   });
   });
-
   if (!pageContainer.state.isEmpty) {
   if (!pageContainer.state.isEmpty) {
     Object.assign(componentMappings, {
     Object.assign(componentMappings, {
       'fix-page-grant-alert': <FixPageGrantAlert />,
       'fix-page-grant-alert': <FixPageGrantAlert />,
     });
     });
   }
   }
-
-  // show the Page accessory modal when query of "compare" is requested
-  if (revisionComparerContainer.getRevisionIDsToCompareAsParam().length > 0) {
-    toastError('Sorry, opening PageAccessoriesModal is not implemented yet in v5.');
-  //   pageAccessoriesContainer.openPageAccessoriesModal('pageHistory');
-  }
 }
 }
 if (pageContainer.state.creator != null) {
 if (pageContainer.state.creator != null) {
   Object.assign(componentMappings, {
   Object.assign(componentMappings, {

+ 3 - 0
packages/app/src/client/base.jsx

@@ -23,6 +23,8 @@ import PageDuplicateModal from '../components/PageDuplicateModal';
 import PagePresentationModal from '../components/PagePresentationModal';
 import PagePresentationModal from '../components/PagePresentationModal';
 import PageRenameModal from '../components/PageRenameModal';
 import PageRenameModal from '../components/PageRenameModal';
 
 
+import ShowPageAccessoriesModal from './services/ShowPageAccessoriesModal';
+
 const logger = loggerFactory('growi:cli:app');
 const logger = loggerFactory('growi:cli:app');
 
 
 if (!window) {
 if (!window) {
@@ -69,6 +71,7 @@ const componentMappings = {
   'system-version': <SystemVersion />,
   'system-version': <SystemVersion />,
 
 
 
 
+  'show-page-accessories-modal': <ShowPageAccessoriesModal />,
 };
 };
 
 
 export { appContainer, componentMappings };
 export { appContainer, componentMappings };

+ 4 - 1
packages/app/src/client/services/ContextExtractor.tsx

@@ -18,7 +18,8 @@ import {
   useShareLinkId, useShareLinksNumber, useTemplateTagData, useCurrentUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
   useShareLinkId, useShareLinksNumber, useTemplateTagData, useCurrentUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser, useTargetAndAncestors,
   useNotFoundTargetPathOrId, useIsSearchPage, useIsForbidden, useIsIdenticalPath, useHasParent,
   useNotFoundTargetPathOrId, useIsSearchPage, useIsForbidden, useIsIdenticalPath, useHasParent,
   useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader, useIsNotFoundPermalink,
   useIsAclEnabled, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsEnabledAttachTitleHeader, useIsNotFoundPermalink,
-  useDefaultIndentSize, useIsIndentSizeForced, useCsrfToken, useIsEmptyPage, useEmptyPageId, useGrowiVersion, useActivityExpirationSeconds,
+  useDefaultIndentSize, useIsIndentSizeForced, useCsrfToken, useIsEmptyPage, useEmptyPageId, useGrowiVersion, useAuditLogEnabled,
+  useActivityExpirationSeconds, useAuditLogAvailableActions,
 } from '../../stores/context';
 } from '../../stores/context';
 
 
 const { isTrashPage: _isTrashPage } = pagePathUtils;
 const { isTrashPage: _isTrashPage } = pagePathUtils;
@@ -123,7 +124,9 @@ const ContextExtractorOnce: FC = () => {
   useIsEnabledAttachTitleHeader(configByContextHydrate.isEnabledAttachTitleHeader);
   useIsEnabledAttachTitleHeader(configByContextHydrate.isEnabledAttachTitleHeader);
   useIsIndentSizeForced(configByContextHydrate.isIndentSizeForced);
   useIsIndentSizeForced(configByContextHydrate.isIndentSizeForced);
   useDefaultIndentSize(configByContextHydrate.adminPreferredIndentSize);
   useDefaultIndentSize(configByContextHydrate.adminPreferredIndentSize);
+  useAuditLogEnabled(configByContextHydrate.auditLogEnabled);
   useActivityExpirationSeconds(configByContextHydrate.activityExpirationSeconds);
   useActivityExpirationSeconds(configByContextHydrate.activityExpirationSeconds);
+  useAuditLogAvailableActions(configByContextHydrate.auditLogAvailableActions);
   useGrowiVersion(configByContextHydrate.crowi.version);
   useGrowiVersion(configByContextHydrate.crowi.version);
 
 
   // Page
   // Page

+ 34 - 0
packages/app/src/client/services/ShowPageAccessoriesModal.tsx

@@ -0,0 +1,34 @@
+import React, { useEffect, useState } from 'react';
+
+import { usePageAccessoriesModal, PageAccessoriesModalContents } from '~/stores/modal';
+
+function getURLQueryParamValue(key: string) {
+// window.location.href is page URL;
+  const queryStr: URLSearchParams = new URL(window.location.href).searchParams;
+  return queryStr.get(key);
+}
+
+const queryCompareFormat = new RegExp(/([a-z0-9]){24}...([a-z0-9]){24}/);
+
+const ShowPageAccessoriesModal = (): JSX.Element => {
+  const { data: status, open: openPageAccessories } = usePageAccessoriesModal();
+  const [isArleadyMounted, setIsArleadyMounted] = useState(false);
+  useEffect(() => {
+    const pageIdParams = getURLQueryParamValue('compare');
+    if (status == null || status.isOpened === true) {
+      return;
+    }
+    if (isArleadyMounted === true) {
+      return;
+    }
+    if (pageIdParams != null) {
+      if (queryCompareFormat.test(pageIdParams)) {
+        openPageAccessories(PageAccessoriesModalContents.PageHistory);
+      }
+    }
+    setIsArleadyMounted(true);
+  }, [openPageAccessories, status, isArleadyMounted]);
+  return <></>;
+};
+
+export default ShowPageAccessoriesModal;

+ 26 - 0
packages/app/src/components/Admin/AuditLog/AuditLogDisableMode.tsx

@@ -0,0 +1,26 @@
+import React, { FC } from 'react';
+
+import { useTranslation } from 'react-i18next';
+
+export const AuditLogDisableMode: FC = () => {
+  const { t } = useTranslation();
+
+  return (
+    <div id="content-main" className="content-main container-lg">
+      <div className="container">
+        <div className="row justify-content-md-center">
+          <div className="col-md-6 mt-5">
+            <div className="text-center">
+              <h1><i className="icon-exclamation large"></i></h1>
+              <h1 className="text-center">{t('AuditLog')}</h1>
+              <h3
+                // eslint-disable-next-line react/no-danger
+                dangerouslySetInnerHTML={{ __html: t('admin:audit_log_management.disable_mode_explain') }}
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};

+ 25 - 3
packages/app/src/components/Admin/AuditLog/AuditLogSettings.tsx

@@ -1,18 +1,24 @@
-import React, { FC } from 'react';
+import React, { FC, useState } from 'react';
 
 
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
+import { Collapse } from 'reactstrap';
 
 
-import { useActivityExpirationSeconds } from '~/stores/context';
+import { useActivityExpirationSeconds, useAuditLogAvailableActions } from '~/stores/context';
 
 
 export const AuditLogSettings: FC = () => {
 export const AuditLogSettings: FC = () => {
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
+  const [isExpandActionList, setIsExpandActionList] = useState(false);
+
   const { data: activityExpirationSecondsData } = useActivityExpirationSeconds();
   const { data: activityExpirationSecondsData } = useActivityExpirationSeconds();
   const activityExpirationSeconds = activityExpirationSecondsData != null ? activityExpirationSecondsData : 2592000;
   const activityExpirationSeconds = activityExpirationSecondsData != null ? activityExpirationSecondsData : 2592000;
 
 
+  const { data: availableActionsData } = useAuditLogAvailableActions();
+  const availableActions = availableActionsData != null ? availableActionsData : [];
+
   return (
   return (
     <>
     <>
-      <h4>{t('admin:audit_log_management.activity_expiration_date')}</h4>
+      <h4 className="mt-4">{t('admin:audit_log_management.activity_expiration_date')}</h4>
       <p className="form-text text-muted">
       <p className="form-text text-muted">
         {t('admin:audit_log_management.activity_expiration_date_explain')}
         {t('admin:audit_log_management.activity_expiration_date_explain')}
       </p>
       </p>
@@ -27,6 +33,22 @@ export const AuditLogSettings: FC = () => {
           }}
           }}
         />
         />
       </p>
       </p>
+
+      <h4 className="mt-4">{t('admin:audit_log_management.available_action_list')}</h4>
+      <p className="form-text text-muted">{t('admin:audit_log_management.available_action_list_explain')}</p>
+      <p className="mt-1">
+        <button type="button" className="btn btn-link p-0" aria-expanded="false" onClick={() => setIsExpandActionList(!isExpandActionList)}>
+          <i className={`fa fa-fw fa-arrow-right ${isExpandActionList ? 'fa-rotate-90' : ''}`}></i>
+          { t('admin:audit_log_management.action_list') }
+        </button>
+      </p>
+      <Collapse isOpen={isExpandActionList}>
+        <ul className="list-group">
+          { availableActions.map(action => (
+            <li key={action} className="list-group-item">{ action }</li>
+          )) }
+        </ul>
+      </Collapse>
     </>
     </>
   );
   );
 };
 };

+ 10 - 2
packages/app/src/components/Admin/AuditLogManagement.tsx

@@ -4,13 +4,15 @@ import { format } from 'date-fns';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 
 
 import {
 import {
-  SupportedActionType, AllSupportedAction, PageActions, CommentActions,
+  SupportedActionType, AllSupportedActions, PageActions, CommentActions,
 } from '~/interfaces/activity';
 } from '~/interfaces/activity';
 import { useSWRxActivity } from '~/stores/activity';
 import { useSWRxActivity } from '~/stores/activity';
+import { useAuditLogEnabled } from '~/stores/context';
 
 
 import PaginationWrapper from '../PaginationWrapper';
 import PaginationWrapper from '../PaginationWrapper';
 
 
 import { ActivityTable } from './AuditLog/ActivityTable';
 import { ActivityTable } from './AuditLog/ActivityTable';
+import { AuditLogDisableMode } from './AuditLog/AuditLogDisableMode';
 import { AuditLogSettings } from './AuditLog/AuditLogSettings';
 import { AuditLogSettings } from './AuditLog/AuditLogSettings';
 import { DateRangePicker } from './AuditLog/DateRangePicker';
 import { DateRangePicker } from './AuditLog/DateRangePicker';
 import { SearchUsernameTypeahead } from './AuditLog/SearchUsernameTypeahead';
 import { SearchUsernameTypeahead } from './AuditLog/SearchUsernameTypeahead';
@@ -39,7 +41,7 @@ export const AuditLogManagement: FC = () => {
   const [endDate, setEndDate] = useState<Date | null>(null);
   const [endDate, setEndDate] = useState<Date | null>(null);
   const [selectedUsernames, setSelectedUsernames] = useState<string[]>([]);
   const [selectedUsernames, setSelectedUsernames] = useState<string[]>([]);
   const [actionMap, setActionMap] = useState(
   const [actionMap, setActionMap] = useState(
-    new Map<SupportedActionType, boolean>(AllSupportedAction.map(action => [action, true])),
+    new Map<SupportedActionType, boolean>(AllSupportedActions.map(action => [action, true])),
   );
   );
 
 
   /*
   /*
@@ -54,6 +56,8 @@ export const AuditLogManagement: FC = () => {
   const totalActivityNum = activityData?.totalDocs != null ? activityData.totalDocs : 0;
   const totalActivityNum = activityData?.totalDocs != null ? activityData.totalDocs : 0;
   const isLoading = activityData === undefined && error == null;
   const isLoading = activityData === undefined && error == null;
 
 
+  const { data: auditLogEnabled } = useAuditLogEnabled();
+
   /*
   /*
    * Functions
    * Functions
    */
    */
@@ -92,6 +96,10 @@ export const AuditLogManagement: FC = () => {
   // eslint-disable-next-line max-len
   // eslint-disable-next-line max-len
   const activityCounter = `<b>${activityList.length === 0 ? 0 : offset + 1}</b> - <b>${(PAGING_LIMIT * activePage) - (PAGING_LIMIT - activityList.length)}</b> of <b>${totalActivityNum}<b/>`;
   const activityCounter = `<b>${activityList.length === 0 ? 0 : offset + 1}</b> - <b>${(PAGING_LIMIT * activePage) - (PAGING_LIMIT - activityList.length)}</b> of <b>${totalActivityNum}<b/>`;
 
 
+  if (!auditLogEnabled) {
+    return <AuditLogDisableMode />;
+  }
+
   return (
   return (
     <div data-testid="admin-auditlog">
     <div data-testid="admin-auditlog">
       <button type="button" className="btn btn-outline-secondary mb-4" onClick={() => setIsSettingPage(!isSettingPage)}>
       <button type="button" className="btn btn-outline-secondary mb-4" onClick={() => setIsSettingPage(!isSettingPage)}>

+ 7 - 7
packages/app/src/components/PageAccessoriesModal.tsx

@@ -1,25 +1,25 @@
 import React, { useEffect, useMemo, useState } from 'react';
 import React, { useEffect, useMemo, useState } from 'react';
 
 
+import { useTranslation } from 'react-i18next';
 import {
 import {
   Modal, ModalBody, ModalHeader,
   Modal, ModalBody, ModalHeader,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
-import { useTranslation } from 'react-i18next';
 
 
+import AppContainer from '~/client/services/AppContainer';
 import { useIsGuestUser, useIsSharedUser } from '~/stores/context';
 import { useIsGuestUser, useIsSharedUser } from '~/stores/context';
 import { usePageAccessoriesModal, PageAccessoriesModalContents } from '~/stores/modal';
 import { usePageAccessoriesModal, PageAccessoriesModalContents } from '~/stores/modal';
-import AppContainer from '~/client/services/AppContainer';
 
 
-import HistoryIcon from './Icons/HistoryIcon';
+import { CustomNavTab } from './CustomNavigation/CustomNav';
+import CustomTabContent from './CustomNavigation/CustomTabContent';
+import ExpandOrContractButton from './ExpandOrContractButton';
 import AttachmentIcon from './Icons/AttachmentIcon';
 import AttachmentIcon from './Icons/AttachmentIcon';
+import HistoryIcon from './Icons/HistoryIcon';
 import ShareLinkIcon from './Icons/ShareLinkIcon';
 import ShareLinkIcon from './Icons/ShareLinkIcon';
-import { withUnstatedContainers } from './UnstatedUtils';
 import PageAttachment from './PageAttachment';
 import PageAttachment from './PageAttachment';
 import PageHistory from './PageHistory';
 import PageHistory from './PageHistory';
 import ShareLink from './ShareLink/ShareLink';
 import ShareLink from './ShareLink/ShareLink';
-import { CustomNavTab } from './CustomNavigation/CustomNav';
-import ExpandOrContractButton from './ExpandOrContractButton';
-import CustomTabContent from './CustomNavigation/CustomTabContent';
+import { withUnstatedContainers } from './UnstatedUtils';
 
 
 
 
 type Props = {
 type Props = {

+ 20 - 16
packages/app/src/interfaces/activity.ts

@@ -41,6 +41,7 @@ const ACTION_ADMIN_MAIL_SMTP_UPDATE = 'ADMIN_MAIL_SMTP_UPDATE';
 const ACTION_ADMIN_MAIL_SES_UPDATE = 'ADMIN_MAIL_SES_UPDATE';
 const ACTION_ADMIN_MAIL_SES_UPDATE = 'ADMIN_MAIL_SES_UPDATE';
 const ACTION_ADMIN_MAIL_TEST_SUBMIT = 'ADMIN_MAIL_TEST_SUBMIT ';
 const ACTION_ADMIN_MAIL_TEST_SUBMIT = 'ADMIN_MAIL_TEST_SUBMIT ';
 const ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE = 'ADMIN_FILE_UPLOAD_CONFIG_UPDATE';
 const ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE = 'ADMIN_FILE_UPLOAD_CONFIG_UPDATE';
+const ACTION_ADMIN_PLUGIN_UPDATE = 'ADMIN_PLUGIN_UPDATE';
 const ACTION_ADMIN_SECURITY_SETTINGS_UPDATE = 'ADMIN_SECURITY_SETTINGS_UPDATE';
 const ACTION_ADMIN_SECURITY_SETTINGS_UPDATE = 'ADMIN_SECURITY_SETTINGS_UPDATE';
 const ACTION_ADMIN_LINE_BREAK_UPDATE = 'ADMIN_LINE_BREAK_UPDATE';
 const ACTION_ADMIN_LINE_BREAK_UPDATE = 'ADMIN_LINE_BREAK_UPDATE';
 const ACTION_ADMIN_LAYOUT_UPDATE = 'ADMIN_LAYOUT_UPDATE';
 const ACTION_ADMIN_LAYOUT_UPDATE = 'ADMIN_LAYOUT_UPDATE';
@@ -98,6 +99,7 @@ export const SupportedAction = {
   ACTION_ADMIN_MAIL_SES_UPDATE,
   ACTION_ADMIN_MAIL_SES_UPDATE,
   ACTION_ADMIN_MAIL_TEST_SUBMIT,
   ACTION_ADMIN_MAIL_TEST_SUBMIT,
   ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE,
   ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE,
+  ACTION_ADMIN_PLUGIN_UPDATE,
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_LINE_BREAK_UPDATE,
   ACTION_ADMIN_LINE_BREAK_UPDATE,
   ACTION_ADMIN_LAYOUT_UPDATE,
   ACTION_ADMIN_LAYOUT_UPDATE,
@@ -112,6 +114,19 @@ export const SupportedAction = {
   ACTION_ADMIN_SEARCH_INDICES_REBUILD,
   ACTION_ADMIN_SEARCH_INDICES_REBUILD,
 } as const;
 } as const;
 
 
+// Action required for notification
+export const EssentialActionGroup = {
+  ACTION_PAGE_LIKE,
+  ACTION_PAGE_BOOKMARK,
+  ACTION_PAGE_UPDATE,
+  ACTION_PAGE_RENAME,
+  ACTION_PAGE_DUPLICATE,
+  ACTION_PAGE_DELETE,
+  ACTION_PAGE_DELETE_COMPLETELY,
+  ACTION_PAGE_REVERT,
+  ACTION_COMMENT_CREATE,
+} as const;
+
 export const ActionGroupSize = {
 export const ActionGroupSize = {
   Small: 'SMALL',
   Small: 'SMALL',
   Medium: 'MEDIUM',
   Medium: 'MEDIUM',
@@ -162,6 +177,7 @@ export const LargeActionGroup = {
   ACTION_ADMIN_MAIL_SES_UPDATE,
   ACTION_ADMIN_MAIL_SES_UPDATE,
   ACTION_ADMIN_MAIL_TEST_SUBMIT,
   ACTION_ADMIN_MAIL_TEST_SUBMIT,
   ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE,
   ACTION_ADMIN_FILE_UPLOAD_CONFIG_UPDATE,
+  ACTION_ADMIN_PLUGIN_UPDATE,
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_SECURITY_SETTINGS_UPDATE,
   ACTION_ADMIN_LINE_BREAK_UPDATE,
   ACTION_ADMIN_LINE_BREAK_UPDATE,
   ACTION_ADMIN_LAYOUT_UPDATE,
   ACTION_ADMIN_LAYOUT_UPDATE,
@@ -176,18 +192,6 @@ export const LargeActionGroup = {
   ACTION_ADMIN_SEARCH_INDICES_REBUILD,
   ACTION_ADMIN_SEARCH_INDICES_REBUILD,
 } as const;
 } as const;
 
 
-export const SupportedActionToNotified = {
-  ACTION_PAGE_LIKE,
-  ACTION_PAGE_BOOKMARK,
-  ACTION_PAGE_UPDATE,
-  ACTION_PAGE_RENAME,
-  ACTION_PAGE_DUPLICATE,
-  ACTION_PAGE_DELETE,
-  ACTION_PAGE_DELETE_COMPLETELY,
-  ACTION_PAGE_REVERT,
-  ACTION_COMMENT_CREATE,
-} as const;
-
 /*
 /*
  * For AuditLogManagement.tsx
  * For AuditLogManagement.tsx
  */
  */
@@ -211,10 +215,10 @@ export const CommentActions = Object.values({
 /*
 /*
  * Array
  * Array
  */
  */
-export const AllSupportedTargetModel = Object.values(SupportedTargetModel);
-export const AllSupportedEventModel = Object.values(SupportedEventModel);
-export const AllSupportedAction = Object.values(SupportedAction);
-export const AllSupportedActionToNotified = Object.values(SupportedActionToNotified);
+export const AllSupportedTargetModels = Object.values(SupportedTargetModel);
+export const AllSupportedEventModels = Object.values(SupportedEventModel);
+export const AllSupportedActions = Object.values(SupportedAction);
+export const AllEssentialActions = Object.values(EssentialActionGroup);
 export const AllSmallGroupActions = Object.values(SmallActionGroup);
 export const AllSmallGroupActions = Object.values(SmallActionGroup);
 export const AllMediumGroupActions = Object.values(MediumActionGroup);
 export const AllMediumGroupActions = Object.values(MediumActionGroup);
 export const AllLargeGroupActions = Object.values(LargeActionGroup);
 export const AllLargeGroupActions = Object.values(LargeActionGroup);

+ 6 - 6
packages/app/src/server/models/activity.ts

@@ -5,9 +5,9 @@ import {
 import mongoosePaginate from 'mongoose-paginate-v2';
 import mongoosePaginate from 'mongoose-paginate-v2';
 
 
 import {
 import {
-  IActivity, ISnapshot, AllSupportedAction, SupportedActionType,
-  AllSupportedTargetModel, SupportedTargetModelType,
-  AllSupportedEventModel, SupportedEventModelType,
+  IActivity, ISnapshot, AllSupportedActions, SupportedActionType,
+  AllSupportedTargetModels, SupportedTargetModelType,
+  AllSupportedEventModels, SupportedEventModelType,
 } from '~/interfaces/activity';
 } from '~/interfaces/activity';
 
 
 import loggerFactory from '../../utils/logger';
 import loggerFactory from '../../utils/logger';
@@ -56,7 +56,7 @@ const activitySchema = new Schema<ActivityDocument, ActivityModel>({
   },
   },
   targetModel: {
   targetModel: {
     type: String,
     type: String,
-    enum: AllSupportedTargetModel,
+    enum: AllSupportedTargetModels,
   },
   },
   target: {
   target: {
     type: Schema.Types.ObjectId,
     type: Schema.Types.ObjectId,
@@ -64,14 +64,14 @@ const activitySchema = new Schema<ActivityDocument, ActivityModel>({
   },
   },
   eventModel: {
   eventModel: {
     type: String,
     type: String,
-    enum: AllSupportedEventModel,
+    enum: AllSupportedEventModels,
   },
   },
   event: {
   event: {
     type: Schema.Types.ObjectId,
     type: Schema.Types.ObjectId,
   },
   },
   action: {
   action: {
     type: String,
     type: String,
-    enum: AllSupportedAction,
+    enum: AllSupportedActions,
     required: true,
     required: true,
   },
   },
   snapshot: snapshotSchema,
   snapshot: snapshotSchema,

+ 2 - 0
packages/app/src/server/models/config.ts

@@ -245,7 +245,9 @@ schema.statics.getLocalconfig = function(crowi) {
     globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
     globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
     pageLimitationL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
     pageLimitationL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
     pageLimitationXL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
     pageLimitationXL: crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
+    auditLogEnabled: crowi.configManager.getConfig('crowi', 'app:auditLogEnabled'),
     activityExpirationSeconds: crowi.configManager.getConfig('crowi', 'app:activityExpirationSeconds'),
     activityExpirationSeconds: crowi.configManager.getConfig('crowi', 'app:activityExpirationSeconds'),
+    auditLogAvailableActions: crowi.activityService.getAvailableActions(false),
     isSidebarDrawerMode: crowi.configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
     isSidebarDrawerMode: crowi.configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'),
     isSidebarClosedAtDockMode: crowi.configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
     isSidebarClosedAtDockMode: crowi.configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'),
   };
   };

+ 3 - 3
packages/app/src/server/models/in-app-notification.ts

@@ -4,7 +4,7 @@ import {
 } from 'mongoose';
 } from 'mongoose';
 import mongoosePaginate from 'mongoose-paginate-v2';
 import mongoosePaginate from 'mongoose-paginate-v2';
 
 
-import { AllSupportedTargetModel, AllSupportedAction } from '~/interfaces/activity';
+import { AllSupportedTargetModels, AllSupportedActions } from '~/interfaces/activity';
 import { InAppNotificationStatuses } from '~/interfaces/in-app-notification';
 import { InAppNotificationStatuses } from '~/interfaces/in-app-notification';
 
 
 import { ActivityDocument } from './activity';
 import { ActivityDocument } from './activity';
@@ -46,7 +46,7 @@ const inAppNotificationSchema = new Schema<InAppNotificationDocument, InAppNotif
   targetModel: {
   targetModel: {
     type: String,
     type: String,
     required: true,
     required: true,
-    enum: AllSupportedTargetModel,
+    enum: AllSupportedTargetModels,
   },
   },
   target: {
   target: {
     type: Schema.Types.ObjectId,
     type: Schema.Types.ObjectId,
@@ -56,7 +56,7 @@ const inAppNotificationSchema = new Schema<InAppNotificationDocument, InAppNotif
   action: {
   action: {
     type: String,
     type: String,
     required: true,
     required: true,
-    enum: AllSupportedAction,
+    enum: AllSupportedActions,
   },
   },
   activities: [
   activities: [
     {
     {

+ 2 - 2
packages/app/src/server/models/subscription.ts

@@ -3,7 +3,7 @@ import {
   Types, Document, Model, Schema,
   Types, Document, Model, Schema,
 } from 'mongoose';
 } from 'mongoose';
 
 
-import { AllSupportedTargetModel } from '~/interfaces/activity';
+import { AllSupportedTargetModels } from '~/interfaces/activity';
 import { SubscriptionStatusType, AllSubscriptionStatusType } from '~/interfaces/subscription';
 import { SubscriptionStatusType, AllSubscriptionStatusType } from '~/interfaces/subscription';
 
 
 
 
@@ -38,7 +38,7 @@ const subscriptionSchema = new Schema<SubscriptionDocument, SubscriptionModel>({
   targetModel: {
   targetModel: {
     type: String,
     type: String,
     required: true,
     required: true,
-    enum: AllSupportedTargetModel,
+    enum: AllSupportedTargetModels,
   },
   },
   target: {
   target: {
     type: Schema.Types.ObjectId,
     type: Schema.Types.ObjectId,

+ 7 - 0
packages/app/src/server/routes/apiv3/activity.ts

@@ -40,6 +40,13 @@ module.exports = (crowi: Crowi): Router => {
 
 
   // eslint-disable-next-line max-len
   // eslint-disable-next-line max-len
   router.get('/', apiLimiter, accessTokenParser, loginRequiredStrictly, adminRequired, validator.list, apiV3FormValidator, async(req: Request, res: ApiV3Response) => {
   router.get('/', apiLimiter, accessTokenParser, loginRequiredStrictly, adminRequired, validator.list, apiV3FormValidator, async(req: Request, res: ApiV3Response) => {
+    const auditLogEnabled = crowi.configManager?.getConfig('crowi', 'app:auditLogEnabled') || false;
+    if (!auditLogEnabled) {
+      const msg = 'AuditLog is not enabled';
+      logger.error(msg);
+      return res.apiv3Err(msg, 405);
+    }
+
     const limit = req.query.limit || await crowi.configManager?.getConfig('crowi', 'customize:showPageLimitationS') || 10;
     const limit = req.query.limit || await crowi.configManager?.getConfig('crowi', 'customize:showPageLimitationS') || 10;
     const offset = req.query.offset || 1;
     const offset = req.query.offset || 1;
 
 

+ 3 - 1
packages/app/src/server/routes/apiv3/app-settings.js

@@ -697,7 +697,7 @@ module.exports = (crowi) => {
    *                schema:
    *                schema:
    *                  $ref: '#/components/schemas/PluginSettingParams'
    *                  $ref: '#/components/schemas/PluginSettingParams'
    */
    */
-  router.put('/plugin-setting', loginRequiredStrictly, adminRequired, csrf, validator.pluginSetting, apiV3FormValidator, async(req, res) => {
+  router.put('/plugin-setting', loginRequiredStrictly, adminRequired, csrf, addActivity, validator.pluginSetting, apiV3FormValidator, async(req, res) => {
     const requestPluginSettingParams = {
     const requestPluginSettingParams = {
       'plugin:isEnabledPlugins': req.body.isEnabledPlugins,
       'plugin:isEnabledPlugins': req.body.isEnabledPlugins,
     };
     };
@@ -707,6 +707,8 @@ module.exports = (crowi) => {
       const pluginSettingParams = {
       const pluginSettingParams = {
         isEnabledPlugins: crowi.configManager.getConfig('crowi', 'plugin:isEnabledPlugins'),
         isEnabledPlugins: crowi.configManager.getConfig('crowi', 'plugin:isEnabledPlugins'),
       };
       };
+      const parameters = { action: SupportedAction.ACTION_ADMIN_PLUGIN_UPDATE };
+      activityEvent.emit('update', res.locals.activity._id, parameters);
       return res.apiv3({ pluginSettingParams });
       return res.apiv3({ pluginSettingParams });
     }
     }
     catch (err) {
     catch (err) {

+ 9 - 4
packages/app/src/server/service/activity.ts

@@ -1,8 +1,8 @@
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 
 
 import {
 import {
-  IActivity, SupportedActionType, ActionGroupSize, AllSupportedAction,
-  AllSmallGroupActions, AllMediumGroupActions, AllLargeGroupActions, AllSupportedActionToNotified,
+  IActivity, SupportedActionType, AllSupportedActions, ActionGroupSize,
+  AllEssentialActions, AllSmallGroupActions, AllMediumGroupActions, AllLargeGroupActions,
 } from '~/interfaces/activity';
 } from '~/interfaces/activity';
 import { IPage } from '~/interfaces/page';
 import { IPage } from '~/interfaces/page';
 import Activity from '~/server/models/activity';
 import Activity from '~/server/models/activity';
@@ -19,7 +19,7 @@ const parseActionString = (actionsString: string): SupportedActionType[] => {
   }
   }
 
 
   const actions = actionsString.split(',').map(value => value.trim());
   const actions = actionsString.split(',').map(value => value.trim());
-  return actions.filter(action => (AllSupportedAction as string[]).includes(action)) as SupportedActionType[];
+  return actions.filter(action => (AllSupportedActions as string[]).includes(action)) as SupportedActionType[];
 };
 };
 
 
 class ActivityService {
 class ActivityService {
@@ -58,10 +58,15 @@ class ActivityService {
   }
   }
 
 
   getAvailableActions = function(isIncludeEssentialActions = true): SupportedActionType[] {
   getAvailableActions = function(isIncludeEssentialActions = true): SupportedActionType[] {
+    const auditLogEnabled = this.crowi.configManager.getConfig('crowi', 'app:auditLogEnabled') || false;
     const auditLogActionGroupSize = this.crowi.configManager.getConfig('crowi', 'app:auditLogActionGroupSize') || ActionGroupSize.Small;
     const auditLogActionGroupSize = this.crowi.configManager.getConfig('crowi', 'app:auditLogActionGroupSize') || ActionGroupSize.Small;
     const auditLogAdditionalActions = this.crowi.configManager.getConfig('crowi', 'app:auditLogAdditionalActions');
     const auditLogAdditionalActions = this.crowi.configManager.getConfig('crowi', 'app:auditLogAdditionalActions');
     const auditLogExcludeActions = this.crowi.configManager.getConfig('crowi', 'app:auditLogExcludeActions');
     const auditLogExcludeActions = this.crowi.configManager.getConfig('crowi', 'app:auditLogExcludeActions');
 
 
+    if (!auditLogEnabled) {
+      return AllEssentialActions;
+    }
+
     const availableActionsSet = new Set<SupportedActionType>();
     const availableActionsSet = new Set<SupportedActionType>();
 
 
     // Set base action group
     // Set base action group
@@ -87,7 +92,7 @@ class ActivityService {
 
 
     // Add essentialActions
     // Add essentialActions
     if (isIncludeEssentialActions) {
     if (isIncludeEssentialActions) {
-      AllSupportedActionToNotified.forEach(action => availableActionsSet.add(action));
+      AllEssentialActions.forEach(action => availableActionsSet.add(action));
     }
     }
 
 
     return Array.from(availableActionsSet);
     return Array.from(availableActionsSet);

+ 6 - 0
packages/app/src/server/service/config-loader.ts

@@ -622,6 +622,12 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type: ValueType.NUMBER,
     type: ValueType.NUMBER,
     default: 8,
     default: 8,
   },
   },
+  AUDIT_LOG_ENABLED: {
+    ns: 'crowi',
+    key: 'app:auditLogEnabled',
+    type: ValueType.BOOLEAN,
+    default: false,
+  },
   ACTIVITY_EXPIRATION_SECONDS: {
   ACTIVITY_EXPIRATION_SECONDS: {
     ns: 'crowi',
     ns: 'crowi',
     key: 'app:activityExpirationSeconds',
     key: 'app:activityExpirationSeconds',

+ 3 - 3
packages/app/src/server/service/in-app-notification.ts

@@ -1,7 +1,7 @@
 import { subDays } from 'date-fns';
 import { subDays } from 'date-fns';
 import { Types } from 'mongoose';
 import { Types } from 'mongoose';
 
 
-import { AllSupportedActionToNotified, SupportedAction } from '~/interfaces/activity';
+import { AllEssentialActions, SupportedAction } from '~/interfaces/activity';
 import { HasObjectId } from '~/interfaces/has-object-id';
 import { HasObjectId } from '~/interfaces/has-object-id';
 import { InAppNotificationStatuses, PaginateResult } from '~/interfaces/in-app-notification';
 import { InAppNotificationStatuses, PaginateResult } from '~/interfaces/in-app-notification';
 import { IPage } from '~/interfaces/page';
 import { IPage } from '~/interfaces/page';
@@ -53,7 +53,7 @@ export default class InAppNotificationService {
   initActivityEventListeners(): void {
   initActivityEventListeners(): void {
     this.activityEvent.on('updated', async(activity: ActivityDocument, target: IPage) => {
     this.activityEvent.on('updated', async(activity: ActivityDocument, target: IPage) => {
       try {
       try {
-        const shouldNotification = activity != null && target != null && (AllSupportedActionToNotified as ReadonlyArray<string>).includes(activity.action);
+        const shouldNotification = activity != null && target != null && (AllEssentialActions as ReadonlyArray<string>).includes(activity.action);
         if (shouldNotification) {
         if (shouldNotification) {
           await this.createInAppNotification(activity, target);
           await this.createInAppNotification(activity, target);
         }
         }
@@ -200,7 +200,7 @@ export default class InAppNotificationService {
   };
   };
 
 
   createInAppNotification = async function(activity: ActivityDocument, target: IPage): Promise<void> {
   createInAppNotification = async function(activity: ActivityDocument, target: IPage): Promise<void> {
-    const shouldNotification = activity != null && target != null && (AllSupportedActionToNotified as ReadonlyArray<string>).includes(activity.action);
+    const shouldNotification = activity != null && target != null && (AllEssentialActions as ReadonlyArray<string>).includes(activity.action);
     if (shouldNotification) {
     if (shouldNotification) {
       let mentionedUsers: IUser[] = [];
       let mentionedUsers: IUser[] = [];
       if (activity.action === SupportedAction.ACTION_COMMENT_CREATE) {
       if (activity.action === SupportedAction.ACTION_COMMENT_CREATE) {

+ 4 - 1
packages/app/src/server/service/search-delegator/elasticsearch.ts

@@ -943,7 +943,6 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
 
 
   appendHighlight(query) {
   appendHighlight(query) {
     query.body.highlight = {
     query.body.highlight = {
-      max_analyzed_offset: 1000000 - 1, // Set the query parameter [max_analyzed_offset] to a value less than index setting [1000000] and this will tolerate long field values by truncating them.
       fields: {
       fields: {
         '*': {
         '*': {
           fragment_size: 40,
           fragment_size: 40,
@@ -953,6 +952,10 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
         },
         },
       },
       },
     };
     };
+
+    if (!this.isElasticsearchV6) {
+      query.body.highlight.max_analyzed_offset = 1000000 - 1; // Set the query parameter [max_analyzed_offset] to a value less than index setting [1000000] and this will tolerate long field values by truncating them.
+    }
   }
   }
 
 
   async search(data: SearchableData<ESQueryTerms>, user, userGroups, option): Promise<ISearchResult<unknown>> {
   async search(data: SearchableData<ESQueryTerms>, user, userGroups, option): Promise<ISearchResult<unknown>> {

+ 2 - 0
packages/app/src/server/views/layout/layout.html

@@ -113,8 +113,10 @@
 <div id="page-accessories-modal"></div>
 <div id="page-accessories-modal"></div>
 <div id="descendants-page-list-modal"></div>
 <div id="descendants-page-list-modal"></div>
 <div id="page-put-back-modal"></div>
 <div id="page-put-back-modal"></div>
+<div id="show-page-accessories-modal"></div>
 <div id="shortcuts-modal"></div>
 <div id="shortcuts-modal"></div>
 
 
+
 {% block body_end %}
 {% block body_end %}
 {% endblock %}
 {% endblock %}
 </body>
 </body>

+ 7 - 4
packages/app/src/stores/activity.ts

@@ -1,14 +1,17 @@
 import { SWRResponse } from 'swr';
 import { SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 import useSWRImmutable from 'swr/immutable';
 
 
-import { apiv3Get } from '../client/util/apiv3-client';
-import { IActivityHasId, ISearchFilter } from '../interfaces/activity';
-import { PaginateResult } from '../interfaces/mongoose-utils';
+import { apiv3Get } from '~/client/util/apiv3-client';
+import { IActivityHasId, ISearchFilter } from '~/interfaces/activity';
+import { PaginateResult } from '~/interfaces/mongoose-utils';
+import { useAuditLogEnabled } from '~/stores/context';
 
 
 export const useSWRxActivity = (limit?: number, offset?: number, searchFilter?: ISearchFilter): SWRResponse<PaginateResult<IActivityHasId>, Error> => {
 export const useSWRxActivity = (limit?: number, offset?: number, searchFilter?: ISearchFilter): SWRResponse<PaginateResult<IActivityHasId>, Error> => {
+  const { data: auditLogEnabled } = useAuditLogEnabled();
+
   const stringifiedSearchFilter = JSON.stringify(searchFilter);
   const stringifiedSearchFilter = JSON.stringify(searchFilter);
   return useSWRImmutable(
   return useSWRImmutable(
-    ['/activity', limit, offset, stringifiedSearchFilter],
+    auditLogEnabled ? ['/activity', limit, offset, stringifiedSearchFilter] : null,
     (endpoint, limit, offset, stringifiedSearchFilter) => apiv3Get(endpoint, { limit, offset, searchFilter: stringifiedSearchFilter })
     (endpoint, limit, offset, stringifiedSearchFilter) => apiv3Get(endpoint, { limit, offset, searchFilter: stringifiedSearchFilter })
       .then(result => result.data.paginationResult),
       .then(result => result.data.paginationResult),
   );
   );

+ 10 - 0
packages/app/src/stores/context.tsx

@@ -3,6 +3,8 @@ import { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 import useSWRImmutable from 'swr/immutable';
 
 
 
 
+import { SupportedActionType } from '~/interfaces/activity';
+
 import { TargetAndAncestors, IsNotFoundPermalink } from '../interfaces/page-listing-results';
 import { TargetAndAncestors, IsNotFoundPermalink } from '../interfaces/page-listing-results';
 import { IUser } from '../interfaces/user';
 import { IUser } from '../interfaces/user';
 
 
@@ -175,10 +177,18 @@ export const useDefaultIndentSize = (initialData?: number) : SWRResponse<number,
   return useStaticSWR<number, Error>('defaultIndentSize', initialData, { fallbackData: 4 });
   return useStaticSWR<number, Error>('defaultIndentSize', initialData, { fallbackData: 4 });
 };
 };
 
 
+export const useAuditLogEnabled = (initialData?: boolean): SWRResponse<boolean, Error> => {
+  return useStaticSWR<boolean, Error>('auditLogEnabled', initialData, { fallbackData: false });
+};
+
 export const useActivityExpirationSeconds = (initialData?: number) : SWRResponse<number, Error> => {
 export const useActivityExpirationSeconds = (initialData?: number) : SWRResponse<number, Error> => {
   return useStaticSWR<number, Error>('activityExpirationSeconds', initialData);
   return useStaticSWR<number, Error>('activityExpirationSeconds', initialData);
 };
 };
 
 
+export const useAuditLogAvailableActions = (initialData?: Array<SupportedActionType>) : SWRResponse<Array<SupportedActionType>, Error> => {
+  return useStaticSWR<Array<SupportedActionType>, Error>('auditLogAvailableActions', initialData);
+};
+
 export const useGrowiVersion = (initialData?: string): SWRResponse<string, any> => {
 export const useGrowiVersion = (initialData?: string): SWRResponse<string, any> => {
   return useStaticSWR('growiVersion', initialData);
   return useStaticSWR('growiVersion', initialData);
 };
 };