Skip to content
3 changes: 2 additions & 1 deletion api/app/events/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,9 @@ def post(self):
args = self.req_parser.parse_args()

user_id = g.current_user["id"]
event_id = request.args['id']
current_user = user_repository.get_by_id(user_id)
if not current_user.is_admin:
if not (current_user.is_admin or current_user.is_event_response_viewer(event_id) or current_user.is_event_response_editor(event_id)):
return FORBIDDEN

if event_repository.exists_by_key(args['key']):
Expand Down
19 changes: 19 additions & 0 deletions api/app/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(self,
self.deleted_datetime_utc = None
self.verified_email = False
self.agree_to_policy()


@property
def full_name(self):
Expand Down Expand Up @@ -103,7 +104,25 @@ def _has_admin_role(self, event_id, admin_role_name):
return True

return False

def _has_read_only_role(self, event_id):
if self.event_roles is None:
return False
for event_role in self.event_roles:
if self.is_admin and event_role.event_id == event_id and (event_role.role == "read_only" or event_role.role == "response_viewer" or event_role.role == "response_editor"):
return True

return False

def is_event_admin(self, event_id):
return self._has_admin_role(event_id, 'admin')

def is_event_response_viewer(self, event_id):
return self._has_read_only_role(event_id, 'response_viewer')

def is_event_response_editor(self, event_id):
return self._has_read_only_role(event_id, 'response_editor')

def is_event_admin(self, event_id):
return self._has_admin_role(event_id, 'admin')

Expand Down
22 changes: 15 additions & 7 deletions webapp/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import ReactGA from "react-ga";
import "./App.css";
import history from "./History";

import { isEventAdmin, isRegistrationAdmin, isRegistrationVolunteer, isEventReviewer } from "./utils/user";
import { isEventAdmin, isEventResponseViewerOnly, isEventResponseEditorOnly, isRegistrationAdmin, isRegistrationVolunteer, isEventReviewer } from "./utils/user";
import { withTranslation } from 'react-i18next';
import { userService } from "./services/user";

Expand Down Expand Up @@ -60,7 +60,9 @@ class EventNav extends Component {
} id="eventNavbar">

<ul className="navbar-nav">
{this.props.user &&
{!isEventResponseViewerOnly(this.props.user, this.props.event) &&
!isEventResponseEditorOnly(this.props.user, this.props.event) &&
this.props.user &&
this.props.event &&
this.props.event.is_application_open && (
<li className="nav-item">
Expand All @@ -74,7 +76,9 @@ class EventNav extends Component {
</NavLink>
</li>
)}
{this.props.user && this.props.event && this.props.event.is_offer_open && (
{!isEventResponseViewerOnly(this.props.user, this.props.event) &&
!isEventResponseEditorOnly(this.props.user, this.props.event) &&
this.props.user && this.props.event && this.props.event.is_offer_open && (
<li className="nav-item">
<NavLink
to={`/${this.props.eventKey}/offer`}
Expand All @@ -86,7 +90,9 @@ class EventNav extends Component {
</NavLink>
</li>
)}
{this.props.user &&
{!isEventResponseViewerOnly(this.props.user, this.props.event) &&
!isEventResponseEditorOnly(this.props.user, this.props.event) &&
this.props.user &&
(
<li className="nav-item dropdown ">
<div
Expand Down Expand Up @@ -132,7 +138,8 @@ class EventNav extends Component {
</div>
</li>
)}
{isEventAdmin(this.props.user, this.props.event) && (
{isEventAdmin(this.props.user, this.props.event) && !isEventResponseViewerOnly(this.props.user, this.props.event) &&
!isEventResponseEditorOnly(this.props.user, this.props.event) && (
<AdminMenu t={t} label="Event Admin">
<NavLink
to={`/${this.props.eventKey}/eventConfig`}
Expand Down Expand Up @@ -206,7 +213,7 @@ class EventNav extends Component {
{t('Review Form')}
</NavLink>
</AdminMenu>
)}
)}
{isEventReviewer(this.props.user, this.props.event) &&
this.props.event &&
this.props.event.is_review_open && (
Expand Down Expand Up @@ -239,7 +246,8 @@ class EventNav extends Component {
</div>
</li>
)}
{(isRegistrationAdmin(this.props.user, this.props.event) || isRegistrationVolunteer(this.props.user, this.props.event)) &&
{(isRegistrationAdmin(this.props.user, this.props.event) || isRegistrationVolunteer(this.props.user, this.props.event)) && !isEventResponseViewerOnly(this.props.user, this.props.event) &&
!isEventResponseEditorOnly(this.props.user, this.props.event) &&
this.props.event &&
this.props.event.is_registration_open && (
<li className="nav-item dropdown">
Expand Down
72 changes: 36 additions & 36 deletions webapp/src/pages/ResponseList/components/ResponseListComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { reviewService } from '../../../services/reviews/review.service';
import { ConfirmModal } from "react-bootstrap4-modal";
import TagSelectorDialog from '../../../components/TagSelectorDialog';
import { createColClassName } from "../../../utils/styling/styling";
import { isEventResponseViewerOnly } from '../../../utils/user';

class ResponseListComponent extends Component {
constructor(props) {
Expand Down Expand Up @@ -373,42 +374,41 @@ class ResponseListComponent extends Component {
/>
</div>
</div>

<form>
<div className="card">
<p className="h4 text-center mb-4">{t("Assign Reviewer")}</p>

<div className="row">
<p className="h6 text-center mb-3">{t("Filter the table above then enter a reviewer's email to assign them the filtered rows (the reviewer must already have a Baobab account)")}</p>

<div className="col-md-10 pr-2">
<FormTextBox
id={"newReviewEmail"}
name={'newReviewEmail'}
placeholder={t("Email")}
onChange={this.handleChange}
value={newReviewerEmail}
key={"newReviewEmail"} />
</div>
<div className="col-md-2 pr-2">
<button
className="btn btn-primary btn-block"
onClick={() => { this.assignReviewer() }}
disabled={!newReviewerEmail}>
{t("Assign")}
</button>
</div>

{reviewerAssignError && <span className="alert alert-danger">
{JSON.stringify(this.state.reviewerAssignError)}
</span>}

{reviewerAssignSuccess && <span className="alert alert-success">
<Trans i18nKey="reviewsAssigned">Assigned {{numReviewsAssigned}} reviews to {{assignedReviewerEmail}}</Trans>
</span>}
</div>
</div>
</form>

{(!isEventResponseViewerOnly(this.props.user && this.props.event) &&
<form>
<div className="card">
<p className="h4 text-center mb-4">{t("Assign Reviewer")}</p>
<div className="row">
<p className="h6 text-center mb-3">{t("Filter the table above then enter a reviewer's email to assign them the filtered rows (the reviewer must already have a Baobab account)")}</p>

<div className="col-md-10 pr-2">
<FormTextBox
id={"newReviewEmail"}
name={'newReviewEmail'}
placeholder={t("Email")}
onChange={this.handleChange}
value={newReviewerEmail}
key={"newReviewEmail"} />
</div>
<div className="col-md-2 pr-2">
<button
className="btn btn-primary btn-block"
onClick={() => { this.assignReviewer() }}
disabled={!newReviewerEmail}>
{t("Assign")}
</button>
</div>
{reviewerAssignError && <span className="alert alert-danger">
{JSON.stringify(this.state.reviewerAssignError)}
</span>}
{reviewerAssignSuccess && <span className="alert alert-success">
<Trans i18nKey="reviewsAssigned">Assigned {{numReviewsAssigned}} reviews to {{assignedReviewerEmail}}</Trans>
</span>}
</div>
</div>
</form>
)}

<TagSelectorDialog
tags={this.state.filteredTags}
Expand Down
Loading