Răsfoiți Sursa

Merge branch 'master' into fix/cleanup-customize-logo-setting

Yuki Takei 2 ani în urmă
părinte
comite
bc82ad405a
59 a modificat fișierele cu 125 adăugiri și 586 ștergeri
  1. 13 1
      apps/app/.eslintrc.js
  2. 0 4
      apps/app/src/components/Admin/Common/AdminInstallButtonRow.tsx
  3. 1 1
      apps/app/src/components/Admin/G2GDataTransfer.tsx
  4. 3 3
      apps/app/src/components/Admin/G2GDataTransferExportForm.tsx
  5. 1 1
      apps/app/src/components/Admin/LegacySlackIntegration/LegacySlackIntegration.jsx
  6. 12 10
      apps/app/src/components/Admin/Notification/GlobalNotification.jsx
  7. 9 22
      apps/app/src/components/Admin/Security/DeleteAllShareLinksModal.jsx
  8. 0 2
      apps/app/src/components/Admin/Security/SecurityManagement.tsx
  9. 0 1
      apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySecretTokenSection.jsx
  10. 9 21
      apps/app/src/components/Admin/SlackIntegration/DeleteSlackBotSettingsModal.jsx
  11. 1 3
      apps/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx
  12. 2 1
      apps/app/src/components/Admin/Users/PasswordResetModal.jsx
  13. 1 2
      apps/app/src/components/Admin/Users/UserInviteModal.jsx
  14. 0 243
      apps/app/src/components/ArchiveCreateModal.jsx
  15. 1 1
      apps/app/src/components/Bookmarks/BookmarkFolderNameInput.tsx
  16. 0 1
      apps/app/src/components/CompleteUserRegistration.tsx
  17. 1 1
      apps/app/src/components/CompleteUserRegistrationForm.tsx
  18. 1 5
      apps/app/src/components/DescendantsPageList.tsx
  19. 3 2
      apps/app/src/components/Hotkeys/Subscribers/EditPage.jsx
  20. 1 1
      apps/app/src/components/Hotkeys/Subscribers/FocusToGlobalSearch.jsx
  21. 0 1
      apps/app/src/components/Hotkeys/Subscribers/ShowStaffCredit.jsx
  22. 2 2
      apps/app/src/components/InvitedForm.tsx
  23. 14 16
      apps/app/src/components/LoginForm.tsx
  24. 1 1
      apps/app/src/components/Page/RevisionRenderer.tsx
  25. 11 16
      apps/app/src/components/Page/TagEditModal.tsx
  26. 5 5
      apps/app/src/components/PageCreateModal.jsx
  27. 2 6
      apps/app/src/components/PageDuplicateModal.tsx
  28. 2 2
      apps/app/src/components/PageRenameModal.tsx
  29. 1 1
      apps/app/src/components/PageStatusAlert.tsx
  30. 1 0
      apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx
  31. 1 1
      apps/app/src/components/StaffCredit/StaffCredit.tsx
  32. 1 1
      apps/app/src/components/TableOfContents.tsx
  33. 1 0
      apps/app/src/components/UsersHomepageFooter.tsx
  34. 3 5
      apps/app/src/server/models/attachment.js
  35. 0 2
      apps/app/src/server/models/index.js
  36. 1 0
      apps/app/src/server/models/named-query.ts
  37. 0 22
      apps/app/src/server/models/page-archive.js
  38. 0 1
      apps/app/src/server/models/page-operation.ts
  39. 0 65
      apps/app/src/server/routes/admin.js
  40. 1 1
      apps/app/src/server/routes/apiv3/bookmarks.js
  41. 1 0
      apps/app/src/server/routes/apiv3/installer.ts
  42. 1 82
      apps/app/src/server/routes/apiv3/page.js
  43. 2 4
      apps/app/src/server/routes/apiv3/slack-integration-legacy-settings.js
  44. 1 2
      apps/app/src/server/routes/apiv3/slack-integration-settings.js
  45. 1 1
      apps/app/src/server/routes/apiv3/slack-integration.js
  46. 1 1
      apps/app/src/server/routes/apiv3/user-activation.ts
  47. 0 3
      apps/app/src/server/routes/apiv3/user-group.js
  48. 0 1
      apps/app/src/server/routes/index.js
  49. 0 1
      apps/app/src/server/routes/me.js
  50. 0 1
      apps/app/src/server/routes/page.js
  51. 0 1
      apps/app/src/server/service/customize.ts
  52. 1 1
      apps/app/src/server/service/search.ts
  53. 2 0
      apps/app/src/server/service/slack-command-handler/create-page-service.js
  54. 0 2
      apps/app/src/server/service/slack-command-handler/search.js
  55. 1 1
      apps/app/src/server/service/slack-command-handler/togetter.js
  56. 0 1
      apps/app/src/server/service/user-notification/index.ts
  57. 2 1
      apps/app/src/services/renderer/renderer.tsx
  58. 6 8
      apps/app/src/stores/in-app-notification.ts
  59. 0 1
      apps/app/test/integration/models/page-redirect.test.js

+ 13 - 1
apps/app/.eslintrc.js

