|
@@ -33,6 +33,7 @@ import type { CrowiRequest } from '~/interfaces/crowi-request';
|
|
|
// import { useRendererSettings } from '~/stores/renderer';
|
|
// import { useRendererSettings } from '~/stores/renderer';
|
|
|
// import { EditorMode, useEditorMode, useIsMobile } from '~/stores/ui';
|
|
// import { EditorMode, useEditorMode, useIsMobile } from '~/stores/ui';
|
|
|
import type { EditorConfig } from '~/interfaces/editor-settings';
|
|
import type { EditorConfig } from '~/interfaces/editor-settings';
|
|
|
|
|
+import { IPageGrantData } from '~/interfaces/page';
|
|
|
import type { RendererConfig } from '~/interfaces/services/renderer';
|
|
import type { RendererConfig } from '~/interfaces/services/renderer';
|
|
|
import type { ISidebarConfig } from '~/interfaces/sidebar-config';
|
|
import type { ISidebarConfig } from '~/interfaces/sidebar-config';
|
|
|
import type { IUserUISettings } from '~/interfaces/user-ui-settings';
|
|
import type { IUserUISettings } from '~/interfaces/user-ui-settings';
|
|
@@ -57,7 +58,7 @@ import loggerFactory from '~/utils/logger';
|
|
|
// import GrowiSubNavigationSwitcher from '../client/js/components/Navbar/GrowiSubNavigationSwitcher';
|
|
// import GrowiSubNavigationSwitcher from '../client/js/components/Navbar/GrowiSubNavigationSwitcher';
|
|
|
import { DescendantsPageListModal } from '../components/DescendantsPageListModal';
|
|
import { DescendantsPageListModal } from '../components/DescendantsPageListModal';
|
|
|
import { BasicLayoutWithEditorMode } from '../components/Layout/BasicLayout';
|
|
import { BasicLayoutWithEditorMode } from '../components/Layout/BasicLayout';
|
|
|
-import GrowiContextualSubNavigation from '../components/Navbar/GrowiContextualSubNavigation';
|
|
|
|
|
|
|
+import GrowiContextualSubNavigationSubstance from '../components/Navbar/GrowiContextualSubNavigation';
|
|
|
import DisplaySwitcher from '../components/Page/DisplaySwitcher';
|
|
import DisplaySwitcher from '../components/Page/DisplaySwitcher';
|
|
|
// import { serializeUserSecurely } from '../server/models/serializers/user-serializer';
|
|
// import { serializeUserSecurely } from '../server/models/serializers/user-serializer';
|
|
|
// import PageStatusAlert from '../client/js/components/PageStatusAlert';
|
|
// import PageStatusAlert from '../client/js/components/PageStatusAlert';
|
|
@@ -72,12 +73,12 @@ import {
|
|
|
useIsAclEnabled, useIsSearchPage, useTemplateTagData, useTemplateBodyData, useIsEnabledAttachTitleHeader,
|
|
useIsAclEnabled, useIsSearchPage, useTemplateTagData, useTemplateBodyData, useIsEnabledAttachTitleHeader,
|
|
|
useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
|
|
useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
|
|
|
useIsSlackConfigured, useRendererConfig,
|
|
useIsSlackConfigured, useRendererConfig,
|
|
|
- useEditorConfig, useIsAllReplyShown, useIsUploadableFile, useIsUploadableImage, useCustomizedLogoSrc, useIsContainerFluid, useIsNotCreatable,
|
|
|
|
|
|
|
+ useEditorConfig, useIsAllReplyShown, useIsUploadableFile, useIsUploadableImage, useIsContainerFluid, useIsNotCreatable,
|
|
|
} from '../stores/context';
|
|
} from '../stores/context';
|
|
|
|
|
|
|
|
import { NextPageWithLayout } from './_app.page';
|
|
import { NextPageWithLayout } from './_app.page';
|
|
|
import {
|
|
import {
|
|
|
- CommonProps, getNextI18NextConfig, getServerSideCommonProps, generateCustomTitle,
|
|
|
|
|
|
|
+ CommonProps, getNextI18NextConfig, getServerSideCommonProps, generateCustomTitleForPage,
|
|
|
} from './utils/commons';
|
|
} from './utils/commons';
|
|
|
|
|
|
|
|
|
|
|
|
@@ -91,7 +92,8 @@ const NotCreatablePage = dynamic(() => import('../components/NotCreatablePage').
|
|
|
const ForbiddenPage = dynamic(() => import('../components/ForbiddenPage'), { ssr: false });
|
|
const ForbiddenPage = dynamic(() => import('../components/ForbiddenPage'), { ssr: false });
|
|
|
const UnsavedAlertDialog = dynamic(() => import('../components/UnsavedAlertDialog'), { ssr: false });
|
|
const UnsavedAlertDialog = dynamic(() => import('../components/UnsavedAlertDialog'), { ssr: false });
|
|
|
const PageSideContents = dynamic<PageSideContentsProps>(() => import('../components/PageSideContents').then(mod => mod.PageSideContents), { ssr: false });
|
|
const PageSideContents = dynamic<PageSideContentsProps>(() => import('../components/PageSideContents').then(mod => mod.PageSideContents), { ssr: false });
|
|
|
-const GrowiSubNavigationSwitcher = dynamic(() => import('../components/Navbar/GrowiSubNavigationSwitcher'), { ssr: false });
|
|
|
|
|
|
|
+const GrowiSubNavigationSwitcher = dynamic(() => import('../components/Navbar/GrowiSubNavigationSwitcher')
|
|
|
|
|
+ .then(mod => mod.GrowiSubNavigationSwitcher), { ssr: false });
|
|
|
const UsersHomePageFooter = dynamic<UsersHomePageFooterProps>(() => import('../components/UsersHomePageFooter')
|
|
const UsersHomePageFooter = dynamic<UsersHomePageFooterProps>(() => import('../components/UsersHomePageFooter')
|
|
|
.then(mod => mod.UsersHomePageFooter), { ssr: false });
|
|
.then(mod => mod.UsersHomePageFooter), { ssr: false });
|
|
|
const DrawioModal = dynamic(() => import('../components/PageEditor/DrawioModal').then(mod => mod.DrawioModal), { ssr: false });
|
|
const DrawioModal = dynamic(() => import('../components/PageEditor/DrawioModal').then(mod => mod.DrawioModal), { ssr: false });
|
|
@@ -132,6 +134,20 @@ superjson.registerCustom<IPageToShowRevisionWithMeta, IPageToShowRevisionWithMet
|
|
|
'IPageToShowRevisionWithMetaTransformer',
|
|
'IPageToShowRevisionWithMetaTransformer',
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
|
|
+// GrowiContextualSubNavigation for NOT shared page
|
|
|
|
|
+type GrowiContextualSubNavigationProps = {
|
|
|
|
|
+ isLinkSharingDisabled: boolean,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps): JSX.Element => {
|
|
|
|
|
+ const { isLinkSharingDisabled } = props;
|
|
|
|
|
+ const { data: currentPage } = useSWRxCurrentPage();
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div data-testid="grw-contextual-sub-nav">
|
|
|
|
|
+ <GrowiContextualSubNavigationSubstance currentPage={currentPage} isLinkSharingDisabled={isLinkSharingDisabled}/>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
const IdenticalPathPage = (): JSX.Element => {
|
|
const IdenticalPathPage = (): JSX.Element => {
|
|
|
const IdenticalPathPage = dynamic(() => import('../components/IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
|
|
const IdenticalPathPage = dynamic(() => import('../components/IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
|
|
@@ -185,6 +201,8 @@ type Props = CommonProps & {
|
|
|
isIndentSizeForced: boolean,
|
|
isIndentSizeForced: boolean,
|
|
|
disableLinkSharing: boolean,
|
|
disableLinkSharing: boolean,
|
|
|
|
|
|
|
|
|
|
+ grantData?: IPageGrantData,
|
|
|
|
|
+
|
|
|
rendererConfig: RendererConfig,
|
|
rendererConfig: RendererConfig,
|
|
|
|
|
|
|
|
// UI
|
|
// UI
|
|
@@ -266,7 +284,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
|
|
|
useHasDraftOnHackmd(pageWithMeta?.data.hasDraftOnHackmd ?? false);
|
|
useHasDraftOnHackmd(pageWithMeta?.data.hasDraftOnHackmd ?? false);
|
|
|
useCurrentPathname(props.currentPathname);
|
|
useCurrentPathname(props.currentPathname);
|
|
|
|
|
|
|
|
- useSWRxCurrentPage(undefined, pageWithMeta?.data ?? null); // store initial data
|
|
|
|
|
|
|
+ useSWRxCurrentPage(pageWithMeta?.data ?? null); // store initial data
|
|
|
|
|
|
|
|
useEditingMarkdown(pageWithMeta?.data.revision?.body);
|
|
useEditingMarkdown(pageWithMeta?.data.revision?.body);
|
|
|
|
|
|
|
@@ -284,8 +302,9 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
|
|
|
|
|
|
|
|
// sync grant data
|
|
// sync grant data
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- mutateSelectedGrant(grantData?.grantData.currentPageGrant);
|
|
|
|
|
- }, [grantData?.grantData.currentPageGrant, mutateSelectedGrant]);
|
|
|
|
|
|
|
+ const grantDataToApply = props.grantData ? props.grantData : grantData?.grantData.currentPageGrant;
|
|
|
|
|
+ mutateSelectedGrant(grantDataToApply);
|
|
|
|
|
+ }, [grantData?.grantData.currentPageGrant, mutateSelectedGrant, props.grantData]);
|
|
|
|
|
|
|
|
// sync pathname by Shallow Routing https://nextjs.org/docs/routing/shallow-routing
|
|
// sync pathname by Shallow Routing https://nextjs.org/docs/routing/shallow-routing
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
@@ -298,7 +317,7 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
|
|
|
|
|
|
|
|
const isTopPagePath = isTopPage(pageWithMeta?.data.path ?? '');
|
|
const isTopPagePath = isTopPage(pageWithMeta?.data.path ?? '');
|
|
|
|
|
|
|
|
- const title = generateCustomTitle(props, 'GROWI');
|
|
|
|
|
|
|
+ const title = generateCustomTitleForPage(props, pagePath ?? '');
|
|
|
|
|
|
|
|
|
|
|
|
|
const sideContents = !props.isNotFound && !props.isNotCreatable
|
|
const sideContents = !props.isNotFound && !props.isNotCreatable
|
|
@@ -394,6 +413,21 @@ class MultiplePagesHitsError extends ExtensibleCustomError {
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// apply parent page grant fot creating page
|
|
|
|
|
+async function applyGrantToPage(props: Props, ancestor: any) {
|
|
|
|
|
+ await ancestor.populate('grantedGroup');
|
|
|
|
|
+ const grant = {
|
|
|
|
|
+ grant: ancestor.grant,
|
|
|
|
|
+ };
|
|
|
|
|
+ const grantedGroup = ancestor.grantedGroup ? {
|
|
|
|
|
+ grantedGroup: {
|
|
|
|
|
+ id: ancestor.grantedGroup.id,
|
|
|
|
|
+ name: ancestor.grantedGroup.name,
|
|
|
|
|
+ },
|
|
|
|
|
+ } : {};
|
|
|
|
|
+ props.grantData = Object.assign(grant, grantedGroup);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function injectPageData(context: GetServerSidePropsContext, props: Props): Promise<void> {
|
|
async function injectPageData(context: GetServerSidePropsContext, props: Props): Promise<void> {
|
|
|
const { model: mongooseModel } = await import('mongoose');
|
|
const { model: mongooseModel } = await import('mongoose');
|
|
|
|
|
|
|
@@ -451,6 +485,12 @@ async function injectPageData(context: GetServerSidePropsContext, props: Props):
|
|
|
props.templateTagData = templateData.templateTags as string[];
|
|
props.templateTagData = templateData.templateTags as string[];
|
|
|
props.templateBodyData = templateData.templateBody as string;
|
|
props.templateBodyData = templateData.templateBody as string;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // apply pagrent page grant
|
|
|
|
|
+ const ancestor = await Page.findAncestorByPathAndViewer(currentPathname, user);
|
|
|
|
|
+ if (ancestor != null) {
|
|
|
|
|
+ await applyGrantToPage(props, ancestor);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
props.pageWithMeta = pageWithMeta;
|
|
props.pageWithMeta = pageWithMeta;
|