Преглед изворни кода

Merge branch 'main' into support/fix-lint-error

jam411 пре 3 година
родитељ
комит
36e00f941f

+ 7 - 48
packages/app/src/components/TemplateModal.tsx

@@ -1,5 +1,6 @@
 import React, { useCallback, useState } from 'react';
 
+import { ITemplate } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import {
   Modal,
@@ -9,58 +10,11 @@ import {
 } from 'reactstrap';
 
 import { usePreviewOptions } from '~/stores/renderer';
+import { useTemplates } from '~/stores/template';
 
 import Preview from './PageEditor/Preview';
 
 
-type ITemplate = {
-  id: string,
-  name: string,
-  markdown: string,
-}
-
-const templates: ITemplate[] = [
-  {
-    id: '__preset1__',
-    name: 'WESEEK Inner Wiki Style',
-    markdown: `# 関連ページ
-
-$lsx()
-
-# `,
-  },
-  {
-    id: '__preset2__',
-    name: 'Qiita Style',
-    markdown: `# <会議体名>
-## 日時
-yyyy/mm/dd hh:mm〜hh:mm
-
-## 場所
-
-## 出席者
--
-
-## 議題
-1. [議題1](#link)
-2.
-3.
-
-## 議事内容
-### <a name="link"></a>議題1
-
-## 決定事項
-- 決定事項1
-
-## アクション事項
-- [ ] アクション
-
-## 次回
-yyyy/mm/dd (予定、時間は追って連絡)`,
-  },
-];
-
-
 type TemplateRadioButtonProps = {
   template: ITemplate,
   onChange: (selectedTemplate: ITemplate) => void,
@@ -99,6 +53,7 @@ export const TemplateModal = (props: Props): JSX.Element => {
   const { isOpen, onClose, onSubmit } = props;
 
   const { data: rendererOptions } = usePreviewOptions();
+  const { data: templates } = useTemplates();
 
   const [selectedTemplate, setSelectedTemplate] = useState<ITemplate>();
 
@@ -112,6 +67,10 @@ export const TemplateModal = (props: Props): JSX.Element => {
     onClose();
   }, [onClose, onSubmit]);
 
+  if (templates == null) {
+    return <></>;
+  }
+
   return (
     <Modal className="link-edit-modal" isOpen={isOpen} toggle={onClose} size="lg" autoFocus={false}>
       <ModalHeader tag="h4" toggle={onClose} className="bg-primary text-light">

+ 4 - 33
packages/app/src/pages/_document.page.tsx

@@ -1,5 +1,6 @@
 import React from 'react';
 
+import mongoose from 'mongoose';
 import Document, {
   DocumentContext, DocumentInitialProps,
   Html, Head, Main, NextScript,
@@ -8,36 +9,6 @@ import Document, {
 import { ActivatePluginService, GrowiPluginManifestEntries } from '~/client/services/activate-plugin';
 import { GrowiPlugin, GrowiPluginResourceType } from '~/interfaces/plugin';
 
-
-// FIXME: dummy data
-// ------------------
-const growiPluginsExample: GrowiPlugin[] = [
-  {
-    isEnabled: true,
-    installedPath: 'weseek/growi-plugin-copy-code-to-clipboard',
-    origin: {
-      url: 'https://github.com/weseek/growi-plugin-copy-code-to-clipboard',
-    },
-    meta: {
-      name: 'weseek/growi-plugin-copy-code-to-clipboard',
-      types: [GrowiPluginResourceType.Script],
-    },
-  },
-  {
-    isEnabled: true,
-    installedPath: 'weseek/growi-plugin-markdown-templates',
-    origin: {
-      url: 'https://github.com/weseek/growi-plugin-markdown-templates',
-    },
-    meta: {
-      name: 'weseek/growi-plugin-markdown-templates',
-      types: [GrowiPluginResourceType.Template],
-    },
-  },
-];
-// ------------------
-
-
 type HeadersForGrowiPluginProps = {
   pluginManifestEntries: GrowiPluginManifestEntries;
 }
@@ -87,9 +58,9 @@ class GrowiDocument extends Document<GrowiDocumentProps> {
     const initialProps: DocumentInitialProps = await Document.getInitialProps(ctx);
 
     // TODO: load GrowiPlugin documents from DB
-    // const pluginManifestEntries: GrowiPluginManifestEntries = await ActivatePluginService.retrievePluginManifests(growiPluginsExample);
-    const pluginManifestEntries: GrowiPluginManifestEntries = await ActivatePluginService.retrievePluginManifests([]);
-
+    const GrowiPlugin = mongoose.model<GrowiPlugin>('GrowiPlugin');
+    const growiPlugins = await GrowiPlugin.find({ isEnabled: true });
+    const pluginManifestEntries: GrowiPluginManifestEntries = await ActivatePluginService.retrievePluginManifests(growiPlugins);
     return { ...initialProps, pluginManifestEntries };
   }
 

+ 2 - 1
packages/app/src/server/crowi/index.js

@@ -15,6 +15,7 @@ import loggerFactory from '~/utils/logger';
 import { projectRoot } from '~/utils/project-dir-utils';
 
 import Activity from '../models/activity';
+import GrowiPlugin from '../models/growi-plugin';
 import PageRedirect from '../models/page-redirect';
 import Tag from '../models/tag';
 import UserGroup from '../models/user-group';
@@ -68,7 +69,6 @@ function Crowi() {
   this.importService = null;
   this.pluginService = null;
   this.searchService = null;
-  this.pluginService = null;
   this.socketIoService = null;
   this.pageService = null;
   this.syncPageStatusService = null;
@@ -296,6 +296,7 @@ Crowi.prototype.setupModels = async function() {
   allModels.Tag = Tag;
   allModels.UserGroup = UserGroup;
   allModels.PageRedirect = PageRedirect;
+  allModels.growiPlugin = GrowiPlugin;
 
   Object.keys(allModels).forEach((key) => {
     return this.model(key, models[key](this));

+ 40 - 0
packages/app/src/server/models/growi-plugin.ts

@@ -0,0 +1,40 @@
+import {
+  Schema, Model, Document,
+} from 'mongoose';
+
+import {
+  GrowiPlugin, GrowiPluginMeta, GrowiPluginOrigin, GrowiPluginResourceType,
+} from '~/interfaces/plugin';
+
+import { getOrCreateModel } from '../util/mongoose-utils';
+
+export interface GrowiPluginDocument extends GrowiPlugin, Document {
+}
+export type GrowiPluginModel = Model<GrowiPluginDocument>
+
+const growiPluginMetaSchema = new Schema<GrowiPluginMeta>({
+  name: { type: String, required: true },
+  types: {
+    type: [String],
+    enum: GrowiPluginResourceType,
+    require: true,
+  },
+  desc: { type: String },
+  author: { type: String },
+});
+
+const growiPluginOriginSchema = new Schema<GrowiPluginOrigin>({
+  url: { type: String },
+  ghBranch: { type: String },
+  ghTag: { type: String },
+});
+
+const growiPluginSchema = new Schema<GrowiPluginDocument, GrowiPluginModel>({
+  isEnabled: { type: Boolean },
+  installedPath: { type: String },
+  origin: growiPluginOriginSchema,
+  meta: growiPluginMetaSchema,
+});
+
+
+export default getOrCreateModel<GrowiPluginDocument, GrowiPluginModel>('GrowiPlugin', growiPluginSchema);

+ 15 - 5
packages/app/src/server/service/plugin.ts

@@ -1,8 +1,10 @@
-
 import { execSync } from 'child_process';
+import fs from 'fs';
 import path from 'path';
 
-import { GrowiPlugin, GrowiPluginOrigin } from '~/interfaces/plugin';
+import mongoose from 'mongoose';
+
+import { GrowiPlugin, GrowiPluginMeta, GrowiPluginOrigin } from '~/interfaces/plugin';
 import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 
@@ -41,11 +43,20 @@ export class PluginService {
       console.log('downloadZipFile error', err);
     }
 
-    // TODO: detect plugins
-    // TODO: save documents
+    // save plugin metadata
+    const ghRepositoryName = ghUrl.split('/').slice(-1)[0];
+    const installedPath = path.join(downloadDir, `${ghRepositoryName}-main`);
+    const plugins = await PluginService.detectPlugins(origin, installedPath);
+    await this.savePluginMetaData(plugins);
+
     return;
   }
 
+  async savePluginMetaData(plugins: GrowiPlugin[]): Promise<void> {
+    const GrowiPlugin = mongoose.model('GrowiPlugin');
+    await GrowiPlugin.insertMany(plugins);
+  }
+
   // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
   static async detectPlugins(origin: GrowiPluginOrigin, installedPath: string, parentPackageJson?: any): Promise<GrowiPlugin[]> {
     const packageJsonPath = path.resolve(pluginStoringPath, installedPath, 'package.json');
@@ -75,7 +86,6 @@ export class PluginService {
     if (growiPlugin.types == null) {
       throw new Error('\'growiPlugin\' section must have a \'types\' property.');
     }
-
     const plugin = {
       isEnabled: true,
       installedPath,

+ 61 - 0
packages/app/src/stores/template.tsx

@@ -0,0 +1,61 @@
+import { ITemplate } from '@growi/core';
+import useSWR, { SWRResponse } from 'swr';
+
+import { getGrowiFacade } from '~/utils/growi-facade';
+
+const presetTemplates: ITemplate[] = [
+  // preset 1
+  {
+    id: '__preset1__',
+    name: '[Preset] WESEEK Inner Wiki Style',
+    markdown: `# 関連ページ
+
+$lsx()
+
+# `,
+  },
+
+  // preset 2
+  {
+    id: '__preset2__',
+    name: '[Preset] Qiita Style',
+    markdown: `# <会議体名>
+## 日時
+yyyy/mm/dd hh:mm〜hh:mm
+
+## 場所
+
+## 出席者
+-
+
+## 議題
+1. [議題1](#link)
+2.
+3.
+
+## 議事内容
+### <a name="link"></a>議題1
+
+## 決定事項
+- 決定事項1
+
+## アクション事項
+- [ ] アクション
+
+## 次回
+yyyy/mm/dd (予定、時間は追って連絡)`,
+  },
+];
+
+export const useTemplates = (): SWRResponse<ITemplate[], Error> => {
+  return useSWR<ITemplate[], Error>(
+    'templates',
+    () => [
+      ...presetTemplates,
+      ...Object.values(getGrowiFacade().customTemplates ?? {}),
+    ],
+    {
+      fallbackData: presetTemplates,
+    },
+  );
+};