Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Remove Collaboration #488

Merged
merged 10 commits into from
Aug 28, 2020
7 changes: 5 additions & 2 deletions src/client/components/forms/userCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import * as bootstrap from 'react-bootstrap';
import {trim, uniqBy} from 'lodash';
import CustomInput from '../../input';
import DeleteCollectionModal from '../pages/parts/delete-collection-modal';
import DeleteOrRemoveCollaborationModal from '../pages/parts/delete-or-remove-collaboration-modal';
import EntitySearchFieldOption from '../../entity-editor/common/entity-search-field-option';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import PropTypes from 'prop-types';
Expand Down Expand Up @@ -172,9 +172,10 @@ class UserCollectionForm extends React.Component {
return (
<div>
<h1>Create your collection</h1>
<DeleteCollectionModal
<DeleteOrRemoveCollaborationModal
collection={this.props.collection}
show={this.state.showModal}
userId={this.props.collection.ownerId}
onCloseModal={this.handleCloseModal}
/>
<div>
Expand Down Expand Up @@ -299,6 +300,7 @@ UserCollectionForm.propTypes = {
id: PropTypes.string,
items: PropTypes.array,
name: PropTypes.string,
ownerId: PropTypes.number,
public: PropTypes.bool
})
};
Expand All @@ -310,6 +312,7 @@ UserCollectionForm.defaultProps = {
id: null,
items: [],
name: null,
ownerId: null,
public: false
}
};
Expand Down
29 changes: 22 additions & 7 deletions src/client/components/pages/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
*/

import * as bootstrap from 'react-bootstrap';
import {ENTITY_TYPE_ICONS, genEntityIconHTMLElement} from '../../helpers/entity';
import AddEntityToCollectionModal from './parts/add-entity-to-collection-modal';
import AuthorTable from './entities/author-table';
import DeleteCollectionModal from './parts/delete-collection-modal';
import DeleteOrRemoveCollaborationModal from './parts/delete-or-remove-collaboration-modal';
import {ENTITY_TYPE_ICONS} from '../../helpers/entity';
import EditionGroupTable from './entities/editionGroup-table';
import EditionTable from './entities/edition-table';
import EntityImage from './entities/image';
Expand Down Expand Up @@ -232,13 +232,15 @@ class CollectionPage extends React.Component {
onToggleRow: this.toggleRow,
selectedEntities: this.state.selectedEntities,
showAdd: false,
showCheckboxes: this.props.isOwner || this.props.isCollaborator
showCheckboxes: Boolean(this.props.isOwner) || Boolean(this.props.isCollaborator)
};
return (
<div>
<DeleteCollectionModal
<DeleteOrRemoveCollaborationModal
collection={this.props.collection}
isDelete={this.props.isOwner}
show={this.state.showDeleteModal}
userId={this.props.userId}
onCloseModal={this.handleCloseDeleteModal}
/>
<AddEntityToCollectionModal
Expand Down Expand Up @@ -275,7 +277,7 @@ class CollectionPage extends React.Component {
</Button> : null
}
{
(this.props.isCollaborator || this.props.isOwner) && this.props.entities.length ?
(this.props.isCollaborator || this.props.isOwner) && this.state.entities.length ?
<Button
bsSize="small"
bsStyle="danger"
Expand Down Expand Up @@ -310,6 +312,17 @@ class CollectionPage extends React.Component {
<FontAwesomeIcon icon="trash-alt"/>&nbsp;Delete collection
</Button> : null
}
{
this.props.isCollaborator ?
<Button
bsSize="small"
bsStyle="warning"
title="Remove yourself as a collaborator"
onClick={this.handleShowDeleteModal}
>
<FontAwesomeIcon icon="times-circle"/>&nbsp;Stop collaboration
</Button> : null
}
</div>
<div id="pageWithPagination">
<PagerElement
Expand All @@ -336,15 +349,17 @@ CollectionPage.propTypes = {
isCollaborator: PropTypes.bool,
isOwner: PropTypes.bool,
nextEnabled: PropTypes.bool.isRequired,
size: PropTypes.number
size: PropTypes.number,
userId: PropTypes.number
};
CollectionPage.defaultProps = {
entities: [],
from: 0,
isCollaborator: false,
isOwner: false,
showCheckboxes: false,
size: 20
size: 20,
userId: null
};

export default CollectionPage;
84 changes: 0 additions & 84 deletions src/client/components/pages/parts/delete-collection-modal.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import * as bootstrap from 'react-bootstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import PropTypes from 'prop-types';
import React from 'react';
import request from 'superagent';


const {Alert, Button, Modal} = bootstrap;

class DeleteOrRemoveCollaborationModal extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null
};

this.handleSubmit = this.handleSubmit.bind(this);
}

