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

Merge pull request #8094 from weseek/support/omit-hackmd

support: Omit HackMD (CodiMD)
Yuki Takei 2 лет назад
Родитель
Сommit
7803c3fd5d
58 измененных файлов с 54 добавлено и 447 удалено
  1. 1 2
      README.md
  2. 1 2
      README_JP.md
  3. 1 0
      _obsolete/packages/.eslintignore
  4. 0 0
      _obsolete/packages/hackmd/.eslintignore
  5. 0 0
      _obsolete/packages/hackmd/.gitignore
  6. 0 0
      _obsolete/packages/hackmd/package.json
  7. 0 0
      _obsolete/packages/hackmd/src/hackmd-agent.js
  8. 0 0
      _obsolete/packages/hackmd/src/hackmd-styles.ts
  9. 0 0
      _obsolete/packages/hackmd/src/index.ts
  10. 0 0
      _obsolete/packages/hackmd/src/style.scss
  11. 0 0
      _obsolete/packages/hackmd/tsconfig.json
  12. 0 0
      _obsolete/packages/hackmd/vite.config.js
  13. 0 2
      apps/app/.env.development
  14. 0 0
      apps/app/_obsolete/src/client/services/side-effects/hackmd-draft-updated.ts
  15. 0 0
      apps/app/_obsolete/src/components/PageEditorByHackmd.tsx
  16. 0 0
      apps/app/_obsolete/src/components/PageEditorByHackmd/HackmdEditor.jsx
  17. 0 0
      apps/app/_obsolete/src/interfaces/hackmd.ts
  18. 0 0
      apps/app/_obsolete/src/server/routes/hackmd.js
  19. 0 0
      apps/app/_obsolete/src/stores/hackmd.ts
  20. 0 2
      apps/app/package.json
  21. 0 4
      apps/app/public/static/locales/en_US/admin.json
  22. 1 22
      apps/app/public/static/locales/en_US/translation.json
  23. 0 4
      apps/app/public/static/locales/ja_JP/admin.json
  24. 1 22
      apps/app/public/static/locales/ja_JP/translation.json
  25. 0 4
      apps/app/public/static/locales/zh_CN/admin.json
  26. 1 22
      apps/app/public/static/locales/zh_CN/translation.json
  27. 0 19
      apps/app/src/client/services/page-operation.ts
  28. 2 4
      apps/app/src/client/services/side-effects/page-updated.ts
  29. 0 13
      apps/app/src/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx
  30. 0 25
      apps/app/src/components/Navbar/PageEditorModeManager.jsx
  31. 0 12
      apps/app/src/components/Navbar/PageEditorModeManager.module.scss
  32. 0 2
      apps/app/src/components/Page/DisplaySwitcher.tsx
  33. 3 4
      apps/app/src/components/PageEditor/EditorNavbarBottom.tsx
  34. 0 1
      apps/app/src/components/PageEditor/PageEditor.tsx
  35. 19 61
      apps/app/src/components/PageStatusAlert.tsx
  36. 0 1
      apps/app/src/interfaces/page-operation.ts
  37. 0 3
      apps/app/src/interfaces/websocket.ts
  38. 0 1
      apps/app/src/models/admin/import-option-for-pages.js
  39. 2 10
      apps/app/src/pages/[[...path]].page.tsx
  40. 0 1
      apps/app/src/server/models/interfaces/page-operation.ts
  41. 0 43
      apps/app/src/server/models/obsolete-page.js
  42. 0 1
      apps/app/src/server/models/page-operation.ts
  43. 0 3
      apps/app/src/server/models/page.ts
  44. 1 2
      apps/app/src/server/models/serializers/page-serializer.js
  45. 1 3
      apps/app/src/server/models/vo/s2c-message.js
  46. 0 6
      apps/app/src/server/routes/apiv3/overwrite-params/pages.js
  47. 0 7
      apps/app/src/server/routes/index.js
  48. 1 2
      apps/app/src/server/routes/page.js
  49. 0 12
      apps/app/src/server/service/config-loader.ts
  50. 7 18
      apps/app/src/server/service/page.ts
  51. 3 15
      apps/app/src/server/service/system-events/sync-page-status.ts
  52. 0 4
      apps/app/src/stores/context.tsx
  53. 9 19
      apps/app/src/stores/remote-latest-page.ts
  54. 0 10
      apps/app/src/stores/ui.tsx
  55. 0 48
      apps/app/src/styles/_editor.scss
  56. 0 1
      apps/app/tsconfig.build.server.json
  57. 0 3
      packages/core/src/interfaces/page.ts
  58. 0 7
      yarn.lock

+ 1 - 2
README.md

