mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-12-23 16:59:48 +01:00
Fix routing for entity names with parenthesis (#1998)
If entities like users, groups or repository namespaces contains parenthesis the frontend router gets confused and doesn't work properly. To fix this issue we escape the chars in the url which may cause such problems because they are reserved by the http url schema. Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com> Co-authored-by: Florian Scholdei <florian.scholdei@cloudogu.com>
This commit is contained in:
2
gradle/changelog/url_escaping_for_parenthesis.yaml
Normal file
2
gradle/changelog/url_escaping_for_parenthesis.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- type: fixed
|
||||||
|
description: Escape parenthesis for entity names to fix routing ([#1998](https://github.com/scm-manager/scm-manager/pull/1998))
|
||||||
@@ -108,3 +108,11 @@ export function matchedUrl(props: any) {
|
|||||||
const match = props.match;
|
const match = props.match;
|
||||||
return matchedUrlFromMatch(match);
|
return matchedUrlFromMatch(match);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function escapeUrlForRoute(url: string) {
|
||||||
|
return url.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unescapeUrlForRoute(url: string) {
|
||||||
|
return url.replace(/\\/g, "");
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { NavLink } from "../navigation";
|
|||||||
import { Route } from "react-router-dom";
|
import { Route } from "react-router-dom";
|
||||||
import { WithTranslation, withTranslation } from "react-i18next";
|
import { WithTranslation, withTranslation } from "react-i18next";
|
||||||
import { Repository, Links, Link } from "@scm-manager/ui-types";
|
import { Repository, Links, Link } from "@scm-manager/ui-types";
|
||||||
|
import { urls } from "@scm-manager/ui-api";
|
||||||
|
|
||||||
type GlobalRouteProps = {
|
type GlobalRouteProps = {
|
||||||
url: string;
|
url: string;
|
||||||
@@ -49,7 +50,7 @@ class ConfigurationBinder {
|
|||||||
|
|
||||||
route(path: string, Component: any) {
|
route(path: string, Component: any) {
|
||||||
return (
|
return (
|
||||||
<Route path={path} exact>
|
<Route path={urls.escapeUrlForRoute(path)} exact>
|
||||||
{Component}
|
{Component}
|
||||||
</Route>
|
</Route>
|
||||||
);
|
);
|
||||||
@@ -135,7 +136,7 @@ class ConfigurationBinder {
|
|||||||
const link = repository._links[linkName];
|
const link = repository._links[linkName];
|
||||||
if (link) {
|
if (link) {
|
||||||
return this.route(
|
return this.route(
|
||||||
url + "/settings" + to,
|
urls.unescapeUrlForRoute(url) + "/settings" + to,
|
||||||
<RepositoryComponent repository={repository} link={(link as Link).href} {...additionalProps} />
|
<RepositoryComponent repository={repository} link={(link as Link).href} {...additionalProps} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { urls } from "@scm-manager/ui-api";
|
||||||
import { useLocation, useRouteMatch } from "react-router-dom";
|
import { useLocation, useRouteMatch } from "react-router-dom";
|
||||||
import { RoutingProps } from "./RoutingProps";
|
import { RoutingProps } from "./RoutingProps";
|
||||||
|
|
||||||
@@ -33,8 +34,8 @@ const useActiveMatch = ({ to, activeOnlyWhenExact, activeWhenMatch }: RoutingPro
|
|||||||
}
|
}
|
||||||
|
|
||||||
const match = useRouteMatch({
|
const match = useRouteMatch({
|
||||||
path,
|
path: urls.escapeUrlForRoute(path),
|
||||||
exact: activeOnlyWhenExact,
|
exact: activeOnlyWhenExact
|
||||||
});
|
});
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -42,7 +43,7 @@ const useActiveMatch = ({ to, activeOnlyWhenExact, activeWhenMatch }: RoutingPro
|
|||||||
const isActiveWhenMatch = () => {
|
const isActiveWhenMatch = () => {
|
||||||
if (activeWhenMatch) {
|
if (activeWhenMatch) {
|
||||||
return activeWhenMatch({
|
return activeWhenMatch({
|
||||||
location,
|
location
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -47,19 +47,20 @@ const SingleRepositoryRole: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = urls.matchedUrlFromMatch(match);
|
const url = urls.matchedUrlFromMatch(match);
|
||||||
|
const escapedUrl = urls.escapeUrlForRoute(url);
|
||||||
|
|
||||||
const extensionProps = {
|
const extensionProps = {
|
||||||
role,
|
role,
|
||||||
url
|
url: escapedUrl
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title title={t("repositoryRole.title")} />
|
<Title title={t("repositoryRole.title")} />
|
||||||
<Route path={`${url}/info`}>
|
<Route path={`${escapedUrl}/info`}>
|
||||||
<PermissionRoleDetail role={role} url={url} />
|
<PermissionRoleDetail role={role} url={url} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/edit`} exact>
|
<Route path={`${escapedUrl}/edit`} exact>
|
||||||
<EditRepositoryRole role={role} />
|
<EditRepositoryRole role={role} />
|
||||||
</Route>
|
</Route>
|
||||||
<ExtensionPoint<extensionPoints.RolesRoute> name="roles.route" props={extensionProps} renderAll={true} />
|
<ExtensionPoint<extensionPoints.RolesRoute> name="roles.route" props={extensionProps} renderAll={true} />
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const SingleGroup: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = urls.matchedUrlFromMatch(match);
|
const url = urls.matchedUrlFromMatch(match);
|
||||||
|
const escapedUrl = urls.escapeUrlForRoute(url);
|
||||||
|
|
||||||
const extensionProps = {
|
const extensionProps = {
|
||||||
group,
|
group,
|
||||||
@@ -70,16 +71,23 @@ const SingleGroup: FC = () => {
|
|||||||
<Page title={group.name}>
|
<Page title={group.name}>
|
||||||
<CustomQueryFlexWrappedColumns>
|
<CustomQueryFlexWrappedColumns>
|
||||||
<PrimaryContentColumn>
|
<PrimaryContentColumn>
|
||||||
<Route path={url} exact>
|
<Route path={escapedUrl} exact>
|
||||||
<Details group={group} />
|
<Details group={group} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/general`} exact>
|
<Route path={`${escapedUrl}/settings/general`} exact>
|
||||||
<EditGroup group={group} />
|
<EditGroup group={group} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/permissions`} exact>
|
<Route path={`${escapedUrl}/settings/permissions`} exact>
|
||||||
<SetGroupPermissions group={group} />
|
<SetGroupPermissions group={group} />
|
||||||
</Route>
|
</Route>
|
||||||
<ExtensionPoint<extensionPoints.GroupRoute> name="group.route" props={extensionProps} renderAll={true} />
|
<ExtensionPoint<extensionPoints.GroupRoute>
|
||||||
|
name="group.route"
|
||||||
|
props={{
|
||||||
|
group,
|
||||||
|
url: escapedUrl
|
||||||
|
}}
|
||||||
|
renderAll={true}
|
||||||
|
/>
|
||||||
</PrimaryContentColumn>
|
</PrimaryContentColumn>
|
||||||
<SecondaryNavigationColumn>
|
<SecondaryNavigationColumn>
|
||||||
<SecondaryNavigation label={t("singleGroup.menu.navigationLabel")}>
|
<SecondaryNavigation label={t("singleGroup.menu.navigationLabel")}>
|
||||||
|
|||||||
@@ -61,10 +61,12 @@ const BranchRoot: FC<Props> = ({ repository }) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const escapedUrl = urls.escapeUrlForRoute(url);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Redirect exact from={url} to={`${url}/info`} />
|
<Redirect exact from={escapedUrl} to={`${url}/info`} />
|
||||||
<Route path={`${url}/info`}>
|
<Route path={`${escapedUrl}/info`}>
|
||||||
<BranchView repository={repository} branch={branch} />
|
<BranchView repository={repository} branch={branch} />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
import React, { FC, ReactNode } from "react";
|
import React, { FC, ReactNode } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { BranchSelector, devices, Level } from "@scm-manager/ui-components";
|
import { BranchSelector, devices, Level, urls } from "@scm-manager/ui-components";
|
||||||
import CodeViewSwitcher, { SwitchViewLink } from "./CodeViewSwitcher";
|
import CodeViewSwitcher, { SwitchViewLink } from "./CodeViewSwitcher";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Branch } from "@scm-manager/ui-types";
|
import { Branch } from "@scm-manager/ui-types";
|
||||||
@@ -87,7 +87,9 @@ const CodeActionBar: FC<Props> = ({ selectedBranch, branches, onSelectBranch, sw
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
children={actions}
|
children={actions}
|
||||||
right={<CodeViewSwitcher currentUrl={location.pathname} switchViewLink={switchViewLink} />}
|
right={
|
||||||
|
<CodeViewSwitcher currentUrl={urls.escapeUrlForRoute(location.pathname)} switchViewLink={switchViewLink} />
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</ActionBar>
|
</ActionBar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import { Route, useLocation } from "react-router-dom";
|
|||||||
import Sources from "../../sources/containers/Sources";
|
import Sources from "../../sources/containers/Sources";
|
||||||
import ChangesetsRoot from "../../containers/ChangesetsRoot";
|
import ChangesetsRoot from "../../containers/ChangesetsRoot";
|
||||||
import { Branch, Repository } from "@scm-manager/ui-types";
|
import { Branch, Repository } from "@scm-manager/ui-types";
|
||||||
import { ErrorPage, Loading } from "@scm-manager/ui-components";
|
import { ErrorPage, Loading, urls } from "@scm-manager/ui-components";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useBranches } from "@scm-manager/ui-api";
|
import { useBranches } from "@scm-manager/ui-api";
|
||||||
import FileSearch from "./FileSearch";
|
import FileSearch from "./FileSearch";
|
||||||
@@ -84,24 +84,28 @@ type RoutingProps = {
|
|||||||
selectedBranch?: string;
|
selectedBranch?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CodeRouting: FC<RoutingProps> = ({ repository, baseUrl, branches, selectedBranch }) => (
|
const CodeRouting: FC<RoutingProps> = ({ repository, baseUrl, branches, selectedBranch }) => {
|
||||||
|
|
||||||
|
const escapedUrl = urls.escapeUrlForRoute(baseUrl);
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<Route path={`${baseUrl}/sources`} exact={true}>
|
<Route path={`${escapedUrl}/sources`} exact={true}>
|
||||||
<Sources repository={repository} baseUrl={baseUrl} branches={branches} />
|
<Sources repository={repository} baseUrl={baseUrl} branches={branches} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${baseUrl}/sources/:revision/:path*`}>
|
<Route path={`${escapedUrl}/sources/:revision/:path*`}>
|
||||||
<Sources repository={repository} baseUrl={baseUrl} branches={branches} selectedBranch={selectedBranch} />
|
<Sources repository={repository} baseUrl={baseUrl} branches={branches} selectedBranch={selectedBranch} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${baseUrl}/changesets`}>
|
<Route path={`${escapedUrl}/changesets`}>
|
||||||
<ChangesetsRoot repository={repository} baseUrl={baseUrl} branches={branches} />
|
<ChangesetsRoot repository={repository} baseUrl={baseUrl} branches={branches} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${baseUrl}/branch/:branch/changesets/`}>
|
<Route path={`${escapedUrl}/branch/:branch/changesets/`}>
|
||||||
<ChangesetsRoot repository={repository} baseUrl={baseUrl} branches={branches} selectedBranch={selectedBranch} />
|
<ChangesetsRoot repository={repository} baseUrl={baseUrl} branches={branches} selectedBranch={selectedBranch} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${baseUrl}/search/:revision/`}>
|
<Route path={`${escapedUrl}/search/:revision/`}>
|
||||||
<FileSearch repository={repository} baseUrl={baseUrl} branches={branches} selectedBranch={selectedBranch} />
|
<FileSearch repository={repository} baseUrl={baseUrl} branches={branches} selectedBranch={selectedBranch} />
|
||||||
</Route>
|
</Route>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default CodeOverview;
|
export default CodeOverview;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const ChangesetRoot: FC<Props> = ({ repository, baseUrl, branches, selectedBranc
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = urls.stripEndingSlash(match.url);
|
const url = urls.stripEndingSlash(urls.escapeUrlForRoute(match.url));
|
||||||
const defaultBranch = branches?.find(b => b.defaultBranch === true);
|
const defaultBranch = branches?.find(b => b.defaultBranch === true);
|
||||||
|
|
||||||
const isBranchAvailable = () => {
|
const isBranchAvailable = () => {
|
||||||
|
|||||||
@@ -229,6 +229,8 @@ const RepositoryRoot = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const escapedUrl = urls.escapeUrlForRoute(url);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StateMenuContextProvider>
|
<StateMenuContextProvider>
|
||||||
<Page
|
<Page
|
||||||
@@ -247,57 +249,64 @@ const RepositoryRoot = () => {
|
|||||||
<CustomQueryFlexWrappedColumns>
|
<CustomQueryFlexWrappedColumns>
|
||||||
<PrimaryContentColumn>
|
<PrimaryContentColumn>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Redirect exact from={match.url} to={redirectedUrl} />
|
<Redirect exact from={urls.escapeUrlForRoute(match.url)} to={urls.escapeUrlForRoute(redirectedUrl)} />
|
||||||
|
|
||||||
{/* redirect pre 2.0.0-rc2 links */}
|
{/* redirect pre 2.0.0-rc2 links */}
|
||||||
<Redirect from={`${url}/changeset/:id`} to={`${url}/code/changeset/:id`} />
|
<Redirect from={`${escapedUrl}/changeset/:id`} to={`${url}/code/changeset/:id`} />
|
||||||
<Redirect exact from={`${url}/sources`} to={`${url}/code/sources`} />
|
<Redirect exact from={`${escapedUrl}/sources`} to={`${url}/code/sources`} />
|
||||||
<Redirect from={`${url}/sources/:revision/:path*`} to={`${url}/code/sources/:revision/:path*`} />
|
<Redirect from={`${escapedUrl}/sources/:revision/:path*`} to={`${url}/code/sources/:revision/:path*`} />
|
||||||
<Redirect exact from={`${url}/changesets`} to={`${url}/code/changesets`} />
|
<Redirect exact from={`${escapedUrl}/changesets`} to={`${url}/code/changesets`} />
|
||||||
<Redirect from={`${url}/branch/:branch/changesets`} to={`${url}/code/branch/:branch/changesets/`} />
|
<Redirect
|
||||||
|
from={`${escapedUrl}/branch/:branch/changesets`}
|
||||||
|
to={`${url}/code/branch/:branch/changesets/`}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route path={`${url}/info`} exact>
|
<Route path={`${escapedUrl}/info`} exact>
|
||||||
<RepositoryDetails repository={repository} />
|
<RepositoryDetails repository={repository} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/general`}>
|
<Route path={`${escapedUrl}/settings/general`}>
|
||||||
<EditRepo repository={repository} />
|
<EditRepo repository={repository} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/permissions`}>
|
<Route path={`${escapedUrl}/settings/permissions`}>
|
||||||
<Permissions namespaceOrRepository={repository} />
|
<Permissions namespaceOrRepository={repository} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path={`${url}/code/changeset/:id`}>
|
<Route exact path={`${escapedUrl}/code/changeset/:id`}>
|
||||||
<ChangesetView repository={repository} fileControlFactoryFactory={fileControlFactoryFactory} />
|
<ChangesetView repository={repository} fileControlFactoryFactory={fileControlFactoryFactory} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/code/sourceext/:extension`} exact={true}>
|
<Route path={`${escapedUrl}/code/sourceext/:extension`} exact={true}>
|
||||||
<SourceExtensions repository={repository} />
|
<SourceExtensions repository={repository} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/code/sourceext/:extension/:revision/:path*`}>
|
<Route path={`${escapedUrl}/code/sourceext/:extension/:revision/:path*`}>
|
||||||
<SourceExtensions repository={repository} baseUrl={`${url}/code/sources`} />
|
<SourceExtensions repository={repository} baseUrl={`${url}/code/sources`} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/code`}>
|
<Route path={`${escapedUrl}/code`}>
|
||||||
<CodeOverview baseUrl={`${url}/code`} repository={repository} />
|
<CodeOverview baseUrl={`${url}/code`} repository={repository} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/branch/:branch`}>
|
<Route path={`${escapedUrl}/branch/:branch`}>
|
||||||
<BranchRoot repository={repository} />
|
<BranchRoot repository={repository} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/branches`} exact={true}>
|
<Route path={`${escapedUrl}/branches`} exact={true}>
|
||||||
<BranchesOverview repository={repository} baseUrl={`${url}/branch`} />
|
<BranchesOverview repository={repository} baseUrl={`${url}/branch`} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/branches/create`}>
|
<Route path={`${escapedUrl}/branches/create`}>
|
||||||
<CreateBranch repository={repository} />
|
<CreateBranch repository={repository} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/tag/:tag`}>
|
<Route path={`${escapedUrl}/tag/:tag`}>
|
||||||
<TagRoot repository={repository} baseUrl={`${url}/tag`} />
|
<TagRoot repository={repository} baseUrl={`${url}/tag`} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/tags`} exact={true}>
|
<Route path={`${escapedUrl}/tags`} exact={true}>
|
||||||
<TagsOverview repository={repository} baseUrl={`${url}/tag`} />
|
<TagsOverview repository={repository} baseUrl={`${url}/tag`} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/compare/:sourceType/:sourceName`}>
|
<Route path={`${escapedUrl}/compare/:sourceType/:sourceName`}>
|
||||||
<CompareRoot repository={repository} baseUrl={`${url}/compare`} />
|
<CompareRoot repository={repository} baseUrl={`${url}/compare`} />
|
||||||
</Route>
|
</Route>
|
||||||
<ExtensionPoint<extensionPoints.RepositoryRoute>
|
<ExtensionPoint<extensionPoints.RepositoryRoute>
|
||||||
name="repository.route"
|
name="repository.route"
|
||||||
props={extensionProps}
|
props={{
|
||||||
|
repository,
|
||||||
|
url: urls.escapeUrlForRoute(url),
|
||||||
|
indexLinks
|
||||||
|
}}
|
||||||
renderAll={true}
|
renderAll={true}
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -52,10 +52,12 @@ const TagRoot: FC<Props> = ({ repository, baseUrl }) => {
|
|||||||
|
|
||||||
const url = urls.matchedUrlFromMatch(match);
|
const url = urls.matchedUrlFromMatch(match);
|
||||||
|
|
||||||
|
const escapedUrl = urls.escapeUrlForRoute(url);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Redirect exact from={url} to={`${url}/info`} />
|
<Redirect exact from={escapedUrl} to={`${url}/info`} />
|
||||||
<Route path={`${url}/info`}>
|
<Route path={`${escapedUrl}/info`}>
|
||||||
<TagView repository={repository} tag={tag} />
|
<TagView repository={repository} tag={tag} />
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -74,30 +74,35 @@ const SingleUser: FC = () => {
|
|||||||
url
|
url
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const escapedUrl = urls.escapeUrlForRoute(url);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StateMenuContextProvider>
|
<StateMenuContextProvider>
|
||||||
<Page title={user.displayName}>
|
<Page title={user.displayName}>
|
||||||
<CustomQueryFlexWrappedColumns>
|
<CustomQueryFlexWrappedColumns>
|
||||||
<PrimaryContentColumn>
|
<PrimaryContentColumn>
|
||||||
<Route path={url} exact>
|
<Route path={escapedUrl} exact>
|
||||||
<Details user={user} />
|
<Details user={user} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/general`}>
|
<Route path={`${escapedUrl}/settings/general`}>
|
||||||
<EditUser user={user} />
|
<EditUser user={user} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/password`}>
|
<Route path={`${escapedUrl}/settings/password`}>
|
||||||
<SetUserPassword user={user} />
|
<SetUserPassword user={user} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/permissions`}>
|
<Route path={`${escapedUrl}/settings/permissions`}>
|
||||||
<SetUserPermissions user={user} />
|
<SetUserPermissions user={user} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/publickeys`}>
|
<Route path={`${escapedUrl}/settings/publickeys`}>
|
||||||
<SetPublicKeys user={user} />
|
<SetPublicKeys user={user} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path={`${url}/settings/apiKeys`}>
|
<Route path={`${escapedUrl}/settings/apiKeys`}>
|
||||||
<SetApiKeys user={user} />
|
<SetApiKeys user={user} />
|
||||||
</Route>
|
</Route>
|
||||||
<ExtensionPoint<extensionPoints.UserRoute> name="user.route" props={extensionProps} renderAll={true} />
|
<ExtensionPoint<extensionPoints.UserRoute> name="user.route" props={{
|
||||||
|
user,
|
||||||
|
url: escapedUrl
|
||||||
|
}} renderAll={true} />
|
||||||
</PrimaryContentColumn>
|
</PrimaryContentColumn>
|
||||||
<SecondaryNavigationColumn>
|
<SecondaryNavigationColumn>
|
||||||
<SecondaryNavigation label={t("singleUser.menu.navigationLabel")}>
|
<SecondaryNavigation label={t("singleUser.menu.navigationLabel")}>
|
||||||
|
|||||||
Reference in New Issue
Block a user