handleSubmit() {
request.post(this.postUrl)
.send(this.postData)
.then((res) => {
window.location.href = `/editor/${this.props.userId}/collections`;
}, (error) => {
this.setState({
error: 'Something went wrong! Please try again later'
});
});
}

render() {
const {collection} = this.props;
// eslint-disable-next-line one-var
let modalBody, modalTitle, submitButton;
if (this.props.isDelete) {
this.postUrl = `/collection/${collection.id}/delete/handler`;
this.postData = {};
modalTitle = 'Confirm deletion';
modalBody = (
<Alert bsStyle="danger">
<h4>
<FontAwesomeIcon icon="exclamation-triangle"/>&nbsp;
You’re about to delete the Collection: {collection.name}.
</h4>
<p>
Make sure you actually want to delete this Collection <br/>
There is no way to undo this.
</p>
</Alert>
);
submitButton = (
<Button bsStyle="danger" onClick={this.handleSubmit}>
<FontAwesomeIcon icon="trash-alt"/> Delete
</Button>
);
}
else {
// loggedInUser must be collaborator here
this.postUrl = `/collection/${collection.id}/collaborator/remove`;
this.postData = {collaboratorIds: [this.props.userId]};
modalTitle = 'Remove yourself as a collaborator';
modalBody = (
<Alert bsStyle="warning">
<h4>
<FontAwesomeIcon icon="exclamation-triangle"/>&nbsp;
You’re about to remove yourself as a collaborator of Collection: {collection.name}.
</h4>
<p>
Are you sure you want to proceed ? You won’t be able to undo this yourself
and will need to ask the collection&apos;s owner to add you again.
</p>
</Alert>
);
submitButton = (
<Button bsStyle="warning" onClick={this.handleSubmit}>
<FontAwesomeIcon icon="times-circle"/> Stop collaboration
</Button>
);
}

let errorComponent = null;
if (this.state.error) {
errorComponent =
<Alert bsStyle="danger">{this.state.error}</Alert>;
}

return (
<Modal
show={this.props.show}
onHide={this.props.onCloseModal}
>
<Modal.Header closeButton>
<Modal.Title>{modalTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>
{modalBody}
{errorComponent}
MonkeyDo marked this conversation as resolved.
Show resolved Hide resolved
</Modal.Body>
<Modal.Footer>
<Button bsStyle="info" onClick={this.props.onCloseModal}>
Cancel
</Button>
{submitButton}
</Modal.Footer>
</Modal>
);
}
}


DeleteOrRemoveCollaborationModal.displayName = 'DeleteOrRemoveCollaborationModal';
DeleteOrRemoveCollaborationModal.propTypes = {
collection: PropTypes.object.isRequired,
isDelete: PropTypes.bool,
onCloseModal: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired,
userId: PropTypes.number.isRequired
};
DeleteOrRemoveCollaborationModal.defaultProps = {
isDelete: true
};

export default DeleteOrRemoveCollaborationModal;
17 changes: 17 additions & 0 deletions src/server/helpers/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,20 @@ export function validateBBIDsForCollectionRemove(req, res, next) {

return next();
}

export function validateCollaboratorIdsForCollectionRemove(req, res, next) {
const {collaboratorIds = []} = req.body;
if (!collaboratorIds.length) {
return next(new error.BadRequestError('CollaboratorIds array is empty'));
}
const {collection} = res.locals;
for (let i = 0; i < collaboratorIds.length; i++) {
const collaboratorId = collaboratorIds[i];
const isCollaborator = collection.collaborators.find(collaborator => collaborator.id === collaboratorId);
if (!isCollaborator) {
return next(new error.BadRequestError(`User ${collaboratorId} is not a collaborator of collection ${collection.id}`, req));
}
}

return next();
}
Loading