mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
Merge remote-tracking branch 'origin/develop' into bugfix/branchselector_overflow
This commit is contained in:
@@ -45,6 +45,7 @@ import GlobalConfig from "./GlobalConfig";
|
||||
import RepositoryRoles from "../roles/containers/RepositoryRoles";
|
||||
import SingleRepositoryRole from "../roles/containers/SingleRepositoryRole";
|
||||
import CreateRepositoryRole from "../roles/containers/CreateRepositoryRole";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
@@ -54,30 +55,17 @@ type Props = RouteComponentProps &
|
||||
};
|
||||
|
||||
class Admin extends React.Component<Props> {
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
if (url.includes("role")) {
|
||||
return url.substring(0, url.length - 2);
|
||||
}
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
matchesRoles = (route: any) => {
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
const regex = new RegExp(`${url}/role/`);
|
||||
return route.location.pathname.match(regex);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { links, availablePluginsLink, installedPluginsLink, t } = this.props;
|
||||
const { links, availablePluginsLink, installedPluginsLink, match, t } = this.props;
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
const extensionProps = {
|
||||
links,
|
||||
url
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Route, withRouter } from "react-router-dom";
|
||||
import { Route, RouteComponentProps, withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
@@ -34,39 +34,29 @@ import { fetchRoleByName, getFetchRoleFailure, getRoleByName, isFetchRolePending
|
||||
import PermissionRoleDetail from "../components/PermissionRoleDetails";
|
||||
import EditRepositoryRole from "./EditRepositoryRole";
|
||||
import { compose } from "redux";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
roleName: string;
|
||||
role: RepositoryRole;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
repositoryRolesLink: string;
|
||||
disabled: boolean;
|
||||
type Props = WithTranslation &
|
||||
RouteComponentProps & {
|
||||
roleName: string;
|
||||
role: RepositoryRole;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
repositoryRolesLink: string;
|
||||
disabled: boolean;
|
||||
|
||||
// dispatcher function
|
||||
fetchRoleByName: (p1: string, p2: string) => void;
|
||||
// dispatcher function
|
||||
fetchRoleByName: (p1: string, p2: string) => void;
|
||||
|
||||
// context objects
|
||||
match: any;
|
||||
history: History;
|
||||
};
|
||||
// context objects
|
||||
history: History;
|
||||
};
|
||||
|
||||
class SingleRepositoryRole extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
this.props.fetchRoleByName(this.props.repositoryRolesLink, this.props.roleName);
|
||||
}
|
||||
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 2);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t, loading, error, role } = this.props;
|
||||
|
||||
@@ -80,7 +70,7 @@ class SingleRepositoryRole extends React.Component<Props> {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
const extensionProps = {
|
||||
role,
|
||||
|
||||
@@ -46,6 +46,7 @@ import CreateGroup from "../groups/containers/CreateGroup";
|
||||
import Admin from "../admin/containers/Admin";
|
||||
|
||||
import Profile from "./Profile";
|
||||
import NamespaceRoot from "../repos/namespaces/containers/NamespaceRoot";
|
||||
|
||||
type Props = {
|
||||
me: Me;
|
||||
@@ -80,6 +81,7 @@ class Main extends React.Component<Props> {
|
||||
<ProtectedRoute exact path="/repos/:namespace" component={Overview} authenticated={authenticated} />
|
||||
<ProtectedRoute exact path="/repos/:namespace/:page" component={Overview} authenticated={authenticated} />
|
||||
<ProtectedRoute path="/repo/:namespace/:name" component={RepositoryRoot} authenticated={authenticated} />
|
||||
<ProtectedRoute path="/namespace/:namespaceName" component={NamespaceRoot} authenticated={authenticated} />
|
||||
<Redirect exact strict from="/users" to="/users/" />
|
||||
<ProtectedRoute exact path="/users/" component={Users} authenticated={authenticated} />
|
||||
<ProtectedRoute path="/users/create" component={CreateUser} authenticated={authenticated} />
|
||||
|
||||
@@ -44,6 +44,7 @@ import ProfileInfo from "./ProfileInfo";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import SetPublicKeys from "../users/components/publicKeys/SetPublicKeys";
|
||||
import SetPublicKeyNavLink from "../users/components/navLinks/SetPublicKeysNavLink";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
@@ -54,17 +55,6 @@ type Props = RouteComponentProps &
|
||||
};
|
||||
|
||||
class Profile extends React.Component<Props> {
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 2);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
mayChangePassword = () => {
|
||||
const { me } = this.props;
|
||||
return !!me?._links?.password;
|
||||
@@ -76,7 +66,7 @@ class Profile extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
const { me, t } = this.props;
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import { Details } from "./../components/table";
|
||||
import { EditGroupNavLink, SetPermissionsNavLink } from "./../components/navLinks";
|
||||
import EditGroup from "./EditGroup";
|
||||
import SetPermissions from "../../permissions/components/SetPermissions";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
@@ -63,17 +64,6 @@ class SingleGroup extends React.Component<Props> {
|
||||
this.props.fetchGroupByName(this.props.groupLink, this.props.name);
|
||||
}
|
||||
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 2);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t, loading, error, group } = this.props;
|
||||
|
||||
@@ -85,7 +75,7 @@ class SingleGroup extends React.Component<Props> {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
const extensionProps = {
|
||||
group,
|
||||
|
||||
@@ -31,6 +31,7 @@ import { fetchBranch, getBranch, getFetchBranchFailure, isFetchBranchPending } f
|
||||
import { ErrorNotification, Loading, NotFoundError } from "@scm-manager/ui-components";
|
||||
import { History } from "history";
|
||||
import queryString from "query-string";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
@@ -54,21 +55,10 @@ class BranchRoot extends React.Component<Props> {
|
||||
fetchBranch(repository, branchName);
|
||||
}
|
||||
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { repository, branch, loading, error, match, location } = this.props;
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
if (error) {
|
||||
if (error instanceof NotFoundError && queryString.parse(location.search).create === "true") {
|
||||
|
||||
@@ -22,21 +22,37 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { CardColumnGroup, RepositoryEntry } from "@scm-manager/ui-components";
|
||||
import { RepositoryGroup } from "@scm-manager/ui-types";
|
||||
import { Icon } from "@scm-manager/ui-components";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
type Props = WithTranslation & {
|
||||
group: RepositoryGroup;
|
||||
};
|
||||
|
||||
class RepositoryGroupEntry extends React.Component<Props> {
|
||||
render() {
|
||||
const { group } = this.props;
|
||||
const { group, t } = this.props;
|
||||
const settingsLink = group.namespace?._links?.permissions && (
|
||||
<Link to={`/namespace/${group.name}/settings`}>
|
||||
<Icon color={"is-link"} name={"cog"} title={t("repositoryOverview.settings.tooltip")} />
|
||||
</Link>
|
||||
);
|
||||
const namespaceHeader = (
|
||||
<>
|
||||
<Link to={`/repos/${group.name}/`} className={"has-text-dark"}>
|
||||
{group.name}
|
||||
</Link>{" "}
|
||||
{settingsLink}
|
||||
</>
|
||||
);
|
||||
const entries = group.repositories.map((repository, index) => {
|
||||
return <RepositoryEntry repository={repository} key={index} />;
|
||||
});
|
||||
return <CardColumnGroup name={group.name} url={`/repos/${group.name}/`} elements={entries} />;
|
||||
return <CardColumnGroup name={namespaceHeader} elements={entries} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default RepositoryGroupEntry;
|
||||
export default withTranslation("namespaces")(RepositoryGroupEntry);
|
||||
|
||||
@@ -23,20 +23,21 @@
|
||||
*/
|
||||
import React from "react";
|
||||
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { NamespaceCollection, Repository } from "@scm-manager/ui-types";
|
||||
|
||||
import groupByNamespace from "./groupByNamespace";
|
||||
import RepositoryGroupEntry from "./RepositoryGroupEntry";
|
||||
|
||||
type Props = {
|
||||
repositories: Repository[];
|
||||
namespaces: NamespaceCollection;
|
||||
};
|
||||
|
||||
class RepositoryList extends React.Component<Props> {
|
||||
render() {
|
||||
const { repositories } = this.props;
|
||||
const { repositories, namespaces } = this.props;
|
||||
|
||||
const groups = groupByNamespace(repositories);
|
||||
const groups = groupByNamespace(repositories, namespaces);
|
||||
return (
|
||||
<div className="content">
|
||||
{groups.map(group => {
|
||||
|
||||
@@ -73,21 +73,29 @@ it("should group the repositories by their namespace", () => {
|
||||
hitchhikerHeartOfGold,
|
||||
hitchhikerPuzzle42
|
||||
];
|
||||
const namespaces = {
|
||||
_embedded: {
|
||||
namespaces: [{ namespace: "hitchhiker" }, { namespace: "slarti" }, { namespace: "zaphod" }]
|
||||
}
|
||||
};
|
||||
|
||||
const expected = [
|
||||
{
|
||||
name: "hitchhiker",
|
||||
namespace: { namespace: "hitchhiker" },
|
||||
repositories: [hitchhikerHeartOfGold, hitchhikerPuzzle42, hitchhikerRestand]
|
||||
},
|
||||
{
|
||||
name: "slarti",
|
||||
namespace: { namespace: "slarti" },
|
||||
repositories: [slartiFjords, slartiBlueprintsFjords]
|
||||
},
|
||||
{
|
||||
name: "zaphod",
|
||||
namespace: { namespace: "zaphod" },
|
||||
repositories: [zaphodMarvinFirmware]
|
||||
}
|
||||
];
|
||||
|
||||
expect(groupByNamespace(repositories)).toEqual(expected);
|
||||
expect(groupByNamespace(repositories, namespaces)).toEqual(expected);
|
||||
});
|
||||
|
||||
@@ -22,17 +22,22 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Repository, RepositoryGroup } from "@scm-manager/ui-types";
|
||||
import { NamespaceCollection, Repository, RepositoryGroup } from "@scm-manager/ui-types";
|
||||
|
||||
export default function groupByNamespace(repositories: Repository[]): RepositoryGroup[] {
|
||||
export default function groupByNamespace(
|
||||
repositories: Repository[],
|
||||
namespaces: NamespaceCollection
|
||||
): RepositoryGroup[] {
|
||||
const groups = {};
|
||||
for (const repository of repositories) {
|
||||
const groupName = repository.namespace;
|
||||
|
||||
let group = groups[groupName];
|
||||
if (!group) {
|
||||
const namespace = findNamespace(namespaces, groupName);
|
||||
group = {
|
||||
name: groupName,
|
||||
namespace: namespace,
|
||||
repositories: []
|
||||
};
|
||||
groups[groupName] = group;
|
||||
@@ -58,3 +63,7 @@ function sortByName(a, b) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function findNamespace(namespaces: NamespaceCollection, namespaceToFind: string) {
|
||||
return namespaces._embedded.namespaces.find(namespace => namespace.namespace === namespaceToFind);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import Changesets from "./Changesets";
|
||||
import { compose } from "redux";
|
||||
import CodeActionBar from "../codeSection/components/CodeActionBar";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation &
|
||||
RouteComponentProps & {
|
||||
@@ -38,13 +39,6 @@ type Props = WithTranslation &
|
||||
};
|
||||
|
||||
class ChangesetsRoot extends React.Component<Props> {
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
isBranchAvailable = () => {
|
||||
const { branches, selectedBranch } = this.props;
|
||||
return branches?.filter(b => b.name === selectedBranch).length === 0;
|
||||
@@ -75,7 +69,7 @@ class ChangesetsRoot extends React.Component<Props> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const url = this.stripEndingSlash(match.url);
|
||||
const url = urls.stripEndingSlash(match.url);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -76,7 +76,7 @@ const DangerZone: FC<Props> = ({ repository, indexLinks }) => {
|
||||
<>
|
||||
<hr />
|
||||
<Subtitle subtitle={t("repositoryForm.dangerZone")} />
|
||||
<DangerZoneContainer>{dangerZone.map(entry => entry)}</DangerZoneContainer>
|
||||
<DangerZoneContainer>{dangerZone}</DangerZoneContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||
import RepositoryForm from "../components/form";
|
||||
import { Repository, Links } from "@scm-manager/ui-types";
|
||||
import { getModifyRepoFailure, isModifyRepoPending, modifyRepo, modifyRepoReset } from "../modules/repos";
|
||||
@@ -33,8 +33,9 @@ import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { compose } from "redux";
|
||||
import DangerZone from "./DangerZone";
|
||||
import { getLinks } from "../../modules/indexResource";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
type Props = RouteComponentProps & {
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
indexLinks: Links;
|
||||
@@ -45,7 +46,6 @@ type Props = {
|
||||
// context props
|
||||
repository: Repository;
|
||||
history: History;
|
||||
match: any;
|
||||
};
|
||||
|
||||
class EditRepo extends React.Component<Props> {
|
||||
@@ -59,21 +59,10 @@ class EditRepo extends React.Component<Props> {
|
||||
history.push(`/repo/${repository.namespace}/${repository.name}`);
|
||||
};
|
||||
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 2);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading, error, repository, indexLinks } = this.props;
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
const extensionProps = {
|
||||
repository,
|
||||
|
||||
@@ -133,12 +133,12 @@ class Overview extends React.Component<Props> {
|
||||
}
|
||||
|
||||
renderRepositoryList() {
|
||||
const { collection, page, location, t } = this.props;
|
||||
const { collection, page, location, namespaces, t } = this.props;
|
||||
|
||||
if (collection._embedded && collection._embedded.repositories.length > 0) {
|
||||
return (
|
||||
<>
|
||||
<RepositoryList repositories={collection._embedded.repositories} />
|
||||
<RepositoryList repositories={collection._embedded.repositories} namespaces={namespaces} />
|
||||
<LinkPaginator collection={collection} page={page} filter={urls.getQueryStringFromLocation(location)} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -159,7 +159,9 @@ const RenameRepository: FC<Props> = ({ repository, indexLinks }) => {
|
||||
<p>
|
||||
<strong>{t("renameRepo.subtitle")}</strong>
|
||||
<br />
|
||||
{t("renameRepo.description")}
|
||||
{t("renameRepo.description1")}
|
||||
<br />
|
||||
{t("renameRepo.description2")}
|
||||
</p>
|
||||
}
|
||||
right={
|
||||
|
||||
@@ -56,6 +56,7 @@ import SourceExtensions from "../sources/containers/SourceExtensions";
|
||||
import { FileControlFactory, JumpToFileButton } from "@scm-manager/ui-components";
|
||||
import TagsOverview from "../tags/container/TagsOverview";
|
||||
import TagRoot from "../tags/container/TagRoot";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
@@ -84,31 +85,20 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
matchesBranches = (route: any) => {
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
const regex = new RegExp(`${url}/branch/.+/info`);
|
||||
return route.location.pathname.match(regex);
|
||||
};
|
||||
|
||||
matchesTags = (route: any) => {
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
const regex = new RegExp(`${url}/tag/.+/info`);
|
||||
return route.location.pathname.match(regex);
|
||||
};
|
||||
|
||||
matchesCode = (route: any) => {
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
const regex = new RegExp(`${url}(/code)/.*`);
|
||||
return route.location.pathname.match(regex);
|
||||
};
|
||||
@@ -126,7 +116,7 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
|
||||
evaluateDestinationForCodeLink = () => {
|
||||
const { repository } = this.props;
|
||||
const url = `${this.matchedUrl()}/code`;
|
||||
const url = `${urls.matchedUrl(this.props)}/code`;
|
||||
if (repository?._links?.sources) {
|
||||
return `${url}/sources/`;
|
||||
}
|
||||
@@ -146,7 +136,7 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
const extensionProps = {
|
||||
repository,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import * as types from "../../modules/types";
|
||||
import {
|
||||
Action,
|
||||
Action, Namespace,
|
||||
NamespaceCollection,
|
||||
Repository,
|
||||
RepositoryCollection,
|
||||
@@ -66,6 +66,11 @@ export const DELETE_REPO_PENDING = `${DELETE_REPO}_${types.PENDING_SUFFIX}`;
|
||||
export const DELETE_REPO_SUCCESS = `${DELETE_REPO}_${types.SUCCESS_SUFFIX}`;
|
||||
export const DELETE_REPO_FAILURE = `${DELETE_REPO}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export const FETCH_NAMESPACE = "scm/repos/FETCH_NAMESPACE";
|
||||
export const FETCH_NAMESPACE_PENDING = `${FETCH_NAMESPACE}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_NAMESPACE_SUCCESS = `${FETCH_NAMESPACE}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_NAMESPACE_FAILURE = `${FETCH_NAMESPACE}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export const CONTENT_TYPE = "application/vnd.scmm-repository+json;v=2";
|
||||
|
||||
export const CUSTOM_NAMESPACE_STRATEGY = "CustomNamespaceStrategy";
|
||||
@@ -388,6 +393,50 @@ export function deleteRepoFailure(repository: Repository, error: Error): Action
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchNamespace(link: string, namespaceName: string) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(fetchNamespacePending(namespaceName));
|
||||
return apiClient
|
||||
.get(link)
|
||||
.then(response => response.json())
|
||||
.then(namespace => {
|
||||
dispatch(fetchNamespaceSuccess(namespace));
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(fetchNamespaceFailure(namespaceName, err));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchNamespacePending(namespaceName: string): Action {
|
||||
return {
|
||||
type: FETCH_NAMESPACE_PENDING,
|
||||
payload: {
|
||||
namespaceName
|
||||
},
|
||||
itemId: namespaceName
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchNamespaceSuccess(namespace: Namespace): Action {
|
||||
return {
|
||||
type: FETCH_NAMESPACE_SUCCESS,
|
||||
payload: namespace,
|
||||
itemId: namespace.namespace
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchNamespaceFailure(namespaceName: string, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_NAMESPACE_FAILURE,
|
||||
payload: {
|
||||
namespaceName,
|
||||
error
|
||||
},
|
||||
itemId: namespaceName
|
||||
};
|
||||
}
|
||||
|
||||
// reducer
|
||||
|
||||
function createIdentifier(repository: Repository) {
|
||||
@@ -425,6 +474,17 @@ const reducerByNames = (state: object, repository: Repository) => {
|
||||
};
|
||||
};
|
||||
|
||||
const reducerForNamespace = (state: object, namespace: Namespace) => {
|
||||
const identifier = namespace.namespace;
|
||||
return {
|
||||
...state,
|
||||
namespacesByNames: {
|
||||
...state.namespacesByNames,
|
||||
[identifier]: namespace
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const reducerForNamespaces = (state: object, namespaces: NamespaceCollection) => {
|
||||
return {
|
||||
...state,
|
||||
@@ -449,6 +509,8 @@ export default function reducer(
|
||||
return reducerForNamespaces(state, action.payload);
|
||||
case FETCH_REPO_SUCCESS:
|
||||
return reducerByNames(state, action.payload);
|
||||
case FETCH_NAMESPACE_SUCCESS:
|
||||
return reducerForNamespace(state, action.payload);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -497,10 +559,17 @@ export function getFetchNamespacesFailure(state: object) {
|
||||
return getFailure(state, FETCH_NAMESPACES);
|
||||
}
|
||||
|
||||
export function getNamespace(state: object, namespace: string) {
|
||||
if (state.namespaces) {
|
||||
return state.namespaces[namespace];
|
||||
}
|
||||
export function isFetchNamespacePending(state: object) {
|
||||
return isPending(state, FETCH_NAMESPACE);
|
||||
}
|
||||
|
||||
export function getFetchNamespaceFailure(state: object) {
|
||||
return getFailure(state, FETCH_NAMESPACE);
|
||||
}
|
||||
|
||||
export function fetchNamespaceByName(link: string, namespaceName: string) {
|
||||
const namespaceUrl = link.endsWith("/") ? link : link + "/";
|
||||
return fetchNamespace(`${namespaceUrl}${namespaceName}`, namespaceName);
|
||||
}
|
||||
|
||||
export function isFetchRepoPending(state: object, namespace: string, name: string) {
|
||||
@@ -511,6 +580,12 @@ export function getFetchRepoFailure(state: object, namespace: string, name: stri
|
||||
return getFailure(state, FETCH_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function getNamespace(state: object, namespaceName: string) {
|
||||
if (state.repos && state.repos.namespacesByNames) {
|
||||
return state.repos.namespacesByNames[namespaceName];
|
||||
}
|
||||
}
|
||||
|
||||
export function isAbleToCreateRepos(state: object) {
|
||||
return !!(state.repos && state.repos.list && state.repos.list._links && state.repos.list._links.create);
|
||||
}
|
||||
@@ -539,7 +614,12 @@ export function getDeleteRepoFailure(state: object, namespace: string, name: str
|
||||
return getFailure(state, DELETE_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function getPermissionsLink(state: object, namespace: string, name: string) {
|
||||
const repo = getRepository(state, namespace, name);
|
||||
return repo && repo._links ? repo._links.permissions.href : undefined;
|
||||
export function getPermissionsLink(state: object, namespaceName: string, repoName?: string) {
|
||||
if (repoName) {
|
||||
const repo = getRepository(state, namespaceName, repoName);
|
||||
return repo?._links ? repo._links.permissions.href : undefined;
|
||||
} else {
|
||||
const namespace = getNamespace(state, namespaceName);
|
||||
return namespace?._links ? namespace?._links?.permissions?.href : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||
import { fetchNamespaceByName, getNamespace, isFetchNamespacePending } from "../../modules/repos";
|
||||
import { getNamespacesLink } from "../../../modules/indexResource";
|
||||
import { Namespace } from "@scm-manager/ui-types";
|
||||
import {
|
||||
CustomQueryFlexWrappedColumns,
|
||||
ErrorPage,
|
||||
Loading,
|
||||
Page,
|
||||
PrimaryContentColumn,
|
||||
SecondaryNavigation,
|
||||
SecondaryNavigationColumn,
|
||||
StateMenuContextProvider,
|
||||
SubNavigation
|
||||
} from "@scm-manager/ui-components";
|
||||
import Permissions from "../../permissions/containers/Permissions";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import PermissionsNavLink from "./PermissionsNavLink";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
loading: boolean;
|
||||
namespaceName: string;
|
||||
namespacesLink: string;
|
||||
namespace: Namespace;
|
||||
error: Error;
|
||||
|
||||
// dispatch functions
|
||||
fetchNamespace: (link: string, namespace: string) => void;
|
||||
};
|
||||
|
||||
class NamespaceRoot extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { namespacesLink, namespaceName, fetchNamespace } = this.props;
|
||||
fetchNamespace(namespacesLink, namespaceName);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, error, namespaceName, namespace, t } = this.props;
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
const extensionProps = {
|
||||
namespace,
|
||||
url
|
||||
};
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage title={t("namespaceRoot.errorTitle")} subtitle={t("namespaceRoot.errorSubtitle")} error={error} />
|
||||
);
|
||||
}
|
||||
|
||||
if (!namespace || loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<StateMenuContextProvider>
|
||||
<Page title={namespaceName}>
|
||||
<CustomQueryFlexWrappedColumns>
|
||||
<PrimaryContentColumn>
|
||||
<Switch>
|
||||
<Redirect exact from={`${url}/settings`} to={`${url}/settings/permissions`} />
|
||||
<Route
|
||||
path={`${url}/settings/permissions`}
|
||||
render={() => {
|
||||
return <Permissions namespace={namespaceName} />;
|
||||
}}
|
||||
/>
|
||||
</Switch>
|
||||
</PrimaryContentColumn>
|
||||
<SecondaryNavigationColumn>
|
||||
<SecondaryNavigation label={t("namespaceRoot.menu.navigationLabel")}>
|
||||
<ExtensionPoint name="namespace.navigation.topLevel" props={extensionProps} renderAll={true} />
|
||||
<ExtensionPoint name="namespace.route" props={extensionProps} renderAll={true} />
|
||||
<SubNavigation
|
||||
to={`${url}/settings`}
|
||||
label={t("namespaceRoot.menu.settingsNavLink")}
|
||||
title={t("namespaceRoot.menu.settingsNavLink")}
|
||||
>
|
||||
<PermissionsNavLink permissionUrl={`${url}/settings/permissions`} namespace={namespace} />
|
||||
<ExtensionPoint name="namespace.setting" props={extensionProps} renderAll={true} />
|
||||
</SubNavigation>
|
||||
</SecondaryNavigation>
|
||||
</SecondaryNavigationColumn>
|
||||
</CustomQueryFlexWrappedColumns>
|
||||
</Page>
|
||||
</StateMenuContextProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const { namespaceName } = ownProps.match.params;
|
||||
const namespacesLink = getNamespacesLink(state);
|
||||
const namespace = getNamespace(state, namespaceName);
|
||||
const loading = isFetchNamespacePending(state);
|
||||
return { namespaceName, namespacesLink, loading, namespace };
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => {
|
||||
return {
|
||||
fetchNamespace: (link: string, namespaceName: string) => {
|
||||
dispatch(fetchNamespaceByName(link, namespaceName));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withTranslation("namespaces")(NamespaceRoot));
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Namespace } from "@scm-manager/ui-types";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
permissionUrl: string;
|
||||
namespace: Namespace;
|
||||
};
|
||||
|
||||
class PermissionsNavLink extends React.Component<Props> {
|
||||
hasPermissionsLink = () => {
|
||||
return this.props.namespace?._links?.permissions;
|
||||
};
|
||||
render() {
|
||||
if (!this.hasPermissionsLink()) {
|
||||
return null;
|
||||
}
|
||||
const { permissionUrl, t } = this.props;
|
||||
return <NavLink to={permissionUrl} label={t("namespaceRoot.menu.permissionsNavLink")} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("namespaces")(PermissionsNavLink);
|
||||
@@ -57,13 +57,12 @@ import {
|
||||
getRepositoryVerbsLink,
|
||||
getUserAutoCompleteLink
|
||||
} from "../../../modules/indexResource";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
availablePermissions: boolean;
|
||||
availableRepositoryRoles: RepositoryRole[];
|
||||
availableVerbs: string[];
|
||||
namespace: string;
|
||||
repoName: string;
|
||||
repoName?: string;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
permissions: PermissionCollection;
|
||||
@@ -77,17 +76,17 @@ type Props = WithTranslation & {
|
||||
|
||||
// dispatch functions
|
||||
fetchAvailablePermissionsIfNeeded: (repositoryRolesLink: string, repositoryVerbsLink: string) => void;
|
||||
fetchPermissions: (link: string, namespace: string, repoName: string) => void;
|
||||
fetchPermissions: (link: string, namespace: string, repoName?: string) => void;
|
||||
createPermission: (
|
||||
link: string,
|
||||
permission: PermissionCreateEntry,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
repoName?: string,
|
||||
callback?: () => void
|
||||
) => void;
|
||||
createPermissionReset: (p1: string, p2: string) => void;
|
||||
modifyPermissionReset: (p1: string, p2: string) => void;
|
||||
deletePermissionReset: (p1: string, p2: string) => void;
|
||||
createPermissionReset: (namespace: string, repoName?: string) => void;
|
||||
modifyPermissionReset: (namespace: string, repoName?: string) => void;
|
||||
deletePermissionReset: (namespace: string, repoName?: string) => void;
|
||||
|
||||
// context props
|
||||
match: any;
|
||||
@@ -241,7 +240,7 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
|
||||
const mapDispatchToProps = (dispatch: any) => {
|
||||
return {
|
||||
fetchPermissions: (link: string, namespace: string, repoName: string) => {
|
||||
fetchPermissions: (link: string, namespace: string, repoName?: string) => {
|
||||
dispatch(fetchPermissions(link, namespace, repoName));
|
||||
},
|
||||
fetchAvailablePermissionsIfNeeded: (repositoryRolesLink: string, repositoryVerbsLink: string) => {
|
||||
@@ -256,13 +255,13 @@ const mapDispatchToProps = (dispatch: any) => {
|
||||
) => {
|
||||
dispatch(createPermission(link, permission, namespace, repoName, callback));
|
||||
},
|
||||
createPermissionReset: (namespace: string, repoName: string) => {
|
||||
createPermissionReset: (namespace: string, repoName?: string) => {
|
||||
dispatch(createPermissionReset(namespace, repoName));
|
||||
},
|
||||
modifyPermissionReset: (namespace: string, repoName: string) => {
|
||||
modifyPermissionReset: (namespace: string, repoName?: string) => {
|
||||
dispatch(modifyPermissionReset(namespace, repoName));
|
||||
},
|
||||
deletePermissionReset: (namespace: string, repoName: string) => {
|
||||
deletePermissionReset: (namespace: string, repoName?: string) => {
|
||||
dispatch(deletePermissionReset(namespace, repoName));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,14 +43,14 @@ type Props = WithTranslation & {
|
||||
availableRepositoryRoles: RepositoryRole[];
|
||||
availableRepositoryVerbs: string[];
|
||||
submitForm: (p: Permission) => void;
|
||||
modifyPermission: (permission: Permission, namespace: string, name: string) => void;
|
||||
modifyPermission: (permission: Permission, namespace: string, name?: string) => void;
|
||||
permission: Permission;
|
||||
namespace: string;
|
||||
repoName: string;
|
||||
repoName?: string;
|
||||
match: any;
|
||||
history: History;
|
||||
loading: boolean;
|
||||
deletePermission: (permission: Permission, namespace: string, name: string) => void;
|
||||
deletePermission: (permission: Permission, namespace: string, name?: string) => void;
|
||||
deleteLoading: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ export function fetchAvailableFailure(error: Error): Action {
|
||||
|
||||
// fetch permissions
|
||||
|
||||
export function fetchPermissions(link: string, namespace: string, repoName: string) {
|
||||
export function fetchPermissions(link: string, namespace: string, repoName?: string) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(fetchPermissionsPending(namespace, repoName));
|
||||
return apiClient
|
||||
@@ -146,26 +146,26 @@ export function fetchPermissions(link: string, namespace: string, repoName: stri
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPermissionsPending(namespace: string, repoName: string): Action {
|
||||
export function fetchPermissionsPending(namespace: string, repoName?: string): Action {
|
||||
return {
|
||||
type: FETCH_PERMISSIONS_PENDING,
|
||||
payload: {
|
||||
namespace,
|
||||
repoName
|
||||
},
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPermissionsSuccess(permissions: any, namespace: string, repoName: string): Action {
|
||||
export function fetchPermissionsSuccess(permissions: any, namespace: string, repoName?: string): Action {
|
||||
return {
|
||||
type: FETCH_PERMISSIONS_SUCCESS,
|
||||
payload: permissions,
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPermissionsFailure(namespace: string, repoName: string, error: Error): Action {
|
||||
export function fetchPermissionsFailure(namespace: string, repoName?: string, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_PERMISSIONS_FAILURE,
|
||||
payload: {
|
||||
@@ -173,13 +173,13 @@ export function fetchPermissionsFailure(namespace: string, repoName: string, err
|
||||
repoName,
|
||||
error
|
||||
},
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
// modify permission
|
||||
|
||||
export function modifyPermission(permission: Permission, namespace: string, repoName: string, callback?: () => void) {
|
||||
export function modifyPermission(permission: Permission, namespace: string, repoName?: string, callback?: () => void) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(modifyPermissionPending(permission, namespace, repoName));
|
||||
return apiClient
|
||||
@@ -196,7 +196,7 @@ export function modifyPermission(permission: Permission, namespace: string, repo
|
||||
};
|
||||
}
|
||||
|
||||
export function modifyPermissionPending(permission: Permission, namespace: string, repoName: string): Action {
|
||||
export function modifyPermissionPending(permission: Permission, namespace: string, repoName?: string): Action {
|
||||
return {
|
||||
type: MODIFY_PERMISSION_PENDING,
|
||||
payload: permission,
|
||||
@@ -204,12 +204,12 @@ export function modifyPermissionPending(permission: Permission, namespace: strin
|
||||
};
|
||||
}
|
||||
|
||||
export function modifyPermissionSuccess(permission: Permission, namespace: string, repoName: string): Action {
|
||||
export function modifyPermissionSuccess(permission: Permission, namespace: string, repoName?: string): Action {
|
||||
return {
|
||||
type: MODIFY_PERMISSION_SUCCESS,
|
||||
payload: {
|
||||
permission,
|
||||
position: namespace + "/" + repoName
|
||||
position: createPermissionStateKey(namespace, repoName)
|
||||
},
|
||||
itemId: createItemId(permission, namespace, repoName)
|
||||
};
|
||||
@@ -219,7 +219,7 @@ export function modifyPermissionFailure(
|
||||
permission: Permission,
|
||||
error: Error,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
repoName?: string
|
||||
): Action {
|
||||
return {
|
||||
type: MODIFY_PERMISSION_FAILURE,
|
||||
@@ -240,14 +240,14 @@ function newPermissions(oldPermissions: PermissionCollection, newPermission: Per
|
||||
}
|
||||
}
|
||||
|
||||
export function modifyPermissionReset(namespace: string, repoName: string) {
|
||||
export function modifyPermissionReset(namespace: string, repoName?: string) {
|
||||
return {
|
||||
type: MODIFY_PERMISSION_RESET,
|
||||
payload: {
|
||||
namespace,
|
||||
repoName
|
||||
},
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ export function createPermission(
|
||||
link: string,
|
||||
permission: PermissionCreateEntry,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
repoName?: string,
|
||||
callback?: () => void
|
||||
) {
|
||||
return function(dispatch: Dispatch) {
|
||||
@@ -281,48 +281,48 @@ export function createPermission(
|
||||
export function createPermissionPending(
|
||||
permission: PermissionCreateEntry,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
repoName?: string
|
||||
): Action {
|
||||
return {
|
||||
type: CREATE_PERMISSION_PENDING,
|
||||
payload: permission,
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
export function createPermissionSuccess(
|
||||
permission: PermissionCreateEntry,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
repoName?: string
|
||||
): Action {
|
||||
return {
|
||||
type: CREATE_PERMISSION_SUCCESS,
|
||||
payload: {
|
||||
permission,
|
||||
position: namespace + "/" + repoName
|
||||
position: createPermissionStateKey(namespace, repoName)
|
||||
},
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
export function createPermissionFailure(error: Error, namespace: string, repoName: string): Action {
|
||||
export function createPermissionFailure(error: Error, namespace: string, repoName?: string): Action {
|
||||
return {
|
||||
type: CREATE_PERMISSION_FAILURE,
|
||||
payload: error,
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
export function createPermissionReset(namespace: string, repoName: string) {
|
||||
export function createPermissionReset(namespace: string, repoName?: string) {
|
||||
return {
|
||||
type: CREATE_PERMISSION_RESET,
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
// delete permission
|
||||
|
||||
export function deletePermission(permission: Permission, namespace: string, repoName: string, callback?: () => void) {
|
||||
export function deletePermission(permission: Permission, namespace: string, repoName?: string, callback?: () => void) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(deletePermissionPending(permission, namespace, repoName));
|
||||
return apiClient
|
||||
@@ -339,7 +339,7 @@ export function deletePermission(permission: Permission, namespace: string, repo
|
||||
};
|
||||
}
|
||||
|
||||
export function deletePermissionPending(permission: Permission, namespace: string, repoName: string): Action {
|
||||
export function deletePermissionPending(permission: Permission, namespace: string, repoName?: string): Action {
|
||||
return {
|
||||
type: DELETE_PERMISSION_PENDING,
|
||||
payload: permission,
|
||||
@@ -347,12 +347,12 @@ export function deletePermissionPending(permission: Permission, namespace: strin
|
||||
};
|
||||
}
|
||||
|
||||
export function deletePermissionSuccess(permission: Permission, namespace: string, repoName: string): Action {
|
||||
export function deletePermissionSuccess(permission: Permission, namespace: string, repoName?: string): Action {
|
||||
return {
|
||||
type: DELETE_PERMISSION_SUCCESS,
|
||||
payload: {
|
||||
permission,
|
||||
position: namespace + "/" + repoName
|
||||
position: createPermissionStateKey(namespace, repoName)
|
||||
},
|
||||
itemId: createItemId(permission, namespace, repoName)
|
||||
};
|
||||
@@ -361,7 +361,7 @@ export function deletePermissionSuccess(permission: Permission, namespace: strin
|
||||
export function deletePermissionFailure(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
repoName?: string,
|
||||
error: Error
|
||||
): Action {
|
||||
return {
|
||||
@@ -374,14 +374,14 @@ export function deletePermissionFailure(
|
||||
};
|
||||
}
|
||||
|
||||
export function deletePermissionReset(namespace: string, repoName: string) {
|
||||
export function deletePermissionReset(namespace: string, repoName?: string) {
|
||||
return {
|
||||
type: DELETE_PERMISSION_RESET,
|
||||
payload: {
|
||||
namespace,
|
||||
repoName
|
||||
},
|
||||
itemId: namespace + "/" + repoName
|
||||
itemId: createPermissionStateKey(namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -398,9 +398,9 @@ function deletePermissionFromState(oldPermissions: PermissionCollection, permiss
|
||||
return newPermission;
|
||||
}
|
||||
|
||||
function createItemId(permission: Permission, namespace: string, repoName: string) {
|
||||
function createItemId(permission: Permission, namespace: string, repoName?: string) {
|
||||
const groupPermission = permission.groupPermission ? "@" : "";
|
||||
return namespace + "/" + repoName + "/" + groupPermission + permission.name;
|
||||
return createPermissionStateKey(namespace, repoName) + "/" + groupPermission + permission.name;
|
||||
}
|
||||
|
||||
// reducer
|
||||
@@ -427,7 +427,7 @@ export default function reducer(
|
||||
createPermission: !!action.payload._links.create
|
||||
}
|
||||
};
|
||||
case MODIFY_PERMISSION_SUCCESS:
|
||||
case MODIFY_PERMISSION_SUCCESS: {
|
||||
const positionOfPermission = action.payload.position;
|
||||
const newPermission = newPermissions(state[action.payload.position].entries, action.payload.permission);
|
||||
return {
|
||||
@@ -437,7 +437,8 @@ export default function reducer(
|
||||
entries: newPermission
|
||||
}
|
||||
};
|
||||
case CREATE_PERMISSION_SUCCESS:
|
||||
}
|
||||
case CREATE_PERMISSION_SUCCESS: {
|
||||
// return state;
|
||||
const position = action.payload.position;
|
||||
const permissions = state[action.payload.position].entries;
|
||||
@@ -449,9 +450,10 @@ export default function reducer(
|
||||
entries: permissions
|
||||
}
|
||||
};
|
||||
case DELETE_PERMISSION_SUCCESS:
|
||||
}
|
||||
case DELETE_PERMISSION_SUCCESS: {
|
||||
const permissionPosition = action.payload.position;
|
||||
const new_Permissions = deletePermissionFromState(
|
||||
const newPermissions = deletePermissionFromState(
|
||||
state[action.payload.position].entries,
|
||||
action.payload.permission
|
||||
);
|
||||
@@ -459,9 +461,10 @@ export default function reducer(
|
||||
...state,
|
||||
[permissionPosition]: {
|
||||
...state[permissionPosition],
|
||||
entries: new_Permissions
|
||||
entries: newPermissions
|
||||
}
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -490,9 +493,9 @@ function available(state: object) {
|
||||
return {};
|
||||
}
|
||||
|
||||
export function getPermissionsOfRepo(state: object, namespace: string, repoName: string) {
|
||||
if (state.permissions && state.permissions[namespace + "/" + repoName]) {
|
||||
return state.permissions[namespace + "/" + repoName].entries;
|
||||
export function getPermissionsOfRepo(state: object, namespace: string, repoName?: string) {
|
||||
if (state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)]) {
|
||||
return state.permissions[createPermissionStateKey(namespace, repoName)].entries;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,52 +503,62 @@ export function isFetchAvailablePermissionsPending(state: object) {
|
||||
return isPending(state, FETCH_AVAILABLE, "available");
|
||||
}
|
||||
|
||||
export function isFetchPermissionsPending(state: object, namespace: string, repoName: string) {
|
||||
return isPending(state, FETCH_PERMISSIONS, namespace + "/" + repoName);
|
||||
export function isFetchPermissionsPending(state: object, namespace: string, repoName?: string) {
|
||||
return isPending(state, FETCH_PERMISSIONS, createPermissionStateKey(namespace, repoName));
|
||||
}
|
||||
|
||||
export function getFetchAvailablePermissionsFailure(state: object) {
|
||||
return getFailure(state, FETCH_AVAILABLE, "available");
|
||||
}
|
||||
|
||||
export function getFetchPermissionsFailure(state: object, namespace: string, repoName: string) {
|
||||
return getFailure(state, FETCH_PERMISSIONS, namespace + "/" + repoName);
|
||||
export function getFetchPermissionsFailure(state: object, namespace: string, repoName?: string) {
|
||||
return getFailure(state, FETCH_PERMISSIONS, createPermissionStateKey(namespace, repoName));
|
||||
}
|
||||
|
||||
export function isModifyPermissionPending(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
return isPending(state, MODIFY_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
export function isModifyPermissionPending(state: object, namespace: string, repoName?: string, permission: Permission) {
|
||||
return isPending(state, MODIFY_PERMISSION, createItemId(permission, createPermissionStateKey(namespace, repoName)));
|
||||
}
|
||||
|
||||
export function getModifyPermissionFailure(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
return getFailure(state, MODIFY_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
export function getModifyPermissionFailure(
|
||||
state: object,
|
||||
namespace: string,
|
||||
repoName?: string,
|
||||
permission: Permission
|
||||
) {
|
||||
return getFailure(state, MODIFY_PERMISSION, createItemId(permission, createPermissionStateKey(namespace, repoName)));
|
||||
}
|
||||
|
||||
export function hasCreatePermission(state: object, namespace: string, repoName: string) {
|
||||
if (state.permissions && state.permissions[namespace + "/" + repoName])
|
||||
return state.permissions[namespace + "/" + repoName].createPermission;
|
||||
export function hasCreatePermission(state: object, namespace: string, repoName?: string) {
|
||||
if (state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)])
|
||||
return state.permissions[createPermissionStateKey(namespace, repoName)].createPermission;
|
||||
else return null;
|
||||
}
|
||||
|
||||
export function isCreatePermissionPending(state: object, namespace: string, repoName: string) {
|
||||
return isPending(state, CREATE_PERMISSION, namespace + "/" + repoName);
|
||||
export function isCreatePermissionPending(state: object, namespace: string, repoName?: string) {
|
||||
return isPending(state, CREATE_PERMISSION, createPermissionStateKey(namespace, repoName));
|
||||
}
|
||||
|
||||
export function getCreatePermissionFailure(state: object, namespace: string, repoName: string) {
|
||||
return getFailure(state, CREATE_PERMISSION, namespace + "/" + repoName);
|
||||
export function getCreatePermissionFailure(state: object, namespace: string, repoName?: string) {
|
||||
return getFailure(state, CREATE_PERMISSION, createPermissionStateKey(namespace, repoName));
|
||||
}
|
||||
|
||||
export function isDeletePermissionPending(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
export function isDeletePermissionPending(state: object, namespace: string, repoName?: string, permission: Permission) {
|
||||
return isPending(state, DELETE_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
}
|
||||
|
||||
export function getDeletePermissionFailure(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
export function getDeletePermissionFailure(
|
||||
state: object,
|
||||
namespace: string,
|
||||
repoName?: string,
|
||||
permission: Permission
|
||||
) {
|
||||
return getFailure(state, DELETE_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
}
|
||||
|
||||
export function getDeletePermissionsFailure(state: object, namespace: string, repoName: string) {
|
||||
export function getDeletePermissionsFailure(state: object, namespace: string, repoName?: string) {
|
||||
const permissions =
|
||||
state.permissions && state.permissions[namespace + "/" + repoName]
|
||||
? state.permissions[namespace + "/" + repoName].entries
|
||||
state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)]
|
||||
? state.permissions[createPermissionStateKey(namespace, repoName)].entries
|
||||
: null;
|
||||
if (permissions == null) return undefined;
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
@@ -556,10 +569,10 @@ export function getDeletePermissionsFailure(state: object, namespace: string, re
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getModifyPermissionsFailure(state: object, namespace: string, repoName: string) {
|
||||
export function getModifyPermissionsFailure(state: object, namespace: string, repoName?: string) {
|
||||
const permissions =
|
||||
state.permissions && state.permissions[namespace + "/" + repoName]
|
||||
? state.permissions[namespace + "/" + repoName].entries
|
||||
state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)]
|
||||
? state.permissions[createPermissionStateKey(namespace, repoName)].entries
|
||||
: null;
|
||||
if (permissions == null) return undefined;
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
@@ -570,6 +583,10 @@ export function getModifyPermissionsFailure(state: object, namespace: string, re
|
||||
return null;
|
||||
}
|
||||
|
||||
function createPermissionStateKey(namespace: string, repoName?: string) {
|
||||
return namespace + (repoName ? "/" + repoName : "");
|
||||
}
|
||||
|
||||
export function findVerbsForRole(availableRepositoryRoles: RepositoryRole[], roleName: string) {
|
||||
const matchingRole = availableRepositoryRoles.find(role => roleName === role.name);
|
||||
if (matchingRole) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import { Redirect, Switch, useLocation, useRouteMatch, Route } from "react-route
|
||||
import { apiClient, ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import TagView from "../components/TagView";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository;
|
||||
@@ -65,17 +66,6 @@ const TagRoot: FC<Props> = ({ repository, baseUrl }) => {
|
||||
}
|
||||
}, [tags]);
|
||||
|
||||
const stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
const matchedUrl = () => {
|
||||
return stripEndingSlash(match.url);
|
||||
};
|
||||
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
}
|
||||
@@ -84,7 +74,7 @@ const TagRoot: FC<Props> = ({ repository, baseUrl }) => {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const url = matchedUrl();
|
||||
const url = urls.matchedUrlFromMatch(match);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
|
||||
@@ -47,6 +47,7 @@ import { mustGetUsersLink } from "../../modules/indexResource";
|
||||
import SetUserPassword from "../components/SetUserPassword";
|
||||
import SetPermissions from "../../permissions/components/SetPermissions";
|
||||
import SetPublicKeys from "../components/publicKeys/SetPublicKeys";
|
||||
import { urls } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = RouteComponentProps &
|
||||
WithTranslation & {
|
||||
@@ -65,17 +66,6 @@ class SingleUser extends React.Component<Props> {
|
||||
this.props.fetchUserByName(this.props.usersLink, this.props.name);
|
||||
}
|
||||
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
return url.substring(0, url.length - 2);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
matchedUrl = () => {
|
||||
return this.stripEndingSlash(this.props.match.url);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t, loading, error, user } = this.props;
|
||||
|
||||
@@ -87,7 +77,7 @@ class SingleUser extends React.Component<Props> {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const url = urls.matchedUrl(this.props);
|
||||
|
||||
const extensionProps = {
|
||||
user,
|
||||
|
||||
Reference in New Issue
Block a user