@@ -38,8 +38,7 @@
 
 - **Features**
   - Create hierarchical pages with markdown -> [Try GROWI on the demo site](https://docs.growi.org/en/guide/getting-started/try_growi.html)
-  - Simultaneously edit with multiple people by [HackMD(CodiMD)](https://hackmd.io/) integration
-    - [GROWI Docs: HackMD(CodiMD) Integration](https://docs.growi.org/en/admin-guide/admin-cookbook/integrate-with-hackmd.html)
+  - Simultaneously edit with multiple people
   - Support Authentication with LDAP / Active Directory, OAuth
   - SSO(Single Sign On) with SAML
   - Slack/Mattermost, IFTTT Integration

+ 1 - 2
README_JP.md

@@ -37,8 +37,7 @@
 
 - **主な機能**
   - マークダウンを使用してページを階層構造で作成することが可能です。 -> [デモサイトで GROWI を体験する](https://docs.growi.org/ja/guide/getting-started/try_growi.html)。
-  - [HackMD(CodiMd)](https://hackmd.io/) と連携することで同時多人数編集が可能です。
-    - [GROWI Docs: HackMD(CodiMD) 連携](https://docs.growi.org/ja/admin-guide/admin-cookbook/integrate-with-hackmd.html)
+  - 同時多人数編集が可能です。
   - LDAP / Active Direcotry , OAuth 認証をサポートしています。
   - SAML を用いた Single Sign On が可能です。
   - Slack / Mattermost, IFTTT と連携することが可能です。

+ 1 - 0
_obsolete/packages/.eslintignore

@@ -0,0 +1 @@
+**/*

+ 0 - 0
packages/hackmd/.eslintignore → _obsolete/packages/hackmd/.eslintignore


+ 0 - 0
packages/hackmd/.gitignore → _obsolete/packages/hackmd/.gitignore


+ 0 - 0
packages/hackmd/package.json → _obsolete/packages/hackmd/package.json


+ 0 - 0
packages/hackmd/src/hackmd-agent.js → _obsolete/packages/hackmd/src/hackmd-agent.js


+ 0 - 0
packages/hackmd/src/hackmd-styles.ts → _obsolete/packages/hackmd/src/hackmd-styles.ts


+ 0 - 0
packages/hackmd/src/index.ts → _obsolete/packages/hackmd/src/index.ts


+ 0 - 0
packages/hackmd/src/style.scss → _obsolete/packages/hackmd/src/style.scss


+ 0 - 0
packages/hackmd/tsconfig.json → _obsolete/packages/hackmd/tsconfig.json


+ 0 - 0
packages/hackmd/vite.config.js → _obsolete/packages/hackmd/vite.config.js


+ 0 - 2
apps/app/.env.development

@@ -13,8 +13,6 @@ MONGO_URI="mongodb://mongo:27017/growi"
 ELASTICSEARCH_URI="http://elasticsearch:9200/growi"
 ELASTICSEARCH_REQUEST_TIMEOUT=15000
 ELASTICSEARCH_REJECT_UNAUTHORIZED=true
-HACKMD_URI="http://localhost:3010"
-HACKMD_URI_FOR_SERVER="http://hackmd:3000"
 OGP_URI="http://ogp:8088"
 QUESTIONNAIRE_SERVER_ORIGIN="http://host.docker.internal:3003"
 # DRAWIO_URI="http://localhost:8080/?offline=1&https=0"

+ 0 - 0
apps/app/src/client/services/side-effects/hackmd-draft-updated.ts → apps/app/_obsolete/src/client/services/side-effects/hackmd-draft-updated.ts


+ 0 - 0
apps/app/src/components/PageEditorByHackmd.tsx → apps/app/_obsolete/src/components/PageEditorByHackmd.tsx


+ 0 - 0
apps/app/src/components/PageEditorByHackmd/HackmdEditor.jsx → apps/app/_obsolete/src/components/PageEditorByHackmd/HackmdEditor.jsx


+ 0 - 0
apps/app/src/interfaces/hackmd.ts → apps/app/_obsolete/src/interfaces/hackmd.ts


+ 0 - 0
apps/app/src/server/routes/hackmd.js → apps/app/_obsolete/src/server/routes/hackmd.js


+ 0 - 0
apps/app/src/stores/hackmd.ts → apps/app/_obsolete/src/stores/hackmd.ts


+ 0 - 2
apps/app/package.json

@@ -65,7 +65,6 @@
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
     "@growi/core": "link:../../packages/core",
-    "@growi/hackmd": "link:../../packages/hackmd",
     "@growi/pluginkit": "link:../../packages/pluginkit",
     "@growi/preset-templates": "link:../../packages/preset-templates",
     "@growi/preset-themes": "link:../../packages/preset-themes",
@@ -245,7 +244,6 @@
     "mongodb-memory-server": "^8.12.2",
     "morgan": "^1.10.0",
     "null-loader": "^4.0.1",
-    "penpal": "^4.0.0",
     "plantuml-encoder": "^1.2.5",
     "prettier": "^1.19.1",
     "react-codemirror2": "^6.0.0",

+ 0 - 4
apps/app/public/static/locales/en_US/admin.json

@@ -557,10 +557,6 @@
           "initialize_meta_datas": {
             "label": "Initialize page's like, read users and comment count",
             "desc": "Recommended <span class=\"text-danger\">NOT</span> to check this when users will also be restored."
-          },
-          "initialize_hackmd_related_datas": {
-            "label": "Initialize HackMD related data",
-            "desc": "Recommended to check this unless there is important drafts on HackMD."
           }
         },
         "revisions": {

+ 1 - 22
apps/app/public/static/locales/en_US/translation.json

@@ -502,27 +502,6 @@
     "insert_image": "inserts an image",
     "open_sandbox": "Open Sandbox"
   },
-  "hackmd": {
-    "hack_md": "HackMD",
-    "not_set_up": "HackMD is not set up.",
-    "used_for_not_found": "Can not use HackMD to a page that does not exist.",
-    "start_to_edit": "Start to edit with HackMD",
-    "clone_page_content": "Click to clone page content and start to edit.",
-    "unsaved_draft": "HackMD has unsaved draft.",
-    "draft_outdated": "DRAFT MAY BE OUTDATED",
-    "based_on_revision": "The current draft on HackMD is based on",
-    "view_outdated_draft": "View the outdated draft on HackMD",
-    "resume_to_edit": "Resume to edit with HackMD",
-    "discard_changes": "Discard changes of HackMD",
-    "integration_failed": "HackMD Integration failed",
-    "fail_to_connect": "GROWI client failed to connect to GROWI agent for HackMD.",
-    "check_configuration": "Check your configuration following <a href='https://docs.growi.org/en/admin-guide/admin-cookbook/integrate-with-hackmd.html'>the manual</a>.",
-    "not_initialized": "HackmdEditor component has not initialized",
-    "someone_editing": "Someone editing this page on HackMD",
-    "this_page_has_draft": "This page has a draft on HackMD",
-    "need_to_associate_with_growi_to_use_hackmd_refer_to_this": "To use HackMD for simultaneous multi-person editing, need to associate HackMD with GROWI.Please refer to <a href='https://docs.growi.org/en/admin-guide/admin-cookbook/integrate-with-hackmd.html'>here</a>.",
-    "need_to_make_page": "To use HackMD, please make a new page from the <a href='#edit'>built-in editor.</a>"
-  },
   "slack_notification": {
     "popover_title": "Slack Notification",
     "popover_desc": "Input channel name. You can notify multiple channels by entering a comma-separated list."
@@ -737,7 +716,7 @@
         "isForbidden": "Authority not allowed to view",
         "currentPageGrantLabel": "Authorization for this page: ",
         "parentPageGrantLabel": "Authority of parent page: ",
-        "docLink": "For more information on modifying permissions, please refer to <a href='https://docs.growi.org/ja/admin-guide/admin-cookbook/integrate-with-hackmd.html'>こちらのリンク</a>"
+        "docLink": "For more information on modifying permissions, please refer to <a href='https://docs.growi.org/en/guide/features/authority.html#permissions-for-subordinate-pages'>こちらのリンク</a>"
       },
       "radio_btn": {
         "restrected": "Only those who know the link",

+ 0 - 4
apps/app/public/static/locales/ja_JP/admin.json

@@ -566,10 +566,6 @@
           "initialize_meta_datas": {
             "label": "「いいね」「閲覧したユーザー」「コメント数」を初期化する",
             "desc": "users を同時に復元しない場合、このオプションは<span class=\"text-danger\">非推奨</span>です。"
-          },
-          "initialize_hackmd_related_datas": {
-            "label": "HackMD 関連データを初期化する",
-            "desc": "HackMD に重要な下書きデータがない限りはこのオプションをチェックすることを推奨します。"
           }
         },
         "revisions": {

+ 1 - 22
apps/app/public/static/locales/ja_JP/translation.json

@@ -535,27 +535,6 @@
     "insert_image": "で画像を挿入できます",
     "open_sandbox": "Sandbox を開く"
   },
-  "hackmd":{
-    "hack_md": "HackMD",
-    "not_set_up": "HackMD はセットアップされていません",
-    "used_for_not_found": "HackMD は新しいページの作成には利用できません",
-    "start_to_edit": "HackMD を開始する",
-    "clone_page_content": "ページを複製して編集を開始します",
-    "unsaved_draft": "HackMD のドラフトが保存されていません",
-    "draft_outdated": "ドラフトは古くなっている可能性があります",
-    "based_on_revision": "現在のドラフトは次の revision に基づいています",
-    "view_outdated_draft": "HackMD で古いドラフトを表示する",
-    "resume_to_edit": "HackMD で編集を再開する",
-    "discard_changes": "HackMD の変更を破棄する",
-    "integration_failed": "HackMD の統合に失敗しました",
-    "fail_to_connect": "GROWI クライアントが HackMD の GROWI agent に接続できませんでした。",
-    "check_configuration": "<a href='https://docs.growi.org/ja/admin-guide/admin-cookbook/integrate-with-hackmd.html'>こちらのマニュアル</a>から設定を確認してください",
-    "not_initialized": "HackMD コンポーネントは初期化されていません",
-    "someone_editing": "このページは、HackMD で編集されています。",
-    "this_page_has_draft": "このページは、HackMD のドラフトがあります。",
-    "need_to_associate_with_growi_to_use_hackmd_refer_to_this": "HackMD を利用して同時多人数編集を行うには、HackMD と GROWI を連携する必要があります。<a href='https://docs.growi.org/ja/admin-guide/admin-cookbook/integrate-with-hackmd.html'>こちら</a>を参照してください。",
-    "need_to_make_page": "HackMD を利用するためには、<a href='#edit'>ビルトインエディタ</a>で新しいページを作成してください。"
-  },
   "slack_notification": {
     "popover_title": "Slack 通知",
     "popover_desc": "チャンネル名を入れてください。カンマ区切りのリストを入力することで複数のチャンネルに通知することができます。"
@@ -770,7 +749,7 @@
         "isForbidden": "権限の閲覧が許可されていません",
         "currentPageGrantLabel": "このページの権限: ",
         "parentPageGrantLabel": "親のページの権限: ",
-        "docLink": "権限の修正についての詳細は<a href='https://docs.growi.org/ja/admin-guide/admin-cookbook/integrate-with-hackmd.html'>こちらのリンク</a>を参照してください"
+        "docLink": "権限の修正についての詳細は<a href='https://docs.growi.org/ja/guide/features/authority.html#%E9%85%8D%E4%B8%8B%E3%83%98%E3%82%9A%E3%83%BC%E3%82%B7%E3%82%99%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%81%A6%E3%82%99%E3%81%8D%E3%82%8B%E6%A8%A9%E9%99%90'>こちらのリンク</a>を参照してください"
       },
       "radio_btn": {
         "restrected": "リンクを知っている人のみ",

+ 0 - 4
apps/app/public/static/locales/zh_CN/admin.json

@@ -565,10 +565,6 @@
           "initialize_meta_datas": {
             "label": "Initialize page's like, read users and comment count",
             "desc": "Recommended <span class=\"text-danger\">NOT</span> to check this when users will also be restored."
-          },
-          "initialize_hackmd_related_datas": {
-            "label": "Initialize HackMD related data",
-            "desc": "Recommended to check this unless there is important drafts on HackMD."
           }
         },
         "revisions": {

+ 1 - 22
apps/app/public/static/locales/zh_CN/translation.json

@@ -489,27 +489,6 @@
 		"insert_image": "插入图像",
 		"open_sandbox": "开放式沙箱"
 	},
-	"hackmd": {
-    "hack_md": "HackMD",
-    "not_set_up": "HackMD is not set up.",
-    "used_for_not_found": "Can not use HackMD to a page that does not exist.",
-		"start_to_edit": "Start to edit with HackMD",
-		"clone_page_content": "Click to clone page content and start to edit.",
-		"unsaved_draft": "HackMD has unsaved draft.",
-		"draft_outdated": "DRAFT MAY BE OUTDATED",
-		"based_on_revision": "The current draft on HackMD is based on",
-		"view_outdated_draft": "View the outdated draft on HackMD",
-		"resume_to_edit": "Resume to edit with HackMD",
-		"discard_changes": "Discard changes of HackMD",
-		"integration_failed": "HackMD Integration failed",
-		"fail_to_connect": "GROWI client failed to connect to GROWI agent for HackMD.",
-		"check_configuration": "Check your configuration following <a href='https://docs.growi.org/en/admin-guide/admin-cookbook/integrate-with-hackmd.html'>the manual</a>.",
-		"not_initialized": "HackmdEditor component has not initialized",
-		"someone_editing": "Someone editing this page on HackMD",
-    "this_page_has_draft": "This page has a draft on HackMD",
-    "need_to_associate_with_growi_to_use_hackmd_refer_to_this": "若要使用HackMD的多人同时编辑功能,请先关联HackMD和GROWI。详情请参考<a href='https://docs.growi.org/en/admin-guide/admin-cookbook/integrate-with-hackmd.html'>这里</a>。",
-    "need_to_make_page": "To use HackMD, please make a new page from the <a href='#edit'>built-in editor.</a>"
-  },
   "slack_notification": {
     "popover_title": "Slack Notification",
     "popover_desc": "Input channel name. You can notify multiple channels by entering a comma-separated list."
@@ -740,7 +719,7 @@
         "isForbidden": "无权查看的机构",
         "currentPageGrantLabel": "本页的权限: ",
         "parentPageGrantLabel": "父页的权限: ",
-        "docLink": "关于修改授权的更多信息,请参见此<a href='https://docs.growi.org/ja/admin-guide/admin-cookbook/integrate-with-hackmd.html'>此链接</a>"
+        "docLink": "关于修改授权的更多信息,请参见此<a href='https://docs.growi.org/en/guide/features/authority.html#permissions-for-subordinate-pages'>此链接</a>"
       },
       "radio_btn": {
         "restrected": "只有那些知道链接的人",

+ 0 - 19
apps/app/src/client/services/page-operation.ts

@@ -135,23 +135,6 @@ export const useSaveOrUpdate = (): SaveOrUpdateFunction => {
     const { path, pageId, revisionId } = pageInfo;
 
     const options: OptionsToSave = Object.assign({}, optionsToSave);
-    /*
-    * Note: variable "markdown" will be received from params
-    * please delete the following code after implemating HackMD editor function
-    */
-    // let markdown;
-    // if (editorMode === EditorMode.HackMD) {
-    // const pageEditorByHackmd = this.appContainer.getComponentInstance('PageEditorByHackmd');
-    // markdown = await pageEditorByHackmd.getMarkdown();
-    // // set option to sync
-    // options.isSyncRevisionToHackmd = true;
-    // revisionId = this.state.revisionIdHackmdSynced;
-    // }
-    // else {
-    // const pageEditor = this.appContainer.getComponentInstance('PageEditor');
-    // const pageEditor = getComponentInstance('PageEditor');
-    // markdown = pageEditor.getMarkdown();
-    // }
 
     let res;
     if (pageId == null || revisionId == null) {
@@ -209,8 +192,6 @@ export const useUpdateStateAfterSave = (pageId: string|undefined|null, opts?: Up
       remoteRevisionBody: updatedPage.revision.body,
       remoteRevisionLastUpdateUser: updatedPage.lastUpdateUser,
       remoteRevisionLastUpdatedAt: updatedPage.updatedAt,
-      revisionIdHackmdSynced: updatedPage.revisionHackmdSynced?.toString(),
-      hasDraftOnHackmd: updatedPage.hasDraftOnHackmd,
     };
 
     setRemoteLatestPageData(remoterevisionData);

+ 2 - 4
apps/app/src/client/services/side-effects/page-updated.ts

@@ -2,7 +2,7 @@ import { useCallback, useEffect } from 'react';
 
 import { SocketEventName } from '~/interfaces/websocket';
 import { useCurrentPageId } from '~/stores/page';
-import { useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
+import { useSetRemoteLatestPageData, type RemoteRevisionData } from '~/stores/remote-latest-page';
 import { useGlobalSocket } from '~/stores/websocket';
 
 export const usePageUpdatedEffect = (): void => {
@@ -15,13 +15,11 @@ export const usePageUpdatedEffect = (): void => {
   const setLatestRemotePageData = useCallback((data) => {
     const { s2cMessagePageUpdated } = data;
 
-    const remoteData = {
+    const remoteData: RemoteRevisionData = {
       remoteRevisionId: s2cMessagePageUpdated.revisionId,
       remoteRevisionBody: s2cMessagePageUpdated.revisionBody,
       remoteRevisionLastUpdateUser: s2cMessagePageUpdated.remoteLastUpdateUser,
       remoteRevisionLastUpdatedAt: s2cMessagePageUpdated.revisionUpdateAt,
-      revisionIdHackmdSynced: s2cMessagePageUpdated.revisionIdHackmdSynced,
-      hasDraftOnHackmd: s2cMessagePageUpdated.hasDraftOnHackmd,
     };
 
     if (currentPageId != null && currentPageId === s2cMessagePageUpdated.pageId) {

+ 0 - 13
apps/app/src/components/Admin/ImportData/GrowiArchive/ImportCollectionConfigurationModal.jsx

@@ -140,19 +140,6 @@ class ImportCollectionConfigurationModal extends React.Component {
             <p className="form-text text-muted mt-0" dangerouslySetInnerHTML={{ __html: t(`${translationBase}.initialize_meta_datas.desc`) }} />
           </label>
         </div>
-        <div className="form-check form-check-warning">
-          <input
-            id="cbOpt6"
-            type="checkbox"
-            className="form-check-input"
-            checked={option.initHackmdDatas || false} // add ' || false' to avoid uncontrolled input warning
-            onChange={() => this.changeHandler({ initHackmdDatas: !option.initHackmdDatas })}
-          />
-          <label htmlFor="cbOpt6" className="form-label form-check-label">
-            {t(`${translationBase}.initialize_hackmd_related_datas.label`)}
-            <p className="form-text text-muted mt-0" dangerouslySetInnerHTML={{ __html: t(`${translationBase}.initialize_hackmd_related_datas.desc`) }} />
-          </label>
-        </div>
       </>
     );
     /* eslint-enable react/no-unescaped-entities */

+ 0 - 25
apps/app/src/components/Navbar/PageEditorModeManager.jsx

@@ -2,9 +2,7 @@ import React, { useCallback } from 'react';
 
 import { useTranslation } from 'next-i18next';
 import PropTypes from 'prop-types';
-import { UncontrolledTooltip } from 'reactstrap';
 
-import { useIsAdmin, useHackmdUri } from '~/stores/context';
 import { EditorMode, useIsDeviceSmallerThanMd } from '~/stores/ui';
 
 import styles from './PageEditorModeManager.module.scss';
@@ -47,11 +45,6 @@ function PageEditorModeManager(props) {
 
   const { t } = useTranslation();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
-  const { data: hackmdUri } = useHackmdUri();
-
-  const { data: isAdmin } = useIsAdmin();
-  const isHackmdEnabled = hackmdUri != null;
-  const showHackmdBtn = isHackmdEnabled || isAdmin;
 
   const pageEditorModeButtonClickedHandler = useCallback((viewType) => {
     if (isBtnDisabled) {
@@ -90,24 +83,6 @@ function PageEditorModeManager(props) {
             label={t('Edit')}
           />
         )}
-        {(!isDeviceSmallerThanMd || editorMode === EditorMode.View) && showHackmdBtn && (
-          <>
-            <PageEditorModeButtonWrapper
-              editorMode={editorMode}
-              isBtnDisabled={isBtnDisabled || !isHackmdEnabled}
-              onClick={isHackmdEnabled ? pageEditorModeButtonClickedHandler : undefined}
-              targetMode={EditorMode.HackMD}
-              icon={<i className="fa fa-file-text-o" />}
-              label={t('hackmd.hack_md')}
-              id="grw-page-editor-mode-manager-hackmd-button"
-            />
-            { !isHackmdEnabled && (
-              <UncontrolledTooltip placement="top" target="grw-page-editor-mode-manager-hackmd-button" fade={false}>
-                {t('hackmd.not_set_up')}
-              </UncontrolledTooltip>
-            )}
-          </>
-        )}
       </div>
     </>
   );

+ 0 - 12
apps/app/src/components/Navbar/PageEditorModeManager.module.scss

@@ -20,18 +20,6 @@ $btn-line-height: 1.2rem;
         }
       }
     }
-    &.hackmd-button {
-      line-height: $btn-line-height;
-      .grw-page-editor-mode-manager-icon {
-        @include bs.media-breakpoint-down(sm) {
-          font-size: 1.2rem;
-        }
-      }
-      .grw-page-editor-mode-manager-label {
-        font-size: 12px;
-        letter-spacing: -0.6px;
-      }
-    }
   }
 }
 

+ 0 - 2
apps/app/src/components/Page/DisplaySwitcher.tsx

@@ -3,7 +3,6 @@ import React from 'react';
 import dynamic from 'next/dynamic';
 
 
-import { useHackmdDraftUpdatedEffect } from '~/client/services/side-effects/hackmd-draft-updated';
 import { useHashChangedEffect } from '~/client/services/side-effects/hash-changed';
 import { usePageUpdatedEffect } from '~/client/services/side-effects/page-updated';
 import { useIsEditable } from '~/stores/context';
@@ -28,7 +27,6 @@ export const DisplaySwitcher = (props: Props): JSX.Element => {
 
   usePageUpdatedEffect();
   useHashChangedEffect();
-  useHackmdDraftUpdatedEffect();
 
   const isViewMode = editorMode === EditorMode.View;
 

+ 3 - 4
apps/app/src/components/PageEditor/EditorNavbarBottom.tsx

@@ -9,7 +9,7 @@ import { useIsSlackConfigured } from '~/stores/context';
 import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
 import { useCurrentPagePath } from '~/stores/page';
 import {
-  EditorMode, useDrawerOpened, useEditorMode, useIsDeviceSmallerThanMd,
+  useDrawerOpened, useEditorMode, useIsDeviceSmallerThanMd,
 } from '~/stores/ui';
 
 
@@ -76,8 +76,7 @@ const EditorNavbarBottom = (): JSX.Element => {
     </div>
   );
 
-  const isOptionsSelectorEnabled = editorMode !== EditorMode.HackMD;
-  const isCollapsedOptionsSelectorEnabled = isOptionsSelectorEnabled && isDeviceSmallerThanMd;
+  const isCollapsedOptionsSelectorEnabled = isDeviceSmallerThanMd;
 
   return (
     <div className={`${isCollapsedOptionsSelectorEnabled ? 'fixed-bottom' : ''} `}>
@@ -103,7 +102,7 @@ const EditorNavbarBottom = (): JSX.Element => {
       <div className={`flex-expand-horiz align-items-center border-top px-2 px-md-3 ${additionalClasses.join(' ')}`}>
         <form>
           { isDeviceSmallerThanMd && renderDrawerButton() }
-          { isOptionsSelectorEnabled && !isDeviceSmallerThanMd && <OptionsSelector /> }
+          { !isDeviceSmallerThanMd && <OptionsSelector /> }
         </form>
         <form className="flex-nowrap ms-auto">
           {/* Responsive Design for the SlackNotification */}

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

@@ -340,7 +340,6 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
       // Not using 'mutateGrant' to inherit the grant of the parent page
       if (res.pageCreated) {
         logger.info('Page is created', res.page._id);
-        globalEmitter.emit('resetInitializedHackMdStatus');
         mutateIsLatestRevision(true);
         setCreatedPageRevisionIdWithAttachment(res.page.revision);
         await mutateCurrentPageId(res.page._id);

+ 19 - 61
apps/app/src/components/PageStatusAlert.tsx

@@ -5,13 +5,9 @@ import * as ReactDOMServer from 'react-dom/server';
 
 import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
 import { useEditingMarkdown, useIsConflict } from '~/stores/editor';
-import {
-  useHasDraftOnHackmd, useIsHackmdDraftUpdatingInRealtime, useRevisionIdHackmdSynced,
-} from '~/stores/hackmd';
 import { useConflictDiffModal } from '~/stores/modal';
 import { useSWRMUTxCurrentPage, useSWRxCurrentPage } from '~/stores/page';
 import { useRemoteRevisionId, useRemoteRevisionLastUpdateUser } from '~/stores/remote-latest-page';
-import { EditorMode, useEditorMode } from '~/stores/ui';
 
 import { Username } from './User/Username';
 
@@ -26,17 +22,13 @@ type AlertComponentContents = {
 export const PageStatusAlert = (): JSX.Element => {
 
   const { t } = useTranslation();
-  const { data: isHackmdDraftUpdatingInRealtime } = useIsHackmdDraftUpdatingInRealtime();
-  const { data: hasDraftOnHackmd } = useHasDraftOnHackmd();
   const { data: isConflict } = useIsConflict();
   const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
   const { open: openConflictDiffModal } = useConflictDiffModal();
-  const { mutate: mutateEditorMode } = useEditorMode();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
 
   // store remote latest page data
-  const { data: revisionIdHackmdSynced } = useRevisionIdHackmdSynced();
   const { data: remoteRevisionId } = useRemoteRevisionId();
   const { data: remoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
 
@@ -53,37 +45,23 @@ export const PageStatusAlert = (): JSX.Element => {
     openConflictDiffModal();
   }, [openConflictDiffModal]);
 
-  const getContentsForSomeoneEditingAlert = useCallback((): AlertComponentContents => {
-    return {
-      additionalClasses: ['bg-success', 'd-hackmd-none'],
-      label:
-  <>
-    <i className="icon-fw icon-people"></i>
-    {t('hackmd.someone_editing')}
-  </>,
-      btn:
-  <a href="#hackmd" key="btnOpenHackmdSomeoneEditing" className="btn btn-outline-white">
-    <i className="fa fa-fw fa-file-text-o me-1"></i>
-    Open HackMD Editor
-  </a>,
-    };
-  }, [t]);
-
-  const getContentsForDraftExistsAlert = useCallback((): AlertComponentContents => {
-    return {
-      additionalClasses: ['bg-success', 'd-hackmd-none'],
-      label:
-  <>
-    <i className="icon-fw icon-pencil"></i>
-    {t('hackmd.this_page_has_draft')}
-  </>,
-      btn:
-  <button type="button" onClick={() => mutateEditorMode(EditorMode.HackMD)} className="btn btn-outline-white">
-    <i className="fa fa-fw fa-file-text-o me-1"></i>
-    Open HackMD Editor
-  </button>,
-    };
-  }, [mutateEditorMode, t]);
+  // TODO: re-impl for builtin editor
+  //
+  // const getContentsForSomeoneEditingAlert = useCallback((): AlertComponentContents => {
+  //   return {
+  //     additionalClasses: ['bg-success', 'd-hackmd-none'],
+  //     label:
+  // <>
+  //   <i className="icon-fw icon-people"></i>
+  //   {t('hackmd.someone_editing')}
+  // </>,
+  //     btn:
+  // <a href="#hackmd" key="btnOpenHackmdSomeoneEditing" className="btn btn-outline-white">
+  //   <i className="fa fa-fw fa-file-text-o me-1"></i>
+  //   Open HackMD Editor
+  // </a>,
+  //   };
+  // }, [t]);
 
   const getContentsForUpdatedAlert = useCallback((): AlertComponentContents => {
 
@@ -123,37 +101,17 @@ export const PageStatusAlert = (): JSX.Element => {
 
   const alertComponentContents = useMemo(() => {
     const isRevisionOutdated = revision?._id !== remoteRevisionId;
-    const isHackmdDocumentOutdated = revisionIdHackmdSynced !== remoteRevisionId;
 
     // 'revision?._id' and 'remoteRevisionId' are can not be undefined
     if (revision?._id == null || remoteRevisionId == null) { return }
 
     // when remote revision is newer than both
-    if (isHackmdDocumentOutdated && isRevisionOutdated) {
+    if (isRevisionOutdated) {
       return getContentsForUpdatedAlert();
     }
 
-    // when someone editing with HackMD
-    if (isHackmdDraftUpdatingInRealtime) {
-      return getContentsForSomeoneEditingAlert();
-    }
-
-    // when the draft of HackMD is newest
-    if (hasDraftOnHackmd) {
-      return getContentsForDraftExistsAlert();
-    }
-
     return null;
-  }, [
-    revision?._id,
-    remoteRevisionId,
-    revisionIdHackmdSynced,
-    isHackmdDraftUpdatingInRealtime,
-    hasDraftOnHackmd,
-    getContentsForUpdatedAlert,
-    getContentsForSomeoneEditingAlert,
-    getContentsForDraftExistsAlert,
-  ]);
+  }, [revision?._id, remoteRevisionId, getContentsForUpdatedAlert]);
 
   if (!!isGuestUser || !!isReadOnlyUser || alertComponentContents == null) { return <></> }
 

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

@@ -34,5 +34,4 @@ export type OptionsToSave = {
   pageTags: string[] | null;
   grantUserGroupId?: string | null;
   grantUserGroupName?: string | null;
-  isSyncRevisionToHackmd?: boolean;
 };

+ 0 - 3
apps/app/src/interfaces/websocket.ts

@@ -22,9 +22,6 @@ export const SocketEventName = {
   PageUpdated: 'page:update',
   PageDeleted: 'page:delete',
 
-  // Hackmd
-  EditingWithHackmd: 'page:editingWithHackmd',
-
 } as const;
 export type SocketEventName = typeof SocketEventName[keyof typeof SocketEventName];
 

+ 0 - 1
apps/app/src/models/admin/import-option-for-pages.js

@@ -6,7 +6,6 @@ const DEFAULT_PROPS = {
   makePublicForGrant4: false,
   makePublicForGrant5: false,
   initPageMetadatas: false,
-  initHackmdDatas: false,
 };
 
 class ImportOptionForPages extends GrowiArchiveImportOption {

+ 2 - 10
apps/app/src/pages/[[...path]].page.tsx

@@ -33,14 +33,13 @@ import {
   useIsForbidden, useIsSharedUser,
   useIsEnabledStaleNotification, useIsIdenticalPath,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
-  useHackmdUri, useDefaultIndentSize, useIsIndentSizeForced,
+  useDefaultIndentSize, useIsIndentSizeForced,
   useIsAclEnabled, useIsSearchPage, useIsEnabledAttachTitleHeader,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useIsEnabledMarp, useCurrentPathname,
   useIsSlackConfigured, useRendererConfig, useGrowiCloudUri,
   useEditorConfig, useIsAllReplyShown, useIsUploadableFile, useIsUploadableImage, useIsContainerFluid, useIsNotCreatable,
 } from '~/stores/context';
 import { useEditingMarkdown } from '~/stores/editor';
-import { useHasDraftOnHackmd, usePageIdOnHackmd, useRevisionIdHackmdSynced } from '~/stores/hackmd';
 import {
   useSWRxCurrentPage, useSWRMUTxCurrentPage, useSWRxIsGrantNormalized, useCurrentPageId,
   useIsNotFound, useIsLatestRevision, useTemplateTagData, useTemplateBodyData,
@@ -154,7 +153,6 @@ type Props = CommonProps & {
   isAclEnabled: boolean,
   // hasSlackConfig: boolean,
   drawioUri: string | null,
-  hackmdUri: string,
   noCdn: string,
   // highlightJsStyle: string,
   isAllReplyShown: boolean,
@@ -210,7 +208,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   // useIsMailerSetup(props.isMailerSetup);
   useIsAclEnabled(props.isAclEnabled);
   // useHasSlackConfig(props.hasSlackConfig);
-  useHackmdUri(props.hackmdUri);
   // useNoCdn(props.noCdn);
   useDefaultIndentSize(props.adminPreferredIndentSize);
   useIsIndentSizeForced(props.isIndentSizeForced);
@@ -230,8 +227,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   const pagePath = pageWithMeta?.data.path ?? props.currentPathname;
   const revisionBody = pageWithMeta?.data.revision?.body;
 
-  usePageIdOnHackmd(pageWithMeta?.data.pageIdOnHackmd);
-  useHasDraftOnHackmd(pageWithMeta?.data.hasDraftOnHackmd ?? false);
   useCurrentPathname(props.currentPathname);
 
   useSWRxCurrentPage(pageWithMeta?.data ?? null); // store initial data
@@ -248,7 +243,6 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   const { mutate: mutateSelectedGrant } = useSelectedGrant();
 
   const { mutate: mutateRemoteRevisionId } = useRemoteRevisionId();
-  const { mutate: mutateRevisionIdHackmdSynced } = useRevisionIdHackmdSynced();
 
   const { mutate: mutateTemplateTagData } = useTemplateTagData();
   const { mutate: mutateTemplateBodyData } = useTemplateBodyData();
@@ -301,8 +295,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
 
   useEffect(() => {
     mutateRemoteRevisionId(pageWithMeta?.data.revision?._id);
-    mutateRevisionIdHackmdSynced(pageWithMeta?.data.revisionHackmdSynced);
-  }, [mutateRemoteRevisionId, mutateRevisionIdHackmdSynced, pageWithMeta?.data.revision?._id, pageWithMeta?.data.revisionHackmdSynced]);
+  }, [mutateRemoteRevisionId, pageWithMeta?.data.revision?._id]);
 
   useEffect(() => {
     mutateCurrentPageId(pageId ?? null);
@@ -562,7 +555,6 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
   props.isAclEnabled = aclService.isAclEnabled();
   // props.hasSlackConfig = slackNotificationService.hasSlackConfig();
   props.drawioUri = configManager.getConfig('crowi', 'app:drawioUri');
-  props.hackmdUri = configManager.getConfig('crowi', 'app:hackmdUri');
   props.noCdn = configManager.getConfig('crowi', 'app:noCdn');
   // props.highlightJsStyle = configManager.getConfig('crowi', 'customize:highlightJsStyle');
   props.isAllReplyShown = configManager.getConfig('crowi', 'customize:isAllReplyShown');

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

@@ -24,7 +24,6 @@ export type IUserForResuming = {
 export type IOptionsForUpdate = {
   grant?: PageGrant,
   grantUserGroupId?: ObjectIdLike,
-  isSyncRevisionToHackmd?: boolean,
   overwriteScopesOfDescendants?: boolean,
 };
 

+ 0 - 43
apps/app/src/server/models/obsolete-page.js

@@ -701,49 +701,6 @@ export const getPageSchema = (crowi) => {
     await this.updateMany({ _id: { $in: pages.map(p => p._id) } }, { grantedGroup: transferToUserGroupId });
   };
 
-  /**
-   * associate GROWI page and HackMD page
-   * @param {Page} pageData
-   * @param {string} pageIdOnHackmd
-   */
-  pageSchema.statics.registerHackmdPage = function(pageData, pageIdOnHackmd) {
-    pageData.pageIdOnHackmd = pageIdOnHackmd;
-    return this.syncRevisionToHackmd(pageData);
-  };
-
-  /**
-   * update revisionHackmdSynced
-   * @param {Page} pageData
-   * @param {bool} isSave whether save or not
-   */
-  pageSchema.statics.syncRevisionToHackmd = function(pageData, isSave = true) {
-    pageData.revisionHackmdSynced = pageData.revision;
-    pageData.hasDraftOnHackmd = false;
-
-    let returnData = pageData;
-    if (isSave) {
-      returnData = pageData.save();
-    }
-    return returnData;
-  };
-
-  /**
-   * update hasDraftOnHackmd
-   * !! This will be invoked many time from many people !!
-   *
-   * @param {Page} pageData
-   * @param {Boolean} newValue
-   */
-  pageSchema.statics.updateHasDraftOnHackmd = async function(pageData, newValue) {
-    if (pageData.hasDraftOnHackmd === newValue) {
-      // do nothing when hasDraftOnHackmd equals to newValue
-      return;
-    }
-
-    pageData.hasDraftOnHackmd = newValue;
-    return pageData.save();
-  };
-
   pageSchema.methods.getNotificationTargetUsers = async function() {
     const Comment = mongoose.model('Comment');
     const Revision = mongoose.model('Revision');

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

@@ -74,7 +74,6 @@ const optionsSchemaForResuming = new Schema<IOptionsForResuming>({
   grant: { type: Number },
   grantUserGroupId: { type: ObjectId, ref: 'UserGroup' },
   format: { type: String },
-  isSyncRevisionToHackmd: { type: Boolean },
   overwriteScopesOfDescendants: { type: Boolean },
 }, { _id: false });
 

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

@@ -101,9 +101,6 @@ const schema = new Schema<PageDocument, PageModel>({
   liker: [{ type: ObjectId, ref: 'User' }],
   seenUsers: [{ type: ObjectId, ref: 'User' }],
   commentCount: { type: Number, default: 0 },
-  pageIdOnHackmd: { type: String },
-  revisionHackmdSynced: { type: ObjectId, ref: 'Revision' }, // the revision that is synced to HackMD
-  hasDraftOnHackmd: { type: Boolean }, // set true if revision and revisionHackmdSynced are same but HackMD document has modified
   expandContentWidth: { type: Boolean },
   updatedAt: { type: Date, default: Date.now }, // Do not use timetamps for updatedAt because it breaks 'updateMetadata: false' option
   deleteUser: { type: ObjectId, ref: 'User' },

+ 1 - 2
apps/app/src/server/models/serializers/page-serializer.js

@@ -28,9 +28,8 @@ function serializePageSecurely(page) {
     serialized = page.toObject();
   }
 
-  // depopulate revision and revisionHackmdSynced
+  // depopulate revision
   depopulate(serialized, 'revision');
-  depopulate(serialized, 'revisionHackmdSynced');
 
   serializeInsecureUserAttributes(serialized);
 

+ 1 - 3
apps/app/src/server/models/vo/s2c-message.js

@@ -10,15 +10,13 @@ class S2cMessagePageUpdated {
     const serializedPage = serializePageSecurely(page);
 
     const {
-      _id, revision, updatedAt, revisionHackmdSynced, hasDraftOnHackmd,
+      _id, revision, updatedAt,
     } = serializedPage;
 
     this.pageId = _id;
     this.revisionId = revision;
     this.revisionBody = page.revision.body;
     this.revisionUpdateAt = updatedAt;
-    this.revisionIdHackmdSynced = revisionHackmdSynced;
-    this.hasDraftOnHackmd = hasDraftOnHackmd;
 
     if (user != null) {
       this.remoteLastUpdateUser = user;

+ 0 - 6
apps/app/src/server/routes/apiv3/overwrite-params/pages.js

@@ -54,12 +54,6 @@ class PageOverwriteParamsFactory {
       params.extended = {};
     }
 
-    if (option.initHackmdDatas) {
-      params.pageIdOnHackmd = undefined;
-      params.revisionHackmdSynced = undefined;
-      params.hasDraftOnHackmd = undefined;
-    }
-
     return params;
   }
 

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

@@ -47,7 +47,6 @@ module.exports = function(crowi, app) {
   const comment = require('./comment')(crowi, app);
   const tag = require('./tag')(crowi, app);
   const search = require('./search')(crowi, app);
-  const hackmd = require('./hackmd')(crowi, app);
   const ogp = require('./ogp')(crowi);
 
   const next = nextFactory(crowi);
@@ -163,12 +162,6 @@ module.exports = function(crowi, app) {
 
   app.get('/_search'                            , loginRequired, next.delegateToNext);
 
-  app.get('/_hackmd/load-agent'          , hackmd.loadAgent);
-  app.get('/_hackmd/load-styles'         , hackmd.loadStyles);
-  app.post('/_api/hackmd.integrate'      , accessTokenParser , loginRequiredStrictly , excludeReadOnlyUser, hackmd.validateForApi, hackmd.integrate);
-  app.post('/_api/hackmd.discard'        , accessTokenParser , loginRequiredStrictly , excludeReadOnlyUser, hackmd.validateForApi, hackmd.discard);
-  app.post('/_api/hackmd.saveOnHackmd'   , accessTokenParser , loginRequiredStrictly , excludeReadOnlyUser, hackmd.validateForApi, hackmd.saveOnHackmd);
-
   app.use('/forgot-password', express.Router()
     .use(forgotPassword.checkForgotPasswordEnabledMiddlewareFactory(crowi))
     .get('/', forgotPassword.renderForgotPassword(crowi))

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

@@ -455,7 +455,6 @@ module.exports = function(crowi, app) {
     const overwriteScopesOfDescendants = req.body.overwriteScopesOfDescendants || null;
     const isSlackEnabled = !!req.body.isSlackEnabled; // cast to boolean
     const slackChannels = req.body.slackChannels || null;
-    const isSyncRevisionToHackmd = !!req.body.isSyncRevisionToHackmd; // cast to boolean
     const pageTags = req.body.pageTags || undefined;
 
     if (pageId === null || pageBody === null || revisionId === null) {
@@ -482,7 +481,7 @@ module.exports = function(crowi, app) {
       return res.json(ApiResponse.error('Posted param "revisionId" is outdated.', 'conflict', returnLatestRevision));
     }
 
-    const options = { isSyncRevisionToHackmd, overwriteScopesOfDescendants };
+    const options = { overwriteScopesOfDescendants };
     if (grant != null) {
       options.grant = grant;
       options.grantUserGroupId = grantUserGroupId;

+ 0 - 12
apps/app/src/server/service/config-loader.ts

@@ -61,18 +61,6 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     type:    ValueType.BOOLEAN,
     default: false,
   },
-  HACKMD_URI: {
-    ns:      'crowi',
-    key:     'app:hackmdUri',
-    type:    ValueType.STRING,
-    default: null,
-  },
-  HACKMD_URI_FOR_SERVER: {
-    ns:      'crowi',
-    key:     'app:hackmdUriForServer',
-    type:    ValueType.STRING,
-    default: null,
-  },
   // OAUTH_GOOGLE_CLIENT_ID: {
   //   ns:      'crowi',
   //   key:     'security:passport-google:clientId',

+ 7 - 18
apps/app/src/server/service/page.ts

@@ -20,11 +20,11 @@ import {
   PageDeleteConfigValue, IPageDeleteConfigValueToProcessValidation,
 } from '~/interfaces/page-delete-config';
 import {
-  IPageOperationProcessInfo, IPageOperationProcessData, PageActionStage, PageActionType,
+  type IPageOperationProcessInfo, type IPageOperationProcessData, PageActionStage, PageActionType,
 } from '~/interfaces/page-operation';
-import { PageMigrationErrorData, SocketEventName, UpdateDescCountRawData } from '~/interfaces/websocket';
+import { SocketEventName, type PageMigrationErrorData, type UpdateDescCountRawData } from '~/interfaces/websocket';
 import {
-  CreateMethod, PageCreateOptions, PageModel, PageDocument, pushRevision, PageQueryBuilder,
+  type CreateMethod, type PageCreateOptions, type PageModel, type PageDocument, pushRevision, PageQueryBuilder,
 } from '~/server/models/page';
 import { createBatchStream } from '~/server/util/batch-stream';
 import loggerFactory from '~/utils/logger';
@@ -32,9 +32,9 @@ import { prepareDeleteConfigValuesForCalc } from '~/utils/page-delete-config';
 
 import { ObjectIdLike } from '../interfaces/mongoose-utils';
 import { PathAlreadyExistsError } from '../models/errors';
-import { IOptionsForCreate, IOptionsForUpdate } from '../models/interfaces/page-operation';
-import PageOperation, { PageOperationDocument } from '../models/page-operation';
-import { PageRedirectModel } from '../models/page-redirect';
+import type { IOptionsForCreate, IOptionsForUpdate } from '../models/interfaces/page-operation';
+import PageOperation, { type PageOperationDocument } from '../models/page-operation';
+import type { PageRedirectModel } from '../models/page-redirect';
 import { serializePageSecurely } from '../models/serializers/page-serializer';
 import Subscription from '../models/subscription';
 import { V5ConversionError } from '../models/vo/v5-conversion-error';
@@ -3882,10 +3882,9 @@ class PageService {
   async updateGrant(page, user, grantData: {grant: PageGrant, grantedGroup: ObjectIdLike}): Promise<PageDocument> {
     const { grant, grantedGroup } = grantData;
 
-    const options = {
+    const options: IOptionsForUpdate = {
       grant,
       grantUserGroupId: grantedGroup,
-      isSyncRevisionToHackmd: false,
     };
 
     return this.updatePage(page, null, null, user, options);
@@ -4011,17 +4010,12 @@ class PageService {
     let savedPage = await newPageData.save();
 
     // Update body
-    const isSyncRevisionToHackmd = options.isSyncRevisionToHackmd;
     const isBodyPresent = body != null && previousBody != null;
     const shouldUpdateBody = isBodyPresent;
     if (shouldUpdateBody) {
       const newRevision = await Revision.prepareRevision(newPageData, body, previousBody, user);
       savedPage = await pushRevision(savedPage, newRevision, user);
       await savedPage.populateDataToShowRevision();
-
-      if (isSyncRevisionToHackmd) {
-        savedPage = await Page.syncRevisionToHackmd(savedPage);
-      }
     }
 
 
@@ -4074,7 +4068,6 @@ class PageService {
 
     const grant = options.grant || pageData.grant; // use the previous data if absence
     const grantUserGroupId = options.grantUserGroupId || pageData.grantUserGroupId; // use the previous data if absence
-    const isSyncRevisionToHackmd = options.isSyncRevisionToHackmd;
 
     await this.validateAppliedScope(user, grant, grantUserGroupId);
     pageData.applyScope(user, grant, grantUserGroupId);
@@ -4089,10 +4082,6 @@ class PageService {
       const newRevision = await Revision.prepareRevision(pageData, body, previousBody, user);
       savedPage = await pushRevision(savedPage, newRevision, user);
       await savedPage.populateDataToShowRevision();
-
-      if (isSyncRevisionToHackmd) {
-        savedPage = await Page.syncRevisionToHackmd(savedPage);
-      }
     }
 
     // update scopes for descendants

+ 3 - 15
apps/app/src/server/service/system-events/sync-page-status.ts

@@ -1,11 +1,10 @@
 import loggerFactory from '~/utils/logger';
 
-import S2sMessage from '../../models/vo/s2s-message';
 import { S2cMessagePageUpdated } from '../../models/vo/s2c-message';
-import { S2sMessageHandlable } from '../s2s-messaging/handlable';
-import { S2sMessagingService } from '../s2s-messaging/base';
-
+import S2sMessage from '../../models/vo/s2s-message';
 import { RoomPrefix, getRoomNameWithId } from '../../util/socket-io-helpers';
+import { S2sMessagingService } from '../s2s-messaging/base';
+import { S2sMessageHandlable } from '../s2s-messaging/handlable';
 
 const logger = loggerFactory('growi:service:system-events:SyncPageStatusService');
 
@@ -125,17 +124,6 @@ class SyncPageStatusService implements S2sMessageHandlable {
 
       this.publishToOtherServers('page:delete', { s2cMessagePageUpdated });
     });
-    this.emitter.on('saveOnHackmd', (page, user) => {
-      const s2cMessagePageUpdated = new S2cMessagePageUpdated(page);
-
-      // emit to the room for each page
-      socketIoService.getDefaultSocket()
-        .in(getRoomNameWithId(RoomPrefix.PAGE, page._id))
-        .except(getRoomNameWithId(RoomPrefix.USER, user._id))
-        .emit('page:editingWithHackmd', { s2cMessagePageUpdated });
-
-      this.publishToOtherServers('page:editingWithHackmd', { s2cMessagePageUpdated });
-    });
   }
 
 }

+ 0 - 4
apps/app/src/stores/context.tsx

@@ -72,10 +72,6 @@ export const useRegistrationWhitelist = (initialData?: Nullable<string[]>): SWRR
   return useContextSWR<Nullable<string[]>, Error>('registrationWhitelist', initialData);
 };
 
-export const useHackmdUri = (initialData?: Nullable<string>): SWRResponse<Nullable<string>, Error> => {
-  return useContextSWR<Nullable<string>, Error>('hackmdUri', initialData);
-};
-
 export const useIsSearchPage = (initialData?: Nullable<boolean>) : SWRResponse<Nullable<boolean>, Error> => {
   return useContextSWR<Nullable<any>, Error>('isSearchPage', initialData);
 };

+ 9 - 19
apps/app/src/stores/remote-latest-page.ts

@@ -1,36 +1,31 @@
 import { useMemo, useCallback } from 'react';
 
 import type { IUser } from '@growi/core';
-import { SWRResponse } from 'swr';
-
-
-import { useRevisionIdHackmdSynced, useHasDraftOnHackmd } from './hackmd';
-import { useStaticSWR } from './use-static-swr';
+import { useSWRStatic } from '@growi/core/dist/swr';
+import type { SWRResponse } from 'swr';
 
 
 export const useRemoteRevisionId = (initialData?: string): SWRResponse<string, Error> => {
-  return useStaticSWR<string, Error>('remoteRevisionId', initialData);
+  return useSWRStatic<string, Error>('remoteRevisionId', initialData);
 };
 
 export const useRemoteRevisionBody = (initialData?: string): SWRResponse<string, Error> => {
-  return useStaticSWR<string, Error>('remoteRevisionBody', initialData);
+  return useSWRStatic<string, Error>('remoteRevisionBody', initialData);
 };
 
 export const useRemoteRevisionLastUpdateUser = (initialData?: IUser): SWRResponse<IUser, Error> => {
-  return useStaticSWR<IUser, Error>('remoteRevisionLastUpdateUser', initialData);
+  return useSWRStatic<IUser, Error>('remoteRevisionLastUpdateUser', initialData);
 };
 
 export const useRemoteRevisionLastUpdatedAt = (initialData?: Date): SWRResponse<Date, Error> => {
-  return useStaticSWR<Date, Error>('remoteRevisionLastUpdatedAt', initialData);
+  return useSWRStatic<Date, Error>('remoteRevisionLastUpdatedAt', initialData);
 };
 
-type RemoteRevisionData = {
+export type RemoteRevisionData = {
   remoteRevisionId: string,
   remoteRevisionBody: string,
   remoteRevisionLastUpdateUser: IUser,
   remoteRevisionLastUpdatedAt: Date,
-  revisionIdHackmdSynced: string,
-  hasDraftOnHackmd: boolean,
 }
 
 
@@ -40,21 +35,16 @@ export const useSetRemoteLatestPageData = (): { setRemoteLatestPageData: (pageDa
   const { mutate: mutateRemoteRevisionBody } = useRemoteRevisionBody();
   const { mutate: mutateRemoteRevisionLastUpdateUser } = useRemoteRevisionLastUpdateUser();
   const { mutate: mutateRemoteRevisionLastUpdatedAt } = useRemoteRevisionLastUpdatedAt();
-  const { mutate: mutateRevisionIdHackmdSynced } = useRevisionIdHackmdSynced();
-  const { mutate: mutateHasDraftOnHackmd } = useHasDraftOnHackmd();
 
   const setRemoteLatestPageData = useCallback((remoteRevisionData: RemoteRevisionData) => {
     const {
-      remoteRevisionId, remoteRevisionBody, remoteRevisionLastUpdateUser, remoteRevisionLastUpdatedAt, revisionIdHackmdSynced, hasDraftOnHackmd,
+      remoteRevisionId, remoteRevisionBody, remoteRevisionLastUpdateUser, remoteRevisionLastUpdatedAt,
     } = remoteRevisionData;
     mutateRemoteRevisionId(remoteRevisionId);
     mutateRemoteRevisionBody(remoteRevisionBody);
     mutateRemoteRevisionLastUpdateUser(remoteRevisionLastUpdateUser);
     mutateRemoteRevisionLastUpdatedAt(remoteRevisionLastUpdatedAt);
-    mutateRevisionIdHackmdSynced(revisionIdHackmdSynced);
-    mutateHasDraftOnHackmd(hasDraftOnHackmd);
-  // eslint-disable-next-line max-len
-  }, [mutateHasDraftOnHackmd, mutateRemoteRevisionBody, mutateRemoteRevisionId, mutateRemoteRevisionLastUpdateUser, mutateRemoteRevisionLastUpdatedAt, mutateRevisionIdHackmdSynced]);
+  }, [mutateRemoteRevisionBody, mutateRemoteRevisionId, mutateRemoteRevisionLastUpdateUser, mutateRemoteRevisionLastUpdatedAt]);
 
   return useMemo(() => {
     return {

+ 0 - 10
apps/app/src/stores/ui.tsx

@@ -42,7 +42,6 @@ const logger = loggerFactory('growi:stores:ui');
 export const EditorMode = {
   View: 'view',
   Editor: 'editor',
-  HackMD: 'hackmd',
 } as const;
 export type EditorMode = typeof EditorMode[keyof typeof EditorMode];
 
@@ -86,9 +85,6 @@ const getClassNamesByEditorMode = (editorMode: EditorMode | undefined): string[]
     case EditorMode.Editor:
       classNames.push('editing', 'builtin-editor');
       break;
-    case EditorMode.HackMD:
-      classNames.push('editing', 'hackmd');
-      break;
   }
 
   return classNames;
@@ -97,7 +93,6 @@ const getClassNamesByEditorMode = (editorMode: EditorMode | undefined): string[]
 export const EditorModeHash = {
   View: '',
   Edit: '#edit',
-  HackMD: '#hackmd',
 } as const;
 export type EditorModeHash = typeof EditorModeHash[keyof typeof EditorModeHash];
 
@@ -113,9 +108,6 @@ const updateHashByEditorMode = (newEditorMode: EditorMode) => {
     case EditorMode.Editor:
       window.history.replaceState(null, '', `${pathname}${search}${EditorModeHash.Edit}`);
       break;
-    case EditorMode.HackMD:
-      window.history.replaceState(null, '', `${pathname}${search}${EditorModeHash.HackMD}`);
-      break;
   }
 };
 
@@ -129,8 +121,6 @@ export const determineEditorModeByHash = (): EditorMode => {
   switch (hash) {
     case EditorModeHash.Edit:
       return EditorMode.Editor;
-    case EditorModeHash.HackMD:
-      return EditorMode.HackMD;
     default:
       return EditorMode.View;
   }

+ 0 - 48
apps/app/src/styles/_editor.scss

@@ -40,24 +40,6 @@
     display: none !important;
   }
 
-  // hide when HackMD view
-  &.hackmd .d-hackmd-none {
-    display: none;
-  }
-
-  // show only either Edit button or HackMD button
-  &.hackmd .nav-tab-edit {
-    display: none;
-  }
-
-  &.hackmd .grw-nav-item-edit {
-    display: none;
-  }
-
-  &:not(.hackmd) .nav-tab-hackmd {
-    display: none;
-  }
-
 
   /*****************
    * Expand Editor
@@ -224,36 +206,6 @@
 
   // .builtin-editor .tab-pane#edit
 
-  &.hackmd {
-    .hackmd-preinit,
-    #iframe-hackmd-container > iframe {
-      border: none;
-    }
-
-    .hackmd-error {
-      top: 0;
-      background-color: rgba(bs.$gray-800, 0.8);
-    }
-
-    .hackmd-status-label {
-      font-size: 3em;
-    }
-
-    .hackmd-resume-button-container,
-    .hackmd-discard-button-container {
-      .btn-text {
-        display: inline-block;
-        min-width: 230px;
-      }
-    }
-
-    .btn-view-outdated-draft {
-      text-decoration: underline;
-      vertical-align: unset;
-    }
-  }
-
-
   /*****************
    *     Toastr
    *****************/

+ 0 - 1
apps/app/tsconfig.build.server.json

@@ -24,6 +24,5 @@
     "src/linter-checker",
     "src/stores",
     "src/styles",
-    "src/styles-hackmd"
   ]
 }

+ 0 - 3
packages/core/src/interfaces/page.ts

@@ -25,9 +25,6 @@ export type IPage = {
   liker: Ref<IUser>[],
   commentCount: number
   slackChannels: string,
-  pageIdOnHackmd: string,
-  revisionHackmdSynced: Ref<IRevision>,
-  hasDraftOnHackmd: boolean,
   deleteUser: Ref<IUser>,
   deletedAt: Date,
   latestRevision?: Ref<IRevision>,

+ 0 - 7
yarn.lock

@@ -2921,9 +2921,6 @@
     react "^18.2.0"
     react-dom "^18.2.0"
 
-"@growi/hackmd@link:packages/hackmd":
-  version "7.0.0-RC.0"
-
 "@growi/pluginkit@link:packages/pluginkit":
   version "0.1.0"
   dependencies:
@@ -13839,10 +13836,6 @@ pend@~1.2.0:
   resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
   integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
 
-penpal@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/penpal/-/penpal-4.0.0.tgz#1cba7a64600c1e601f91dac393c21843c977bdec"
-
 performance-now@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"