@@ -33,11 +33,23 @@ module.exports = {
     '@typescript-eslint/no-this-alias': ['warn'],
     '@typescript-eslint/no-this-alias': ['warn'],
   },
   },
   overrides: [
   overrides: [
+    {
+      // enable the rule specifically for JavaScript files
+      files: ['*.js', '*.jsx'],
+      rules: {
+        // set 'warn' temporarily -- 2023.08.14 Yuki Takei
+        'react/prop-types': 'warn',
+        // set 'warn' temporarily -- 2023.08.14 Yuki Takei
+        'no-unused-vars': ['warn'],
+      },
+    },
     {
     {
       // enable the rule specifically for TypeScript files
       // enable the rule specifically for TypeScript files
       files: ['*.ts', '*.tsx'],
       files: ['*.ts', '*.tsx'],
       rules: {
       rules: {
-        // '@typescript-eslint/explicit-module-boundary-types': ['error'],
+        'no-unused-vars': 'off',
+        // set 'warn' temporarily -- 2023.08.14 Yuki Takei
+        'react/prop-types': 'warn',
         // set 'warn' temporarily -- 2022.07.25 Yuki Takei
         // set 'warn' temporarily -- 2022.07.25 Yuki Takei
         '@typescript-eslint/explicit-module-boundary-types': ['warn'],
         '@typescript-eslint/explicit-module-boundary-types': ['warn'],
       },
       },

+ 0 - 4
apps/app/src/components/Admin/Common/AdminInstallButtonRow.tsx

@@ -1,7 +1,5 @@
 import React from 'react';
 import React from 'react';
 
 
-import { useTranslation } from 'next-i18next';
-
 type Props = {
 type Props = {
   onClick: () => void,
   onClick: () => void,
   disabled: boolean,
   disabled: boolean,
@@ -9,8 +7,6 @@ type Props = {
 }
 }
 
 
 export const AdminInstallButtonRow = (props: Props): JSX.Element => {
 export const AdminInstallButtonRow = (props: Props): JSX.Element => {
-  // TODO: const { t } = useTranslation('admin');
-
   return (
   return (
     <div className="row my-3">
     <div className="row my-3">
       <div className="mx-auto">
       <div className="mx-auto">

+ 1 - 1
apps/app/src/components/Admin/G2GDataTransfer.tsx

@@ -1,5 +1,5 @@
 import React, {
 import React, {
-  ChangeEvent, useCallback, useEffect, useState,
+  useCallback, useEffect, useState,
 } from 'react';
 } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';

+ 3 - 3
apps/app/src/components/Admin/G2GDataTransferExportForm.tsx

@@ -122,7 +122,7 @@ const G2GDataTransferExportForm = (props: Props): JSX.Element => {
     );
     );
   };
   };
 
 
-  const WarnForGroups = ({ errors }): JSX.Element => {
+  const WarnForGroups = ({ errors }: { errors: Error[] }): JSX.Element => {
     if (errors.length === 0) {
     if (errors.length === 0) {
       return <></>;
       return <></>;
     }
     }
@@ -130,8 +130,8 @@ const G2GDataTransferExportForm = (props: Props): JSX.Element => {
     return (
     return (
       <div className="alert alert-warning">
       <div className="alert alert-warning">
         <ul>
         <ul>
-          {errors.map((error, i) => {
-            return <li key={i}>{error}</li>;
+          {errors.map((error) => {
+            return <li>{error.message}</li>;
           })}
           })}
         </ul>
         </ul>
       </div>
       </div>

+ 1 - 1
apps/app/src/components/Admin/LegacySlackIntegration/LegacySlackIntegration.jsx

@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';

+ 12 - 10
apps/app/src/components/Admin/Notification/GlobalNotification.jsx

@@ -39,7 +39,8 @@ const GlobalNotification = (props) => {
       <p className="card well">
       <p className="card well">
         {/* eslint-disable-next-line react/no-danger */}
         {/* eslint-disable-next-line react/no-danger */}
         <span dangerouslySetInnerHTML={{ __html: t('notification_settings.link_notification_help') }} />
         <span dangerouslySetInnerHTML={{ __html: t('notification_settings.link_notification_help') }} />
-      </p><div className="row mb-4">
+      </p>
+      <div className="row mb-4">
         <div className="col-md-8 offset-md-2">
         <div className="col-md-8 offset-md-2">
           <div className="custom-control custom-checkbox custom-checkbox-success">
           <div className="custom-control custom-checkbox custom-checkbox-success">
             <input
             <input
@@ -55,21 +56,23 @@ const GlobalNotification = (props) => {
             </label>
             </label>
           </div>
           </div>
         </div>
         </div>
-      </div><div className="row mb-4">
-            <div className="col-md-8 offset-md-2">
+      </div>
+
+      <div className="row mb-4">
+        <div className="col-md-8 offset-md-2">
           <div className="custom-control custom-checkbox custom-checkbox-success">
           <div className="custom-control custom-checkbox custom-checkbox-success">
-                <input
+            <input
               id="isNotificationForGroupPageEnabled"
               id="isNotificationForGroupPageEnabled"
               className="custom-control-input"
               className="custom-control-input"
               type="checkbox"
               type="checkbox"
               checked={adminNotificationContainer.state.isNotificationForGroupPageEnabled || false}
               checked={adminNotificationContainer.state.isNotificationForGroupPageEnabled || false}
               onChange={() => { adminNotificationContainer.switchIsNotificationForGroupPageEnabled() }}
               onChange={() => { adminNotificationContainer.switchIsNotificationForGroupPageEnabled() }}
             />
             />
-                <label className="custom-control-label" htmlFor="isNotificationForGroupPageEnabled">
+            <label className="custom-control-label" htmlFor="isNotificationForGroupPageEnabled">
               {/* eslint-disable-next-line react/no-danger */}
               {/* eslint-disable-next-line react/no-danger */}
               <span dangerouslySetInnerHTML={{ __html: t('notification_settings.group_notification_help') }} />
               <span dangerouslySetInnerHTML={{ __html: t('notification_settings.group_notification_help') }} />
             </label>
             </label>
-              </div>
+          </div>
         </div>
         </div>
       </div>
       </div>
       <div className="row my-3">
       <div className="row my-3">
@@ -83,6 +86,7 @@ const GlobalNotification = (props) => {
           </button>
           </button>
         </div>
         </div>
       </div>
       </div>
+
       <h2 className="border-bottom mb-5">{t('notification_settings.notification_list')}
       <h2 className="border-bottom mb-5">{t('notification_settings.notification_list')}
         <button
         <button
           className="btn btn-outline-secondary pull-right"
           className="btn btn-outline-secondary pull-right"
@@ -90,10 +94,8 @@ const GlobalNotification = (props) => {
           onClick={() => router.push('/admin/global-notification/new')}
           onClick={() => router.push('/admin/global-notification/new')}
         >{t('notification_settings.add_notification')}
         >{t('notification_settings.add_notification')}
         </button>
         </button>
-        {/* <a href="/admin/global-notification/new">
-      <p className="btn btn-outline-secondary pull-right">{t('notification_setting.add_notification')}</p>
-    </a> */}
-      </h2><table className="table table-bordered">
+      </h2>
+      <table className="table table-bordered">
         <thead>
         <thead>
           <tr>
           <tr>
             <th>ON/OFF</th>
             <th>ON/OFF</th>

+ 9 - 22
apps/app/src/components/Admin/Security/DeleteAllShareLinksModal.jsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
@@ -7,29 +7,16 @@ import {
 } from 'reactstrap';
 } from 'reactstrap';
 
 
 const DeleteAllShareLinksModal = React.memo((props) => {
 const DeleteAllShareLinksModal = React.memo((props) => {
-  const { t } = props;
+  const { t, onClickDeleteButton, onClose } = props;
 
 
-  function closeModal() {
-    if (props.onClose == null) {
-      return;
-    }
+  const deleteAllLinkHandler = useCallback(() => {
+    onClickDeleteButton?.();
+    onClose?.();
+  }, [onClickDeleteButton, onClose]);
 
 
-    props.onClose();
-  }
-
-  function deleteAllLinkHandler() {
-    if (props.onClickDeleteButton == null) {
-      return;
-    }
-
-    props.onClickDeleteButton();
-
-    closeModal();
-  }
-
-  function closeButtonHandler() {
-    closeModal();
-  }
+  const closeButtonHandler = useCallback(() => {
+    onClose?.();
+  }, [onClose]);
 
 
   return (
   return (
     <Modal isOpen={props.isOpen} toggle={closeButtonHandler} className="page-comment-delete-modal">
     <Modal isOpen={props.isOpen} toggle={closeButtonHandler} className="page-comment-delete-modal">

+ 0 - 2
apps/app/src/components/Admin/Security/SecurityManagement.tsx

@@ -1,7 +1,5 @@
 import React, { useEffect, useCallback } from 'react';
 import React, { useEffect, useCallback } from 'react';
 
 
-import PropTypes from 'prop-types';
-
 import AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
 import AdminGeneralSecurityContainer from '~/client/services/AdminGeneralSecurityContainer';
 import { toastError } from '~/client/util/toastr';
 import { toastError } from '~/client/util/toastr';
 import { toArrayIfNot } from '~/utils/array-utils';
 import { toArrayIfNot } from '~/utils/array-utils';

+ 0 - 1
apps/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxySecretTokenSection.jsx

@@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 
 
-import { withUnstatedContainers } from '../../UnstatedUtils';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
 
 
 
 

+ 9 - 21
apps/app/src/components/Admin/SlackIntegration/DeleteSlackBotSettingsModal.jsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
@@ -7,28 +7,16 @@ import {
 } from 'reactstrap';
 } from 'reactstrap';
 
 
 const DeleteSlackBotSettingsModal = React.memo((props) => {
 const DeleteSlackBotSettingsModal = React.memo((props) => {
-  const { t } = useTranslation();
+  const { t, onClickDeleteButton, onClose } = useTranslation();
 
 
-  function closeModal() {
-    if (props.onClose == null) {
-      return;
-    }
+  const deleteSlackCredentialsHandler = useCallback(() => {
+    onClickDeleteButton?.();
+    onClose?.();
+  }, [onClickDeleteButton, onClose]);
 
 
-    props.onClose();
-  }
-
-  function deleteSlackCredentialsHandler() {
-    if (props.onClickDeleteButton == null) {
-      return;
-    }
-    props.onClickDeleteButton();
-
-    closeModal();
-  }
-
-  function closeButtonHandler() {
-    closeModal();
-  }
+  const closeButtonHandler = useCallback(() => {
+    onClose?.();
+  }, [onClose]);
 
 
   return (
   return (
     <Modal isOpen={props.isOpen} toggle={closeButtonHandler} className="page-comment-delete-modal">
     <Modal isOpen={props.isOpen} toggle={closeButtonHandler} className="page-comment-delete-modal">

+ 1 - 3
apps/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx

@@ -1,11 +1,9 @@
 /* eslint-disable react/prop-types */
 /* eslint-disable react/prop-types */
-import React, { useState, useCallback } from 'react';
+import React, { useState } from 'react';
 
 
 import { SlackbotType } from '@growi/slack';
 import { SlackbotType } from '@growi/slack';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
-import { CopyToClipboard } from 'react-copy-to-clipboard';
-import { Tooltip } from 'reactstrap';
 
 
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
 import { toastSuccess, toastError } from '~/client/util/toastr';
 import { toastSuccess, toastError } from '~/client/util/toastr';

+ 2 - 1
apps/app/src/components/Admin/Users/PasswordResetModal.jsx

@@ -30,7 +30,7 @@ class PasswordResetModal extends React.Component {
   }
   }
 
 
   async resetPassword() {
   async resetPassword() {
-    const { t, userForPasswordResetModal } = this.props;
+    const { userForPasswordResetModal } = this.props;
     try {
     try {
       const res = await apiv3Put('/users/reset-password', { id: userForPasswordResetModal._id });
       const res = await apiv3Put('/users/reset-password', { id: userForPasswordResetModal._id });
       const { newPassword } = res.data;
       const { newPassword } = res.data;
@@ -214,6 +214,7 @@ const PasswordResetModalWrapperFC = (props) => {
 PasswordResetModal.propTypes = {
 PasswordResetModal.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   t: PropTypes.func.isRequired, // i18next
 
 
+  isMailerSetup: PropTypes.bool.isRequired,
   isOpen: PropTypes.bool.isRequired,
   isOpen: PropTypes.bool.isRequired,
   onClose: PropTypes.func.isRequired,
   onClose: PropTypes.func.isRequired,
   userForPasswordResetModal: PropTypes.object,
   userForPasswordResetModal: PropTypes.object,

+ 1 - 2
apps/app/src/components/Admin/Users/UserInviteModal.jsx

@@ -208,8 +208,6 @@ class UserInviteModal extends React.Component {
 
 
   async handleSubmit() {
   async handleSubmit() {
     const { adminUsersContainer } = this.props;
     const { adminUsersContainer } = this.props;
-    // eslint-disable-next-line no-unused-vars
-    const { isCreateUserButtonPushed } = this.state;
 
 
     this.setState({ isCreateUserButtonPushed: true });
     this.setState({ isCreateUserButtonPushed: true });
 
 
@@ -295,6 +293,7 @@ const UserInviteModalWrapper = withUnstatedContainers(UserInviteModalWrapperFC,
 UserInviteModal.propTypes = {
 UserInviteModal.propTypes = {
   t: PropTypes.func.isRequired, // i18next
   t: PropTypes.func.isRequired, // i18next
   adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
   adminUsersContainer: PropTypes.instanceOf(AdminUsersContainer).isRequired,
+  isMailerSetup: PropTypes.bool.isRequired,
 };
 };
 
 
 export default UserInviteModalWrapper;
 export default UserInviteModalWrapper;

+ 0 - 243
apps/app/src/components/ArchiveCreateModal.jsx

@@ -1,243 +0,0 @@
-import React, { useState, useCallback } from 'react';
-
-import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
-import {
-  Modal, ModalHeader, ModalBody, ModalFooter,
-} from 'reactstrap';
-
-import { apiv3Post } from '~/client/util/apiv3-client';
-import { toastSuccess, toastError } from '~/client/util/toastr';
-
-
-const ArchiveCreateModal = (props) => {
-  const { t } = useTranslation();
-  const { appContainer } = props;
-  const [isCommentDownload, setIsCommentDownload] = useState(false);
-  const [isAttachmentFileDownload, setIsAttachmentFileDownload] = useState(false);
-  const [isSubordinatedPageDownload, setIsSubordinatedPageDownload] = useState(false);
-  const [fileType, setFileType] = useState('markdown');
-  const [hierarchyType, setHierarchyType] = useState('allSubordinatedPage');
-  const [hierarchyValue, setHierarchyValue] = useState(1);
-
-  function changeIsCommentDownloadHandler() {
-    setIsCommentDownload(!isCommentDownload);
-  }
-
-  function changeIsAttachmentFileDownloadHandler() {
-    setIsAttachmentFileDownload(!isAttachmentFileDownload);
-  }
-
-  function changeIsSubordinatedPageDownloadHandler() {
-    setIsSubordinatedPageDownload(!isSubordinatedPageDownload);
-  }
-
-  function closeModalHandler() {
-    if (props.onClose == null) {
-      return;
-    }
-
-    props.onClose();
-  }
-
-  const handleChangeFileType = useCallback(
-    (filetype) => {
-      setFileType(filetype);
-    },
-    [],
-  );
-
-  function handleChangeSubordinatedType(hierarchyType) {
-    setHierarchyType(hierarchyType);
-  }
-
-  function handleHierarchyDepth(hierarchyValue) {
-    setHierarchyValue(hierarchyValue);
-  }
-
-
-  async function done() {
-    try {
-      await apiv3Post('/page/archive', {
-        rootPagePath: props.path,
-        isCommentDownload,
-        isAttachmentFileDownload,
-        isSubordinatedPageDownload,
-        fileType,
-        hierarchyType,
-        hierarchyValue,
-      });
-      toastSuccess(t('Submitted the request to create the archive'));
-      closeModalHandler();
-    }
-    catch (e) {
-      toastError(e);
-    }
-  }
-
-  return (
-    <Modal isOpen={props.isOpen} toggle={closeModalHandler}>
-      <ModalHeader tag="h4" toggle={closeModalHandler} className="bg-primary text-white">
-        {t('Create Archive Page')}
-      </ModalHeader>
-      <ModalBody>
-        <div className="form-group">
-          <div className="form-group">
-            <label>{t('Target page')}</label>
-            <br />
-            <code>{props.path}</code>
-          </div>
-
-          <div className="custom-control-inline">
-            <label>{t('File type')}: </label>
-          </div>
-          <div className="custom-control custom-radio custom-control-inline ">
-            <input
-              type="radio"
-              className="custom-control-input"
-              id="customRadio1"
-              name="isFileType"
-              value="customRadio1"
-              checked={fileType === 'markdown'}
-              onChange={() => {
-                handleChangeFileType('markdown');
-              }}
-            />
-            <label className="custom-control-label" htmlFor="customRadio1">
-              MarkDown(.md)
-            </label>
-          </div>
-
-          <div className="custom-control custom-radio custom-control-inline ">
-            <input
-              type="radio"
-              className="custom-control-input"
-              id="customRadio2"
-              name="isFileType"
-              value="customRadio2"
-              checked={fileType === 'pdf'}
-              onChange={() => {
-                handleChangeFileType('pdf');
-              }}
-            />
-            <label className="custom-control-label" htmlFor="customRadio2">
-              PDF(.pdf)
-            </label>
-          </div>
-        </div>
-
-        <div className="my-1 custom-control custom-checkbox custom-checkbox-info">
-          <input
-            className="custom-control-input"
-            name="comment"
-            id="commentFile"
-            type="checkbox"
-            checked={isCommentDownload}
-            onChange={changeIsCommentDownloadHandler}
-          />
-          <label className="custom-control-label" htmlFor="commentFile">
-            {t('Include Comment')}
-          </label>
-        </div>
-        <div className="my-1 custom-control custom-checkbox custom-checkbox-info">
-          <input
-            className="custom-control-input"
-            id="downloadFile"
-            type="checkbox"
-            checked={isAttachmentFileDownload}
-            onChange={changeIsAttachmentFileDownloadHandler}
-          />
-          <label className="custom-control-label" htmlFor="downloadFile">
-            {t('Include Attachment File')}
-          </label>
-        </div>
-        <div className="my-1 custom-control custom-checkbox custom-checkbox-info">
-          <input
-            className="custom-control-input"
-            id="subordinatedFile"
-            type="checkbox"
-            checked={isSubordinatedPageDownload}
-            onChange={changeIsSubordinatedPageDownloadHandler}
-          />
-          <label className="custom-control-label" htmlFor="subordinatedFile">
-            {t('Include Subordinated Page')}
-          </label>
-          {isSubordinatedPageDownload && (
-            <>
-              <div className="FormGroup">
-                <div className="my-1 custom-control custom-radio custom-control-inline ">
-                  <input
-                    type="radio"
-                    className="custom-control-input"
-                    id="customRadio3"
-                    name="isSubordinatedType"
-                    value="customRadio3"
-                    disabled={!isSubordinatedPageDownload}
-                    checked={hierarchyType === 'allSubordinatedPage'}
-                    onChange={() => {
-                      handleChangeSubordinatedType('allSubordinatedPage');
-                    }}
-                  />
-                  <label className="custom-control-label" htmlFor="customRadio3">
-                    {t('All Subordinated Page')}
-                  </label>
-                </div>
-              </div>
-              <div className="FormGroup">
-                <div className="my-1 custom-control custom-radio custom-control-inline">
-                  <input
-                    type="radio"
-                    className="custom-control-input"
-                    id="customRadio4"
-                    name="isSubordinatedType"
-                    value="customRadio4"
-                    disabled={!isSubordinatedPageDownload}
-                    checked={hierarchyType === 'decideHierarchy'}
-                    onChange={() => {
-                      handleChangeSubordinatedType('decideHierarchy');
-                    }}
-                  />
-                  <label className="my-1 custom-control-label" htmlFor="customRadio4">
-                    {t('Specify Hierarchy')}
-                  </label>
-                </div>
-              </div>
-              <div className="my-1 custom-control costom-control-inline">
-                <input
-                  type="number"
-                  min="1"
-                  max="10"
-                  disabled={hierarchyType === 'allSubordinatedPage'}
-                  value={hierarchyValue}
-                  placeholder="1"
-                  onChange={(e) => {
-                    handleHierarchyDepth(e.target.value);
-                  }}
-                />
-              </div>
-            </>
-          )}
-        </div>
-      </ModalBody>
-      <ModalFooter>
-        {/* TO DO implement correct number at GW-3053 */}
-        合計{props.totalPages}ページ取得
-        {props.errorMessage}
-        <button type="button" className="btn btn-primary" onClick={done}>
-          Done
-        </button>
-      </ModalFooter>
-    </Modal>
-  );
-};
-
-ArchiveCreateModal.propTypes = {
-  // appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  isOpen: PropTypes.bool.isRequired,
-  onClose: PropTypes.func,
-  path: PropTypes.string.isRequired,
-  totalPages: PropTypes.number,
-  errorMessage: PropTypes.string,
-};
-
-export default ArchiveCreateModal;

+ 1 - 1
apps/app/src/components/Bookmarks/BookmarkFolderNameInput.tsx

@@ -1,6 +1,6 @@
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
-import { inputValidator, ValidationTarget } from '~/client/util/input-validator';
+import { ValidationTarget } from '~/client/util/input-validator';
 import ClosableTextInput from '~/components/Common/ClosableTextInput';
 import ClosableTextInput from '~/components/Common/ClosableTextInput';
 
 
 
 

+ 0 - 1
apps/app/src/components/CompleteUserRegistration.tsx

@@ -1,7 +1,6 @@
 import React, { FC } from 'react';
 import React, { FC } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import Link from 'next/link';
 
 
 export const CompleteUserRegistration: FC = () => {
 export const CompleteUserRegistration: FC = () => {
   const { t } = useTranslation();
   const { t } = useTranslation();

+ 1 - 1
apps/app/src/components/CompleteUserRegistrationForm.tsx

@@ -170,7 +170,7 @@ const CompleteUserRegistrationForm: React.FC<Props> = (props: Props) => {
               </div>
               </div>
 
 
               <div className="input-group justify-content-center d-flex mt-5">
               <div className="input-group justify-content-center d-flex mt-5">
-                <button disabled={forceDisableForm || disableForm} className="btn btn-fill" id="register">
+                <button type="button" disabled={forceDisableForm || disableForm} className="btn btn-fill" id="register">
                   <div className="eff"></div>
                   <div className="eff"></div>
                   <span className="btn-label"><i className="icon-user-follow"></i></span>
                   <span className="btn-label"><i className="icon-user-follow"></i></span>
                   <span className="btn-label-text">{t('Create')}</span>
                   <span className="btn-label-text">{t('Create')}</span>

+ 1 - 5
apps/app/src/components/DescendantsPageList.tsx

@@ -82,10 +82,6 @@ const DescendantsPageListSubstance = (props: SubstanceProps): JSX.Element => {
     }
     }
   }, [onPagePutBacked, t]);
   }, [onPagePutBacked, t]);
 
 
-  function setPageNumber(selectedPageNumber) {
-    setActivePage(selectedPageNumber);
-  }
-
   if (pagingResult == null) {
   if (pagingResult == null) {
     return (
     return (
       <div className="wiki">
       <div className="wiki">
@@ -113,7 +109,7 @@ const DescendantsPageListSubstance = (props: SubstanceProps): JSX.Element => {
         <div className="my-4">
         <div className="my-4">
           <PaginationWrapper
           <PaginationWrapper
             activePage={activePage}
             activePage={activePage}
-            changePage={setPageNumber}
+            changePage={selectedPageNumber => setActivePage(selectedPageNumber)}
             totalItemsCount={pagingResult.totalCount}
             totalItemsCount={pagingResult.totalCount}
             pagingLimit={pagingResult.limit}
             pagingLimit={pagingResult.limit}
             align="center"
             align="center"

+ 3 - 2
apps/app/src/components/Hotkeys/Subscribers/EditPage.jsx

@@ -1,8 +1,9 @@
-import React, { useEffect } from 'react';
+import { useEffect } from 'react';
+
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
-import { EditorMode, useEditorMode } from '~/stores/ui';
 import { useIsEditable } from '~/stores/context';
 import { useIsEditable } from '~/stores/context';
+import { EditorMode, useEditorMode } from '~/stores/ui';
 
 
 const EditPage = (props) => {
 const EditPage = (props) => {
   const { data: isEditable } = useIsEditable();
   const { data: isEditable } = useIsEditable();

+ 1 - 1
apps/app/src/components/Hotkeys/Subscribers/FocusToGlobalSearch.jsx

@@ -1,4 +1,4 @@
-import { FC, useEffect } from 'react';
+import { useEffect } from 'react';
 
 
 import { useIsEditable } from '~/stores/context';
 import { useIsEditable } from '~/stores/context';
 import { useGlobalSearchFormRef } from '~/stores/ui';
 import { useGlobalSearchFormRef } from '~/stores/ui';

+ 0 - 1
apps/app/src/components/Hotkeys/Subscribers/ShowStaffCredit.jsx

@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
 import StaffCredit from '../../StaffCredit/StaffCredit';
 import StaffCredit from '../../StaffCredit/StaffCredit';

+ 2 - 2
apps/app/src/components/InvitedForm.tsx

@@ -58,8 +58,8 @@ export const InvitedForm = (props: InvitedFormProps): JSX.Element => {
       <>
       <>
         { loginErrors != null && loginErrors.length > 0 ? (
         { loginErrors != null && loginErrors.length > 0 ? (
           <p className="alert alert-danger">
           <p className="alert alert-danger">
-            { loginErrors.map((err, index) => {
-              return <span key={index}>{ t(err.message) }<br /></span>;
+            { loginErrors.map((err) => {
+              return <span>{ t(err.message) }<br /></span>;
             }) }
             }) }
           </p>
           </p>
         ) : (
         ) : (

+ 14 - 16
apps/app/src/components/LoginForm.tsx

@@ -141,8 +141,9 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
     if (errors == null || errors.length === 0) return <></>;
     if (errors == null || errors.length === 0) return <></>;
     return (
     return (
       <div className="alert alert-danger">
       <div className="alert alert-danger">
-        {errors.map((err, index) => {
-          return <small key={index} dangerouslySetInnerHTML={{ __html: tWithOpt(err.message, err.args) }}></small>;
+        {errors.map((err) => {
+          // eslint-disable-next-line react/no-danger
+          return <small dangerouslySetInnerHTML={{ __html: tWithOpt(err.message, err.args) }}></small>;
         })}
         })}
       </div>
       </div>
     );
     );
@@ -153,13 +154,11 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
     if (errors == null || errors.length === 0) return <></>;
     if (errors == null || errors.length === 0) return <></>;
     return (
     return (
       <ul className="alert alert-danger">
       <ul className="alert alert-danger">
-        {errors.map((err, index) => {
-          return (
-            <li key={index} className={index > 0 ? 'mt-1' : ''}>
-              {tWithOpt(err.message, err.args)}
-            </li>
-          );
-        })}
+        {errors.map((err, index) => (
+          <li className={index > 0 ? 'mt-1' : ''}>
+            {tWithOpt(err.message, err.args)}
+          </li>
+        ))}
       </ul>
       </ul>
     );
     );
   }, [tWithOpt]);
   }, [tWithOpt]);
@@ -400,13 +399,11 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
         {
         {
           registerErrors != null && registerErrors.length > 0 && (
           registerErrors != null && registerErrors.length > 0 && (
             <p className="alert alert-danger">
             <p className="alert alert-danger">
-              {registerErrors.map((err, index) => {
-                return (
-                  <span key={index}>
-                    {t(err.message)}<br />
-                  </span>
-                );
-              })}
+              {registerErrors.map(err => (
+                <span>
+                  {t(err.message)}<br />
+                </span>
+              ))}
             </p>
             </p>
           )
           )
         }
         }
@@ -521,6 +518,7 @@ export const LoginForm = (props: LoginFormProps): JSX.Element => {
           {/* Sign up button (submit) */}
           {/* Sign up button (submit) */}
           <div className="input-group justify-content-center my-4">
           <div className="input-group justify-content-center my-4">
             <button
             <button
+              type="button"
               className="btn btn-fill rounded-0"
               className="btn btn-fill rounded-0"
               id="register"
               id="register"
               disabled={(!isMailerSetup && isEmailAuthenticationEnabled) || isLoading}
               disabled={(!isMailerSetup && isEmailAuthenticationEnabled) || isLoading}

+ 1 - 1
apps/app/src/components/Page/RevisionRenderer.tsx

@@ -21,7 +21,7 @@ const ErrorFallback: React.FC<FallbackProps> = React.memo(({ error, resetErrorBo
     <div role="alert">
     <div role="alert">
       <p>Something went wrong:</p>
       <p>Something went wrong:</p>
       <pre>{error.message}</pre>
       <pre>{error.message}</pre>
-      <button className="btn btn-primary" onClick={resetErrorBoundary}>Reload</button>
+      <button type="button" className="btn btn-primary" onClick={resetErrorBoundary}>Reload</button>
     </div>
     </div>
   );
   );
 });
 });

+ 11 - 16
apps/app/src/components/Page/TagEditModal.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
 
 
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 import {
 import {
@@ -15,32 +15,27 @@ type Props = {
 };
 };
 
 
 function TagEditModal(props: Props): JSX.Element {
 function TagEditModal(props: Props): JSX.Element {
+  const { onClose, onTagsUpdated } = props;
+
   const [tags, setTags] = useState<string[]>([]);
   const [tags, setTags] = useState<string[]>([]);
   const { t } = useTranslation();
   const { t } = useTranslation();
 
 
-  function onTagsUpdatedByTagsInput(tags: string[]) {
-    setTags(tags);
-  }
-
   useEffect(() => {
   useEffect(() => {
     setTags(props.tags);
     setTags(props.tags);
   }, [props.tags]);
   }, [props.tags]);
 
 
-  function closeModalHandler() {
-    if (props.onClose == null) {
-      return;
-    }
-    props.onClose();
-  }
+  const closeModalHandler = useCallback(() => {
+    onClose?.();
+  }, [onClose]);
 
 
-  function handleSubmit() {
-    if (props.onTagsUpdated == null) {
+  const handleSubmit = useCallback(() => {
+    if (onTagsUpdated == null) {
       return;
       return;
     }
     }
 
 
-    props.onTagsUpdated(tags);
+    onTagsUpdated(tags);
     closeModalHandler();
     closeModalHandler();
-  }
+  }, [closeModalHandler, onTagsUpdated, tags]);
 
 
   return (
   return (
     <Modal isOpen={props.isOpen} toggle={closeModalHandler} id="edit-tag-modal" autoFocus={false}>
     <Modal isOpen={props.isOpen} toggle={closeModalHandler} id="edit-tag-modal" autoFocus={false}>
@@ -48,7 +43,7 @@ function TagEditModal(props: Props): JSX.Element {
         {t('tag_edit_modal.edit_tags')}
         {t('tag_edit_modal.edit_tags')}
       </ModalHeader>
       </ModalHeader>
       <ModalBody>
       <ModalBody>
-        <TagsInput tags={tags} onTagsUpdated={onTagsUpdatedByTagsInput} autoFocus />
+        <TagsInput tags={tags} onTagsUpdated={tags => setTags(tags)} autoFocus />
       </ModalBody>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
         <button type="button" className="btn btn-primary" onClick={handleSubmit}>
         <button type="button" className="btn btn-primary" onClick={handleSubmit}>

+ 5 - 5
apps/app/src/components/PageCreateModal.jsx

@@ -1,5 +1,5 @@
 import React, {
 import React, {
-  useEffect, useState, useMemo,
+  useEffect, useState, useMemo, useCallback,
 } from 'react';
 } from 'react';
 
 
 import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
 import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
@@ -107,7 +107,7 @@ const PageCreateModal = () => {
    * join path, check if creatable, then redirect
    * join path, check if creatable, then redirect
    * @param {string} paths
    * @param {string} paths
    */
    */
-  async function redirectToEditor(...paths) {
+  const redirectToEditor = useCallback(async(...paths) => {
     try {
     try {
       const editorPath = generateEditorPath(...paths);
       const editorPath = generateEditorPath(...paths);
       await router.push(editorPath);
       await router.push(editorPath);
@@ -119,7 +119,7 @@ const PageCreateModal = () => {
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
     }
     }
-  }
+  }, [closeCreateModal, mutateEditorMode, router]);
 
 
   /**
   /**
    * access today page
    * access today page
@@ -139,9 +139,9 @@ const PageCreateModal = () => {
     redirectToEditor(pageNameInput);
     redirectToEditor(pageNameInput);
   }
   }
 
 
-  function ppacSubmitHandler(input) {
+  const ppacSubmitHandler = useCallback((input) => {
     redirectToEditor(input);
     redirectToEditor(input);
-  }
+  }, [redirectToEditor]);
 
 
   /**
   /**
    * access template page
    * access template page

+ 2 - 6
apps/app/src/components/PageDuplicateModal.tsx

@@ -80,14 +80,10 @@ const PageDuplicateModal = (): JSX.Element => {
     }
     }
   }, [isOpened, pageNameInput, subordinatedPages, checkExistPathsDebounce, page]);
   }, [isOpened, pageNameInput, subordinatedPages, checkExistPathsDebounce, page]);
 
 
-  /**
-   * change pageNameInput for PagePathAutoComplete
-   * @param {string} value
-   */
-  function ppacInputChangeHandler(value) {
+  const ppacInputChangeHandler = useCallback((value: string) => {
     setErrs(null);
     setErrs(null);
     setPageNameInput(value);
     setPageNameInput(value);
-  }
+  }, []);
 
 
   /**
   /**
    * change pageNameInput
    * change pageNameInput

+ 2 - 2
apps/app/src/components/PageRenameModal.tsx

@@ -166,10 +166,10 @@ const PageRenameModal = (): JSX.Element => {
     }
     }
   }, [isOpened, pageNameInput, subordinatedPages, checkExistPathsDebounce, page, checkIsUsersHomepageDebounce]);
   }, [isOpened, pageNameInput, subordinatedPages, checkExistPathsDebounce, page, checkIsUsersHomepageDebounce]);
 
 
-  function ppacInputChangeHandler(value) {
+  const ppacInputChangeHandler = useCallback((value: string) => {
     setErrs(null);
     setErrs(null);
     setPageNameInput(value);
     setPageNameInput(value);
-  }
+  }, []);
 
 
   /**
   /**
    * change pageNameInput
    * change pageNameInput

+ 1 - 1
apps/app/src/components/PageStatusAlert.tsx

@@ -78,7 +78,7 @@ export const PageStatusAlert = (): JSX.Element => {
     {t('hackmd.this_page_has_draft')}
     {t('hackmd.this_page_has_draft')}
   </>,
   </>,
       btn:
       btn:
-  <button onClick={() => mutateEditorMode(EditorMode.HackMD)} className="btn btn-outline-white">
+  <button type="button" onClick={() => mutateEditorMode(EditorMode.HackMD)} className="btn btn-outline-white">
     <i className="fa fa-fw fa-file-text-o mr-1"></i>
     <i className="fa fa-fw fa-file-text-o mr-1"></i>
     Open HackMD Editor
     Open HackMD Editor
   </button>,
   </button>,

+ 1 - 0
apps/app/src/components/Sidebar/Bookmarks/BookmarkContents.tsx

@@ -41,6 +41,7 @@ export const BookmarkContents = (): JSX.Element => {
     <>
     <>
       <div className="col-8 mb-2 ">
       <div className="col-8 mb-2 ">
         <button
         <button
+          type="button"
           className="btn btn-block btn-outline-secondary rounded-pill d-flex justify-content-start align-middle"
           className="btn btn-block btn-outline-secondary rounded-pill d-flex justify-content-start align-middle"
           onClick={onClickNewBookmarkFolder}
           onClick={onClickNewBookmarkFolder}
         >
         >

+ 1 - 1
apps/app/src/components/StaffCredit/StaffCredit.tsx

@@ -13,7 +13,7 @@ import loggerFactory from '~/utils/logger';
 import styles from './StaffCredit.module.scss';
 import styles from './StaffCredit.module.scss';
 
 
 
 
-// eslint-disable-next-line no-unused-vars
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 const logger = loggerFactory('growi:cli:StaffCredit');
 const logger = loggerFactory('growi:cli:StaffCredit');
 
 
 
 

+ 1 - 1
apps/app/src/components/TableOfContents.tsx

@@ -13,7 +13,7 @@ import styles from './TableOfContents.module.scss';
 
 
 const { isUserPage: _isUserPage } = pagePathUtils;
 const { isUserPage: _isUserPage } = pagePathUtils;
 
 
-// eslint-disable-next-line no-unused-vars
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 const logger = loggerFactory('growi:TableOfContents');
 const logger = loggerFactory('growi:TableOfContents');
 
 
 const TableOfContents = (): JSX.Element => {
 const TableOfContents = (): JSX.Element => {

+ 1 - 0
apps/app/src/components/UsersHomepageFooter.tsx

@@ -31,6 +31,7 @@ export const UsersHomepageFooter = (props: UsersHomepageFooterProps): JSX.Elemen
           {t('footer.bookmarks')}
           {t('footer.bookmarks')}
           <span className="ml-auto pl-2 ">
           <span className="ml-auto pl-2 ">
             <button
             <button
+              type="button"
               className={`btn btn-sm grw-expand-compress-btn ${isExpanded ? 'active' : ''}`}
               className={`btn btn-sm grw-expand-compress-btn ${isExpanded ? 'active' : ''}`}
               onClick={() => setIsExpanded(!isExpanded)}
               onClick={() => setIsExpanded(!isExpanded)}
             >
             >

+ 3 - 5
apps/app/src/server/models/attachment.js

@@ -1,12 +1,9 @@
+import path from 'path';
+
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import { AttachmentType } from '../interfaces/attachment';
 import { AttachmentType } from '../interfaces/attachment';
 
 
-// disable no-return-await for model functions
-/* eslint-disable no-return-await */
-
-// eslint-disable-next-line no-unused-vars
-const path = require('path');
 
 
 const { addSeconds } = require('date-fns');
 const { addSeconds } = require('date-fns');
 const mongoose = require('mongoose');
 const mongoose = require('mongoose');
@@ -60,6 +57,7 @@ module.exports = function(crowi) {
 
 
 
 
   attachmentSchema.statics.createWithoutSave = function(pageId, user, fileStream, originalName, fileFormat, fileSize, attachmentType) {
   attachmentSchema.statics.createWithoutSave = function(pageId, user, fileStream, originalName, fileFormat, fileSize, attachmentType) {
+    // eslint-disable-next-line @typescript-eslint/no-this-alias
     const Attachment = this;
     const Attachment = this;
 
 
     const extname = path.extname(originalName);
     const extname = path.extname(originalName);

+ 0 - 2
apps/app/src/server/models/index.js

@@ -2,8 +2,6 @@ import Page from '~/server/models/page';
 
 
 module.exports = {
 module.exports = {
   Page,
   Page,
-  // TODO GW-2746 bulk export pages
-  // PageArchive: require('./page-archive'),
   PageTagRelation: require('./page-tag-relation'),
   PageTagRelation: require('./page-tag-relation'),
   User: require('./user'),
   User: require('./user'),
   ExternalAccount: require('./external-account'),
   ExternalAccount: require('./external-account'),

+ 1 - 0
apps/app/src/server/models/named-query.ts

@@ -10,6 +10,7 @@ import loggerFactory from '../../utils/logger';
 import { getOrCreateModel } from '../util/mongoose-utils';
 import { getOrCreateModel } from '../util/mongoose-utils';
 
 
 
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 const logger = loggerFactory('growi:models:named-query');
 const logger = loggerFactory('growi:models:named-query');
 
 
 export interface NamedQueryDocument extends INamedQuery, Document {}
 export interface NamedQueryDocument extends INamedQuery, Document {}

+ 0 - 22
apps/app/src/server/models/page-archive.js

@@ -1,22 +0,0 @@
-module.exports = function(crowi) {
-  const mongoose = require('mongoose');
-  const ObjectId = mongoose.Schema.Types.ObjectId;
-
-  const pageArchiveSchema = new mongoose.Schema({
-    owner: {
-      type: ObjectId,
-      ref: 'User',
-      index: true,
-      required: true,
-    },
-    rootPagePath: { type: String, required: true },
-    fileType: { type: String, enum: ['pdf', 'markdown'], required: true },
-    numOfPages: { type: Number, required: true },
-    hasComment: { type: Boolean, required: true },
-    hasAttachment: { type: Boolean, required: true },
-  }, {
-    timestamps: true,
-  });
-
-  return mongoose.model('PageArchive', pageArchiveSchema);
-};

+ 0 - 1
apps/app/src/server/models/page-operation.ts

@@ -17,7 +17,6 @@ const TIME_TO_ADD_SEC = 10;
 
 
 const logger = loggerFactory('growi:models:page-operation');
 const logger = loggerFactory('growi:models:page-operation');
 
 
-type IObjectId = mongoose.Types.ObjectId;
 const ObjectId = mongoose.Schema.Types.ObjectId;
 const ObjectId = mongoose.Schema.Types.ObjectId;
 
 
 /*
 /*

+ 0 - 65
apps/app/src/server/routes/admin.js

@@ -1,29 +1,19 @@
 import { SupportedAction } from '~/interfaces/activity';
 import { SupportedAction } from '~/interfaces/activity';
-import UserGroup from '~/server/models/user-group';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:routes:admin');
 const logger = loggerFactory('growi:routes:admin');
-const debug = require('debug')('growi:routes:admin');
 
 
 /* eslint-disable no-use-before-define */
 /* eslint-disable no-use-before-define */
 module.exports = function(crowi, app) {
 module.exports = function(crowi, app) {
 
 
-  const models = crowi.models;
-  const UserGroupRelation = models.UserGroupRelation;
-  const GlobalNotificationSetting = models.GlobalNotificationSetting;
-
   const {
   const {
     configManager,
     configManager,
-    aclService,
-    slackIntegrationService,
     exportService,
     exportService,
   } = crowi;
   } = crowi;
 
 
-  const recommendedWhitelist = require('~/services/xss/recommended-whitelist');
   const ApiResponse = require('../util/apiResponse');
   const ApiResponse = require('../util/apiResponse');
   const importer = require('../util/importer')(crowi);
   const importer = require('../util/importer')(crowi);
 
 
-  const MAX_PAGE_LIST = 50;
   const actions = {};
   const actions = {};
 
 
   const { check, param } = require('express-validator');
   const { check, param } = require('express-validator');
@@ -32,61 +22,6 @@ module.exports = function(crowi, app) {
 
 
   const api = {};
   const api = {};
 
 
-  function createPager(total, limit, page, pagesCount, maxPageList) {
-    const pager = {
-      page,
-      pagesCount,
-      pages: [],
-      total,
-      previous: null,
-      previousDots: false,
-      next: null,
-      nextDots: false,
-    };
-
-    if (page > 1) {
-      pager.previous = page - 1;
-    }
-
-    if (page < pagesCount) {
-      pager.next = page + 1;
-    }
-
-    let pagerMin = Math.max(1, Math.ceil(page - maxPageList / 2));
-    let pagerMax = Math.min(pagesCount, Math.floor(page + maxPageList / 2));
-    if (pagerMin === 1) {
-      if (MAX_PAGE_LIST < pagesCount) {
-        pagerMax = MAX_PAGE_LIST;
-      }
-      else {
-        pagerMax = pagesCount;
-      }
-    }
-    if (pagerMax === pagesCount) {
-      if ((pagerMax - MAX_PAGE_LIST) < 1) {
-        pagerMin = 1;
-      }
-      else {
-        pagerMin = pagerMax - MAX_PAGE_LIST;
-      }
-    }
-
-    pager.previousDots = null;
-    if (pagerMin > 1) {
-      pager.previousDots = true;
-    }
-
-    pager.nextDots = null;
-    if (pagerMax < pagesCount) {
-      pager.nextDots = true;
-    }
-
-    for (let i = pagerMin; i <= pagerMax; i++) {
-      pager.pages.push(i);
-    }
-
-    return pager;
-  }
 
 
   // Importer management
   // Importer management
   actions.importer = {};
   actions.importer = {};

+ 1 - 1
apps/app/src/server/routes/apiv3/bookmarks.js

@@ -79,7 +79,7 @@ module.exports = (crowi) => {
 
 
   const activityEvent = crowi.event('activity');
   const activityEvent = crowi.event('activity');
 
 
-  const { Page, Bookmark, User } = crowi.models;
+  const { Page, Bookmark } = crowi.models;
 
 
   const validator = {
   const validator = {
     bookmarks: [
     bookmarks: [

+ 1 - 0
apps/app/src/server/routes/apiv3/installer.ts

@@ -13,6 +13,7 @@ import { InstallerService, FailedToCreateAdminUserError } from '../../service/in
 import { ApiV3Response } from './interfaces/apiv3-response';
 import { ApiV3Response } from './interfaces/apiv3-response';
 
 
 
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 const logger = loggerFactory('growi:routes:apiv3:installer');
 const logger = loggerFactory('growi:routes:apiv3:installer');
 
 
 
 

+ 1 - 82
apps/app/src/server/routes/apiv3/page.js

@@ -168,7 +168,7 @@ module.exports = (crowi) => {
   const configManager = crowi.configManager;
   const configManager = crowi.configManager;
 
 
   const globalNotificationService = crowi.getGlobalNotificationService();
   const globalNotificationService = crowi.getGlobalNotificationService();
-  const { Page, GlobalNotificationSetting, Bookmark } = crowi.models;
+  const { Page, GlobalNotificationSetting } = crowi.models;
   const { pageService, exportService } = crowi;
   const { pageService, exportService } = crowi;
 
 
   const activityEvent = crowi.event('activity');
   const activityEvent = crowi.event('activity');
@@ -704,87 +704,6 @@ module.exports = (crowi) => {
 
 
   });
   });
 
 
-  // TODO GW-2746 bulk export pages
-  // /**
-  //  * @swagger
-  //  *
-  //  *    /page/archive:
-  //  *      post:
-  //  *        tags: [Page]
-  //  *        summary: /page/archive
-  //  *        description: create page archive
-  //  *        requestBody:
-  //  *          content:
-  //  *            application/json:
-  //  *              schema:
-  //  *                properties:
-  //  *                  rootPagePath:
-  //  *                    type: string
-  //  *                    description: path of the root page
-  //  *                  isCommentDownload:
-  //  *                    type: boolean
-  //  *                    description: whether archive data contains comments
-  //  *                  isAttachmentFileDownload:
-  //  *                    type: boolean
-  //  *                    description: whether archive data contains attachments
-  //  *                  isSubordinatedPageDownload:
-  //  *                    type: boolean
-  //  *                    description: whether archive data children pages
-  //  *                  fileType:
-  //  *                    type: string
-  //  *                    description: file type of archive data(.md, .pdf)
-  //  *                  hierarchyType:
-  //  *                    type: string
-  //  *                    description: method of select children pages archive data contains('allSubordinatedPage', 'decideHierarchy')
-  //  *                  hierarchyValue:
-  //  *                    type: number
-  //  *                    description: depth of hierarchy(use when hierarchyType is 'decideHierarchy')
-  //  *        responses:
-  //  *          200:
-  //  *            description: create page archive
-  //  *            content:
-  //  *              application/json:
-  //  *                schema:
-  //  *                  $ref: '#/components/schemas/Page'
-  //  */
-  // router.post('/archive', accessTokenParser, loginRequired, validator.archive, apiV3FormValidator, async(req, res) => {
-  //   const PageArchive = crowi.model('PageArchive');
-
-  //   const {
-  //     rootPagePath,
-  //     isCommentDownload,
-  //     isAttachmentFileDownload,
-  //     fileType,
-  //   } = req.body;
-  //   const owner = req.user._id;
-
-  //   const numOfPages = 1; // TODO 最終的にzipファイルに取り込むページ数を入れる
-
-  //   const createdPageArchive = PageArchive.create({
-  //     owner,
-  //     fileType,
-  //     rootPagePath,
-  //     numOfPages,
-  //     hasComment: isCommentDownload,
-  //     hasAttachment: isAttachmentFileDownload,
-  //   });
-
-  //   console.log(createdPageArchive);
-  //   return res.apiv3({ });
-
-  // });
-
-  // router.get('/count-children-pages', accessTokenParser, loginRequired, async(req, res) => {
-
-  //   // TO DO implement correct number at another task
-
-  //   const { pageId } = req.query;
-  //   console.log(pageId);
-
-  //   const dummy = 6;
-  //   return res.apiv3({ dummy });
-  // });
-
   /**
   /**
    * @swagger
    * @swagger
    *
    *

+ 2 - 4
apps/app/src/server/routes/apiv3/slack-integration-legacy-settings.js

@@ -1,4 +1,6 @@
 import { ErrorV3 } from '@growi/core/dist/models';
 import { ErrorV3 } from '@growi/core/dist/models';
+import express from 'express';
+import { body } from 'express-validator';
 
 
 import { SupportedAction } from '~/interfaces/activity';
 import { SupportedAction } from '~/interfaces/activity';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -10,12 +12,8 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 // eslint-disable-next-line no-unused-vars
 // eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:routes:apiv3:slack-integration-legacy-setting');
 const logger = loggerFactory('growi:routes:apiv3:slack-integration-legacy-setting');
 
 
-const express = require('express');
-
 const router = express.Router();
 const router = express.Router();
 
 
-const { body } = require('express-validator');
-
 const validator = {
 const validator = {
   slackConfiguration: [
   slackConfiguration: [
     body('webhookUrl').if(value => value != null).isString().trim(),
     body('webhookUrl').if(value => value != null).isString().trim(),

+ 1 - 2
apps/app/src/server/routes/apiv3/slack-integration-settings.js

@@ -17,8 +17,7 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 
 
 const axios = require('axios');
 const axios = require('axios');
 const express = require('express');
 const express = require('express');
-const { body, query, param } = require('express-validator');
-const mongoose = require('mongoose');
+const { body, param } = require('express-validator');
 const urljoin = require('url-join');
 const urljoin = require('url-join');
 
 
 const logger = loggerFactory('growi:routes:apiv3:slack-integration-settings');
 const logger = loggerFactory('growi:routes:apiv3:slack-integration-settings');

+ 1 - 1
apps/app/src/server/routes/apiv3/slack-integration.js

@@ -142,7 +142,7 @@ module.exports = (crowi) => {
     // for without proxy
     // for without proxy
     res.send();
     res.send();
 
 
-    const { interactionPayload, interactionPayloadAccessor } = req;
+    const { interactionPayloadAccessor } = req;
     const siteUrl = crowi.appService.getSiteUrl();
     const siteUrl = crowi.appService.getSiteUrl();
 
 
     const { actionId, callbackId } = interactionPayloadAccessor.getActionIdAndCallbackIdFromPayLoad();
     const { actionId, callbackId } = interactionPayloadAccessor.getActionIdAndCallbackIdFromPayLoad();

+ 1 - 1
apps/app/src/server/routes/apiv3/user-activation.ts

@@ -50,7 +50,7 @@ export const validateCompleteRegistration = (req, res, next) => {
 };
 };
 
 
 async function sendEmailToAllAdmins(userData, admins, appTitle, mailService, template, url) {
 async function sendEmailToAllAdmins(userData, admins, appTitle, mailService, template, url) {
-  const promises = admins.map((admin) => {
+  admins.map((admin) => {
     return mailService.send({
     return mailService.send({
       to: admin.email,
       to: admin.email,
       subject: `[${appTitle}:admin] A New User Created and Waiting for Activation`,
       subject: `[${appTitle}:admin] A New User Created and Waiting for Activation`,

+ 0 - 3
apps/app/src/server/routes/apiv3/user-group.js

@@ -17,13 +17,10 @@ const router = express.Router();
 
 
 const { body, param, query } = require('express-validator');
 const { body, param, query } = require('express-validator');
 const { sanitizeQuery } = require('express-validator');
 const { sanitizeQuery } = require('express-validator');
-const mongoose = require('mongoose');
 
 
 const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 const { serializeUserSecurely } = require('../../models/serializers/user-serializer');
 const { toPagingLimit, toPagingOffset } = require('../../util/express-validator/sanitizer');
 const { toPagingLimit, toPagingOffset } = require('../../util/express-validator/sanitizer');
 
 
-const { ObjectId } = mongoose.Types;
-
 
 
 /**
 /**
  * @swagger
  * @swagger

+ 0 - 1
apps/app/src/server/routes/index.js

@@ -43,7 +43,6 @@ module.exports = function(crowi, app) {
   const loginPassport = require('./login-passport')(crowi, app);
   const loginPassport = require('./login-passport')(crowi, app);
   const me = require('./me')(crowi, app);
   const me = require('./me')(crowi, app);
   const admin = require('./admin')(crowi, app);
   const admin = require('./admin')(crowi, app);
-  const user = require('./user')(crowi, app);
   const attachment = require('./attachment')(crowi, app);
   const attachment = require('./attachment')(crowi, app);
   const comment = require('./comment')(crowi, app);
   const comment = require('./comment')(crowi, app);
   const tag = require('./tag')(crowi, app);
   const tag = require('./tag')(crowi, app);

+ 0 - 1
apps/app/src/server/routes/me.js

@@ -51,7 +51,6 @@
 module.exports = function(crowi, app) {
 module.exports = function(crowi, app) {
   const models = crowi.models;
   const models = crowi.models;
   const UserGroupRelation = models.UserGroupRelation;
   const UserGroupRelation = models.UserGroupRelation;
-  const ExternalAccount = models.ExternalAccount;
   const ApiResponse = require('../util/apiResponse');
   const ApiResponse = require('../util/apiResponse');
 
 
   // , pluginService = require('../service/plugin')
   // , pluginService = require('../service/plugin')

+ 0 - 1
apps/app/src/server/routes/page.js

@@ -833,7 +833,6 @@ module.exports = function(crowi, app) {
     };
     };
 
 
     let page;
     let page;
-    let descendantPages;
     try {
     try {
       page = await Page.findByIdAndViewer(pageId, req.user);
       page = await Page.findByIdAndViewer(pageId, req.user);
       if (page == null) {
       if (page == null) {

+ 0 - 1
apps/app/src/server/service/customize.ts

@@ -1,4 +1,3 @@
-// eslint-disable-next-line no-unused-vars
 import { ColorScheme } from '@growi/core';
 import { ColorScheme } from '@growi/core';
 import { DevidedPagePath } from '@growi/core/dist/models';
 import { DevidedPagePath } from '@growi/core/dist/models';
 import { getForcedColorScheme } from '@growi/core/dist/utils';
 import { getForcedColorScheme } from '@growi/core/dist/utils';

+ 1 - 1
apps/app/src/server/service/search.ts

@@ -19,7 +19,7 @@ import ElasticsearchDelegator from './search-delegator/elasticsearch';
 import PrivateLegacyPagesDelegator from './search-delegator/private-legacy-pages';
 import PrivateLegacyPagesDelegator from './search-delegator/private-legacy-pages';
 
 
 
 
-// eslint-disable-next-line no-unused-vars
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 const logger = loggerFactory('growi:service:search');
 const logger = loggerFactory('growi:service:search');
 
 
 const nonNullable = <T>(value: T): value is NonNullable<T> => value != null;
 const nonNullable = <T>(value: T): value is NonNullable<T> => value != null;

+ 2 - 0
apps/app/src/server/service/slack-command-handler/create-page-service.js

@@ -3,7 +3,9 @@ import { reshapeContentsBody } from '@growi/slack/dist/utils/reshape-contents-bo
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
+// eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:service:CreatePageService');
 const logger = loggerFactory('growi:service:CreatePageService');
+
 const { pathUtils } = require('@growi/core/dist/utils');
 const { pathUtils } = require('@growi/core/dist/utils');
 const mongoose = require('mongoose');
 const mongoose = require('mongoose');
 
 

+ 0 - 2
apps/app/src/server/service/slack-command-handler/search.js

@@ -8,8 +8,6 @@ import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:service:SlackCommandHandler:search');
 const logger = loggerFactory('growi:service:SlackCommandHandler:search');
 
 
-const { formatDistanceStrict } = require('date-fns');
-
 const PAGINGLIMIT = 7;
 const PAGINGLIMIT = 7;
 
 
 
 

+ 1 - 1
apps/app/src/server/service/slack-command-handler/togetter.js

@@ -5,9 +5,9 @@ import { respond, deleteOriginal } from '@growi/slack/dist/utils/response-url';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
+// eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:service:SlackBotService:togetter');
 const logger = loggerFactory('growi:service:SlackBotService:togetter');
 
 
-const axios = require('axios');
 const { parse, format } = require('date-fns');
 const { parse, format } = require('date-fns');
 
 
 const { SlackCommandHandlerError } = require('../../models/vo/slack-command-handler-error');
 const { SlackCommandHandlerError } = require('../../models/vo/slack-command-handler-error');

+ 0 - 1
apps/app/src/server/service/user-notification/index.ts

@@ -1,4 +1,3 @@
-import UpdatePost from '~/server/models/update-post';
 import { toArrayFromCsv } from '~/utils/to-array-from-csv';
 import { toArrayFromCsv } from '~/utils/to-array-from-csv';
 
 
 
 

+ 2 - 1
apps/app/src/services/renderer/renderer.tsx

@@ -29,6 +29,7 @@ import * as xsvToTable from './remark-plugins/xsv-to-table';
 // import EasyGrid from './PreProcessor/EasyGrid';
 // import EasyGrid from './PreProcessor/EasyGrid';
 
 
 
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 const logger = loggerFactory('growi:services:renderer');
 const logger = loggerFactory('growi:services:renderer');
 
 
 
 
@@ -125,7 +126,7 @@ export const generateSSRViewOptions = (
 ): RendererOptions => {
 ): RendererOptions => {
   const options = generateCommonOptions(pagePath);
   const options = generateCommonOptions(pagePath);
 
 
-  const { remarkPlugins, rehypePlugins, components } = options;
+  const { remarkPlugins, rehypePlugins } = options;
 
 
   // add remark plugins
   // add remark plugins
   remarkPlugins.push(
   remarkPlugins.push(

+ 6 - 8
apps/app/src/stores/in-app-notification.ts

@@ -12,12 +12,11 @@ const logger = loggerFactory('growi:cli:InAppNotification');
 
 
 type inAppNotificationPaginateResult = PaginateResult<IInAppNotification>
 type inAppNotificationPaginateResult = PaginateResult<IInAppNotification>
 
 
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const useSWRxInAppNotifications = <Data, Error>(
-  limit: number,
-  offset?: number,
-  status?: InAppNotificationStatuses,
-  config?: SWRConfiguration,
+export const useSWRxInAppNotifications = (
+    limit: number,
+    offset?: number,
+    status?: InAppNotificationStatuses,
+    config?: SWRConfiguration,
 ): SWRResponse<PaginateResult<IInAppNotification>, Error> => {
 ): SWRResponse<PaginateResult<IInAppNotification>, Error> => {
   return useSWR(
   return useSWR(
     ['/in-app-notification/list', limit, offset, status],
     ['/in-app-notification/list', limit, offset, status],
@@ -46,8 +45,7 @@ export const useSWRxInAppNotifications = <Data, Error>(
   );
   );
 };
 };
 
 
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export const useSWRxInAppNotificationStatus = <Data, Error>(
+export const useSWRxInAppNotificationStatus = (
 ): SWRResponse<number, Error> => {
 ): SWRResponse<number, Error> => {
   return useSWR(
   return useSWR(
     '/in-app-notification/status',
     '/in-app-notification/status',

+ 0 - 1
apps/app/test/integration/models/page-redirect.test.js

@@ -1,6 +1,5 @@
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 
 
-import { IPageRedirect, PageRedirectModel } from '../../../src/server/models/page-redirect';
 import { getInstance } from '../setup-crowi';
 import { getInstance } from '../setup-crowi';
 
 
 describe('PageRedirect', () => {
 describe('PageRedirect', () => {