From d9b25b6737aca00b5e99f88625589171514321b9 Mon Sep 17 00:00:00 2001 From: Yuki Takei Date: Wed, 11 Oct 2023 10:33:46 +0000 Subject: [PATCH 1/7] add model --- .../features/cms/interfaces/cms-namespace.ts | 7 ++++++ apps/app/src/features/cms/interfaces/index.ts | 1 + .../src/features/cms/server/consts/index.ts | 0 .../cms/server/models/cms-namespace.ts | 22 +++++++++++++++++++ .../src/features/cms/server/models/index.ts | 1 + 5 files changed, 31 insertions(+) create mode 100644 apps/app/src/features/cms/interfaces/cms-namespace.ts create mode 100644 apps/app/src/features/cms/interfaces/index.ts create mode 100644 apps/app/src/features/cms/server/consts/index.ts create mode 100644 apps/app/src/features/cms/server/models/cms-namespace.ts create mode 100644 apps/app/src/features/cms/server/models/index.ts diff --git a/apps/app/src/features/cms/interfaces/cms-namespace.ts b/apps/app/src/features/cms/interfaces/cms-namespace.ts new file mode 100644 index 0000000000..6318a43fcb --- /dev/null +++ b/apps/app/src/features/cms/interfaces/cms-namespace.ts @@ -0,0 +1,7 @@ +export type ICmsNamespaceAttribute = Map; +export type ICmsNamespace = { + namespace: string, + desc?: string, + attributes?: ICmsNamespaceAttribute[], + meta?: Map, +}; diff --git a/apps/app/src/features/cms/interfaces/index.ts b/apps/app/src/features/cms/interfaces/index.ts new file mode 100644 index 0000000000..f8f7760638 --- /dev/null +++ b/apps/app/src/features/cms/interfaces/index.ts @@ -0,0 +1 @@ +export * from './cms-namespace'; diff --git a/apps/app/src/features/cms/server/consts/index.ts b/apps/app/src/features/cms/server/consts/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/app/src/features/cms/server/models/cms-namespace.ts b/apps/app/src/features/cms/server/models/cms-namespace.ts new file mode 100644 index 0000000000..392309a216 --- /dev/null +++ b/apps/app/src/features/cms/server/models/cms-namespace.ts @@ -0,0 +1,22 @@ +import { + Schema, type Model, type Document, +} from 'mongoose'; + +import { getOrCreateModel } from '~/server/util/mongoose-utils'; + +import type { ICmsNamespace } from '../../interfaces'; + +export interface ICmsNamespaceDocument extends ICmsNamespace, Document { +} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ICmsNamespaceModel extends Model { +} + +const cmsNamespaceSchema = new Schema({ + namespace: { type: String, required: true, unique: true }, + desc: { type: String }, + attributes: [Map], + meta: Map, +}); + +export const CmsNamespace = getOrCreateModel('CmsNamespace', cmsNamespaceSchema); diff --git a/apps/app/src/features/cms/server/models/index.ts b/apps/app/src/features/cms/server/models/index.ts new file mode 100644 index 0000000000..f8f7760638 --- /dev/null +++ b/apps/app/src/features/cms/server/models/index.ts @@ -0,0 +1 @@ +export * from './cms-namespace'; From cd73302017b29fcdb5b80f413f203a516577b974 Mon Sep 17 00:00:00 2001 From: Yuki Takei Date: Wed, 11 Oct 2023 10:57:43 +0000 Subject: [PATCH 2/7] add cms list pages --- .../cms/client/components/CmsList/CmsList.tsx | 3 + .../cms/client/components/CmsList/index.ts | 1 + apps/app/src/pages/_cms/index.page.tsx | 156 ++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 apps/app/src/features/cms/client/components/CmsList/CmsList.tsx create mode 100644 apps/app/src/features/cms/client/components/CmsList/index.ts create mode 100644 apps/app/src/pages/_cms/index.page.tsx diff --git a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx new file mode 100644 index 0000000000..54be90a6d6 --- /dev/null +++ b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx @@ -0,0 +1,3 @@ +export const CmsList = (): JSX.Element => { + return <>foo; +}; diff --git a/apps/app/src/features/cms/client/components/CmsList/index.ts b/apps/app/src/features/cms/client/components/CmsList/index.ts new file mode 100644 index 0000000000..74aa838064 --- /dev/null +++ b/apps/app/src/features/cms/client/components/CmsList/index.ts @@ -0,0 +1 @@ +export * from './CmsList'; diff --git a/apps/app/src/pages/_cms/index.page.tsx b/apps/app/src/pages/_cms/index.page.tsx new file mode 100644 index 0000000000..529499ce49 --- /dev/null +++ b/apps/app/src/pages/_cms/index.page.tsx @@ -0,0 +1,156 @@ +import React, { ReactNode } from 'react'; + +import type { IUser, IUserHasId } from '@growi/core'; +import type { GetServerSideProps, GetServerSidePropsContext } from 'next'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import dynamic from 'next/dynamic'; +import Head from 'next/head'; + +import { GrowiSubNavigation } from '~/components/Navbar/GrowiSubNavigation'; +import type { CrowiRequest } from '~/interfaces/crowi-request'; +import { useCurrentPageId } from '~/stores/page'; +import { useDrawerMode } from '~/stores/ui'; + +import { BasicLayout } from '../../components/Layout/BasicLayout'; +import { + useCurrentUser, useCurrentPathname, useGrowiCloudUri, + useIsSearchServiceConfigured, useIsSearchServiceReachable, + useIsSearchScopeChildrenAsDefault, useIsSearchPage, useShowPageLimitationXL, +} from '../../stores/context'; +import type { NextPageWithLayout } from '../_app.page'; +import type { CommonProps } from '../utils/commons'; +import { + getServerSideCommonProps, getNextI18NextConfig, generateCustomTitleForPage, useInitSidebarConfig, +} from '../utils/commons'; + +const CmsList = dynamic(() => import('~/features/cms/client/components/CmsList').then(mod => mod.CmsList), { ssr: false }); + +type Props = CommonProps & { + currentUser: IUser, + isSearchServiceConfigured: boolean, + isSearchServiceReachable: boolean, + isSearchScopeChildrenAsDefault: boolean, + showPageLimitationXL: number, +}; + +const CmsPage: NextPageWithLayout = (props: Props) => { + useCurrentUser(props.currentUser ?? null); + + useGrowiCloudUri(props.growiCloudUri); + + useIsSearchServiceConfigured(props.isSearchServiceConfigured); + useIsSearchServiceReachable(props.isSearchServiceReachable); + useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault); + + useIsSearchPage(false); + useCurrentPageId(null); + useCurrentPathname('/trash'); + + // init sidebar config with UserUISettings and sidebarConfig + useInitSidebarConfig(props.sidebarConfig, props.userUISettings); + + useShowPageLimitationXL(props.showPageLimitationXL); + + const { data: isDrawerMode } = useDrawerMode(); + + const title = generateCustomTitleForPage(props, '/trash'); + + return ( + <> + + {title} + +
+
+ +
+ +
+ +
+ +
+
+ + ); +}; + +type LayoutProps = Props & { + children?: ReactNode, +} + +const Layout = ({ children, ...props }: LayoutProps): JSX.Element => { + // init sidebar config with UserUISettings and sidebarConfig + useInitSidebarConfig(props.sidebarConfig, props.userUISettings); + + return {children}; +}; + +CmsPage.getLayout = function getLayout(page) { + return ( + <> + + {page} + + + ); +}; + +function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void { + const req: CrowiRequest = context.req as CrowiRequest; + const { crowi } = req; + const { + searchService, configManager, + } = crowi; + + props.isSearchServiceConfigured = searchService.isConfigured; + props.isSearchServiceReachable = searchService.isReachable; + props.isSearchScopeChildrenAsDefault = configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault'); + props.showPageLimitationXL = crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'); + + props.sidebarConfig = { + isSidebarDrawerMode: configManager.getConfig('crowi', 'customize:isSidebarDrawerMode'), + isSidebarClosedAtDockMode: configManager.getConfig('crowi', 'customize:isSidebarClosedAtDockMode'), + }; + +} + +/** + * for Server Side Translations + * @param context + * @param props + * @param namespacesRequired + */ +async function injectNextI18NextConfigurations(context: GetServerSidePropsContext, props: Props, namespacesRequired?: string[] | undefined): Promise { + const nextI18NextConfig = await getNextI18NextConfig(serverSideTranslations, context, namespacesRequired); + props._nextI18Next = nextI18NextConfig._nextI18Next; +} + +export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => { + const req = context.req as CrowiRequest; + const { user } = req; + const result = await getServerSideCommonProps(context); + + if (!('props' in result)) { + throw new Error('invalid getSSP result'); + } + const props: Props = result.props as Props; + + if (user != null) { + props.currentUser = user.toObject(); + } + injectServerConfigurations(context, props); + await injectNextI18NextConfigurations(context, props, ['translation']); + + return { + props, + }; +}; + +export default CmsPage; From 4fa22cdd6268b9183d408f395f913143323e106d Mon Sep 17 00:00:00 2001 From: Yuki Takei Date: Wed, 11 Oct 2023 11:02:58 +0000 Subject: [PATCH 3/7] modify title --- apps/app/src/pages/_cms/index.page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/app/src/pages/_cms/index.page.tsx b/apps/app/src/pages/_cms/index.page.tsx index 529499ce49..c45c970adf 100644 --- a/apps/app/src/pages/_cms/index.page.tsx +++ b/apps/app/src/pages/_cms/index.page.tsx @@ -44,7 +44,7 @@ const CmsPage: NextPageWithLayout = (props: Props) => { useIsSearchPage(false); useCurrentPageId(null); - useCurrentPathname('/trash'); + useCurrentPathname('/_cms'); // init sidebar config with UserUISettings and sidebarConfig useInitSidebarConfig(props.sidebarConfig, props.userUISettings); @@ -53,7 +53,7 @@ const CmsPage: NextPageWithLayout = (props: Props) => { const { data: isDrawerMode } = useDrawerMode(); - const title = generateCustomTitleForPage(props, '/trash'); + const title = generateCustomTitleForPage(props, 'GROWI CMS Manager'); return ( <> @@ -63,7 +63,7 @@ const CmsPage: NextPageWithLayout = (props: Props) => {
Date: Wed, 11 Oct 2023 11:37:15 +0000 Subject: [PATCH 4/7] add table --- .../cms/client/components/CmsList/CmsList.tsx | 45 ++++++++++++++++++- .../cms/client/stores/cms-namespace.ts | 22 +++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 apps/app/src/features/cms/client/stores/cms-namespace.ts diff --git a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx index 54be90a6d6..2d4a129665 100644 --- a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx +++ b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx @@ -1,3 +1,46 @@ +import Link from 'next/link'; + +import { useSWRxCmsNamespaces } from '../../stores/cms-namespace'; + export const CmsList = (): JSX.Element => { - return <>foo; + const { data } = useSWRxCmsNamespaces(); + + if (data == null) { // data should be not null by `suspense: true` + return <>; + } + + return ( + + + + + + + + + {data.map((cmsNamespace) => { + const { namespace, desc } = cmsNamespace; + return ( + + + + + ); + })} + + + + +
namespacedesc
+ + {namespace} + + + {desc} +
+ +
+ ); }; diff --git a/apps/app/src/features/cms/client/stores/cms-namespace.ts b/apps/app/src/features/cms/client/stores/cms-namespace.ts new file mode 100644 index 0000000000..590400ca0b --- /dev/null +++ b/apps/app/src/features/cms/client/stores/cms-namespace.ts @@ -0,0 +1,22 @@ +import useSWR, { type SWRConfiguration, type SWRResponse } from 'swr'; + +import { apiv3Get } from '~/client/util/apiv3-client'; + +import type { ICmsNamespace } from '../../interfaces'; + +export const useSWRxCmsNamespaces = (config?: SWRConfiguration): SWRResponse => { + return useSWR( + '/cms-namespace', + async(endpoint) => { + return [] as ICmsNamespace[]; + // try { + // const res = await apiv3Get(endpoint); + // return res.data; + // } + // catch (err) { + // throw new Error(err); + // } + }, + config, + ); +}; From 78c0f2c3df38190d2d1ca63b89aed8b2b2a9e9ae Mon Sep 17 00:00:00 2001 From: Yuki Takei Date: Wed, 11 Oct 2023 11:56:07 +0000 Subject: [PATCH 5/7] implement API --- .../cms/client/components/CmsList/CmsList.tsx | 6 ++-- .../cms/client/stores/cms-namespace.ts | 21 +++++++------ .../features/cms/server/routes/apiv3/index.ts | 12 +++++++ .../cms/server/routes/apiv3/namespace.ts | 31 +++++++++++++++++++ apps/app/src/server/routes/apiv3/index.js | 1 + 5 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 apps/app/src/features/cms/server/routes/apiv3/index.ts create mode 100644 apps/app/src/features/cms/server/routes/apiv3/namespace.ts diff --git a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx index 2d4a129665..f4e6b2f493 100644 --- a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx +++ b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx @@ -13,7 +13,7 @@ export const CmsList = (): JSX.Element => { - + @@ -21,8 +21,8 @@ export const CmsList = (): JSX.Element => { {data.map((cmsNamespace) => { const { namespace, desc } = cmsNamespace; return ( - - +
namespacenamespace desc
+
{namespace} diff --git a/apps/app/src/features/cms/client/stores/cms-namespace.ts b/apps/app/src/features/cms/client/stores/cms-namespace.ts index 590400ca0b..2c49ab2941 100644 --- a/apps/app/src/features/cms/client/stores/cms-namespace.ts +++ b/apps/app/src/features/cms/client/stores/cms-namespace.ts @@ -4,18 +4,21 @@ import { apiv3Get } from '~/client/util/apiv3-client'; import type { ICmsNamespace } from '../../interfaces'; +type ListCmsNamespaceResults = { + data: ICmsNamespace[], +} + export const useSWRxCmsNamespaces = (config?: SWRConfiguration): SWRResponse => { return useSWR( - '/cms-namespace', + '/cms/namespace', async(endpoint) => { - return [] as ICmsNamespace[]; - // try { - // const res = await apiv3Get(endpoint); - // return res.data; - // } - // catch (err) { - // throw new Error(err); - // } + try { + const res = await apiv3Get(endpoint); + return res.data.data; + } + catch (err) { + throw new Error(err); + } }, config, ); diff --git a/apps/app/src/features/cms/server/routes/apiv3/index.ts b/apps/app/src/features/cms/server/routes/apiv3/index.ts new file mode 100644 index 0000000000..92aeb5a490 --- /dev/null +++ b/apps/app/src/features/cms/server/routes/apiv3/index.ts @@ -0,0 +1,12 @@ +import express, { Router } from 'express'; + +import Crowi from '~/server/crowi'; + + +module.exports = (crowi: Crowi): Router => { + const router = express.Router(); + + router.use('/namespace', require('./namespace')(crowi)); + + return router; +}; diff --git a/apps/app/src/features/cms/server/routes/apiv3/namespace.ts b/apps/app/src/features/cms/server/routes/apiv3/namespace.ts new file mode 100644 index 0000000000..8e58a3b0cb --- /dev/null +++ b/apps/app/src/features/cms/server/routes/apiv3/namespace.ts @@ -0,0 +1,31 @@ +import express, { Request, Router } from 'express'; + +import Crowi from '~/server/crowi'; +import { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response'; + +import { CmsNamespace } from '../../models'; + + +/* + * Validators + */ +const validator = { +}; + +module.exports = (crowi: Crowi): Router => { + const loginRequiredStrictly = require('~/server/middlewares/login-required')(crowi); + + const router = express.Router(); + + router.get('/', loginRequiredStrictly, async(req: Request, res: ApiV3Response) => { + try { + const data = await CmsNamespace.find({}); + return res.apiv3({ data }); + } + catch (err) { + return res.apiv3Err(err); + } + }); + + return router; +}; diff --git a/apps/app/src/server/routes/apiv3/index.js b/apps/app/src/server/routes/apiv3/index.js index 28ed5875f3..da954c3908 100644 --- a/apps/app/src/server/routes/apiv3/index.js +++ b/apps/app/src/server/routes/apiv3/index.js @@ -116,6 +116,7 @@ module.exports = (crowi, app) => { router.use('/bookmark-folder', require('./bookmark-folder')(crowi)); router.use('/questionnaire', require('~/features/questionnaire/server/routes/apiv3/questionnaire')(crowi)); router.use('/templates', require('~/features/templates/server/routes/apiv3')(crowi)); + router.use('/cms', require('~/features/cms/server/routes/apiv3')(crowi)); return [router, routerForAdmin, routerForAuth]; }; From 31041764bd5a3a15bc56218c27cb790488d9c8d0 Mon Sep 17 00:00:00 2001 From: Yuki Takei Date: Wed, 11 Oct 2023 12:06:01 +0000 Subject: [PATCH 6/7] add a service module to create --- .../cms/client/components/CmsList/CmsList.tsx | 1 + .../app/src/features/cms/client/services/namespace.ts | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 apps/app/src/features/cms/client/services/namespace.ts diff --git a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx index f4e6b2f493..7cad62f4e2 100644 --- a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx +++ b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx @@ -1,5 +1,6 @@ import Link from 'next/link'; +import { create } from '../../services/namespace'; import { useSWRxCmsNamespaces } from '../../stores/cms-namespace'; export const CmsList = (): JSX.Element => { diff --git a/apps/app/src/features/cms/client/services/namespace.ts b/apps/app/src/features/cms/client/services/namespace.ts new file mode 100644 index 0000000000..cfc5736a7c --- /dev/null +++ b/apps/app/src/features/cms/client/services/namespace.ts @@ -0,0 +1,11 @@ +import { apiv3Post } from '~/client/util/apiv3-client'; + +import { ICmsNamespace } from '../../interfaces'; + +export const create = async(namespace: string, desc?: string): Promise => { + const newNamespace: ICmsNamespace = { + namespace, + desc, + }; + await apiv3Post('/cms/namespace', { data: newNamespace }); +}; From 8ef71d9ca9a0ad43e89cbb70a450b546485f078a Mon Sep 17 00:00:00 2001 From: Yuki Takei Date: Wed, 11 Oct 2023 12:11:02 +0000 Subject: [PATCH 7/7] rename files --- apps/app/src/features/cms/client/components/CmsList/CmsList.tsx | 2 +- .../cms/client/services/{namespace.ts => cms-namespace.ts} | 0 .../cms/server/routes/apiv3/{namespace.ts => cms-namespace.ts} | 0 apps/app/src/features/cms/server/routes/apiv3/index.ts | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename apps/app/src/features/cms/client/services/{namespace.ts => cms-namespace.ts} (100%) rename apps/app/src/features/cms/server/routes/apiv3/{namespace.ts => cms-namespace.ts} (100%) diff --git a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx index 7cad62f4e2..ab98e82d8f 100644 --- a/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx +++ b/apps/app/src/features/cms/client/components/CmsList/CmsList.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; -import { create } from '../../services/namespace'; +import { create } from '../../services/cms-namespace'; import { useSWRxCmsNamespaces } from '../../stores/cms-namespace'; export const CmsList = (): JSX.Element => { diff --git a/apps/app/src/features/cms/client/services/namespace.ts b/apps/app/src/features/cms/client/services/cms-namespace.ts similarity index 100% rename from apps/app/src/features/cms/client/services/namespace.ts rename to apps/app/src/features/cms/client/services/cms-namespace.ts diff --git a/apps/app/src/features/cms/server/routes/apiv3/namespace.ts b/apps/app/src/features/cms/server/routes/apiv3/cms-namespace.ts similarity index 100% rename from apps/app/src/features/cms/server/routes/apiv3/namespace.ts rename to apps/app/src/features/cms/server/routes/apiv3/cms-namespace.ts diff --git a/apps/app/src/features/cms/server/routes/apiv3/index.ts b/apps/app/src/features/cms/server/routes/apiv3/index.ts index 92aeb5a490..f4cb353e2e 100644 --- a/apps/app/src/features/cms/server/routes/apiv3/index.ts +++ b/apps/app/src/features/cms/server/routes/apiv3/index.ts @@ -6,7 +6,7 @@ import Crowi from '~/server/crowi'; module.exports = (crowi: Crowi): Router => { const router = express.Router(); - router.use('/namespace', require('./namespace')(crowi)); + router.use('/namespace', require('./cms-namespace')(crowi)); return router; };