From 0a5b85d1129f52b17c5f9fe626ab9fc0d99d920e Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Tue, 30 Apr 2024 18:25:27 +0200 Subject: [PATCH 1/8] added read only checks --- api/app/users/api.py | 1 + api/app/users/models.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/api/app/users/api.py b/api/app/users/api.py index e99685425..4a9ee0a18 100644 --- a/api/app/users/api.py +++ b/api/app/users/api.py @@ -61,6 +61,7 @@ def user_info(user, roles): 'email': user.email, 'title': user.user_title, 'is_admin': user.is_admin, + 'is_read_only': user.is_read_only, 'primary_language': user.user_primaryLanguage, 'roles': [{'event_id': event_role.event_id, 'role': event_role.role} for event_role in roles] } diff --git a/api/app/users/models.py b/api/app/users/models.py index b19977735..0660d0e1d 100644 --- a/api/app/users/models.py +++ b/api/app/users/models.py @@ -28,6 +28,7 @@ class AppUser(db.Model, UserMixin): password = db.Column(db.String(255), nullable=False) active = db.Column(db.Boolean(), nullable=False) is_admin = db.Column(db.Boolean(), nullable=False) + is_read_only = db.Column(db.Boolean(), nullable=False) # READ ONLY ACCESS is_deleted = db.Column(db.Boolean(), nullable=False) deleted_datetime_utc = db.Column(db.DateTime(), nullable=True) verified_email = db.Column(db.Boolean(), nullable=True) @@ -49,7 +50,8 @@ def __init__(self, user_title, password, organisation_id, - is_admin=False): + is_admin=False, + is_read_only=False): self.email = email self.firstname = firstname self.lastname = lastname @@ -58,6 +60,7 @@ def __init__(self, self.organisation_id = organisation_id self.active = True self.is_admin = is_admin + self.is_read_only = is_read_only self.is_deleted = False self.deleted_datetime_utc = None self.verified_email = False @@ -103,6 +106,19 @@ def _has_admin_role(self, event_id, admin_role_name): return True return False + + def _has_read_only_role(self, event_id): + if self.is_read_only: + return True + + if self.event_roles is None: + return False + + for event_role in self.event_roles: + if event_role.event_id == event_id and event_role.role == "read_only": + return True + + return False def is_event_admin(self, event_id): return self._has_admin_role(event_id, 'admin') From 5798f82b48a0cf7116369b0b129ed947fca20053 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Thu, 2 May 2024 09:33:31 +0200 Subject: [PATCH 2/8] minor changes --- api/app/events/api.py | 2 +- api/app/users/api.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/app/events/api.py b/api/app/events/api.py index 2ced3ef5d..7b5bbd9fd 100644 --- a/api/app/events/api.py +++ b/api/app/events/api.py @@ -246,7 +246,7 @@ def post(self): user_id = g.current_user["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_read_only): return FORBIDDEN if event_repository.exists_by_key(args['key']): diff --git a/api/app/users/api.py b/api/app/users/api.py index 4a9ee0a18..caff1441f 100644 --- a/api/app/users/api.py +++ b/api/app/users/api.py @@ -292,7 +292,7 @@ def get(self): current_user = user_repository.get_by_id(current_user_id) - if current_user.is_admin: + if current_user.is_admin or current_user.is_read_only: user = user_repository.get_by_id_with_response(user_id) if user is None: return USER_NOT_FOUND From d5c166f28f9a2d79bb9d63a0b4801c945c6e4a53 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Tue, 7 May 2024 10:57:03 +0200 Subject: [PATCH 3/8] Implemented read_only checks --- webapp/src/App.js | 70 ++- .../components/EventConfigComponent.js | 544 ++++++++++++------ webapp/src/pages/eventHome/EventHome.js | 2 +- webapp/src/pages/home/Home.js | 2 +- webapp/src/utils/user.js | 13 + 5 files changed, 442 insertions(+), 189 deletions(-) diff --git a/webapp/src/App.js b/webapp/src/App.js index 2d2ad9acb..c09b4f2f5 100755 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -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, isEventReadOnly, isRegistrationAdmin, isRegistrationVolunteer, isEventReviewer } from "./utils/user"; import { withTranslation } from 'react-i18next'; import { userService } from "./services/user"; @@ -200,6 +200,74 @@ class EventNav extends Component { )} + {isEventReadOnly(this.props.user, this.props.event) && ( + + + {t('Edit Event Details')} + + + {t('Review Assignment')} + + + {t('Invited Guests')} + + + {t('Response List')} + + + {t('Review Dashboard')} + + + {t('Configure Tags')} + + + {t('Offers')} + +
Form Settings
+ + {t('Application Form')} + + + {t('Review Form')} + +
+ )} {isEventReviewer(this.props.user, this.props.event) && this.props.event && this.props.event.is_review_open && ( diff --git a/webapp/src/pages/eventConfig/components/EventConfigComponent.js b/webapp/src/pages/eventConfig/components/EventConfigComponent.js index 30af8c130..e433208f8 100644 --- a/webapp/src/pages/eventConfig/components/EventConfigComponent.js +++ b/webapp/src/pages/eventConfig/components/EventConfigComponent.js @@ -450,225 +450,397 @@ class EventConfigComponent extends Component { } const t = this.props.t; + if (this.props.user.is_read_only){ + return ( +
+
+
+
+ - return ( -
-
- -
- - -
- +
+ +
-
+ {this.props.organisation.languages.map((lang) => ( +
+ +
+ +
+
+ ))} - {this.props.organisation.languages.map((lang) => ( -
+
- this.updateEventTextField("name", e, lang.code)} - value={updatedEvent.name[lang.code]} +
- ))} - -
- - -
- -
-
- -
- - -
- -
-
- -
- - -
- this.updateEventTextField("key", e)} - value={updatedEvent.key} - /> -
-
- {this.props.organisation.languages.map((lang) => ( -
+
- this.updateEventTextField("description", e, lang.code)} - value={updatedEvent.description[lang.code]} +
- ))} - -
- - -
- this.updateEventTextField("email_from", e)} - /> + +
+ + +
+ +
-
-
- - -
- this.updateEventTextField("url", e)} - /> + {this.props.organisation.languages.map((lang) => ( +
+ + +
+ +
+
+ ))} + +
+ + +
+ +
-
- {updatedEvent.event_type && this.renderDatePickerTable()} +
+ - +
+ +
+
-
+ {updatedEvent.event_type && this.renderDatePickerTable()} -
-
- - {t("Cancel")} - -
- -
- {isNewEvent ? ( - - ) : - ( - - )} + +
+
+ {errors && showErrors && this.getErrorMessages(errors)}
+
+ );} -
- {errors && showErrors && this.getErrorMessages(errors)} + else { + return ( +
+
+
+
+ + +
+ +
+
+ + {this.props.organisation.languages.map((lang) => ( +
+ +
+ this.updateEventTextField("name", e, lang.code)} + value={updatedEvent.name[lang.code]} + /> +
+
+ ))} + +
+ + +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ this.updateEventTextField("key", e)} + value={updatedEvent.key} + /> +
+
+ + {this.props.organisation.languages.map((lang) => ( +
+ + +
+ this.updateEventTextField("description", e, lang.code)} + value={updatedEvent.description[lang.code]} + /> +
+
+ ))} + +
+ + +
+ this.updateEventTextField("email_from", e)} + /> +
+
+ +
+ + +
+ this.updateEventTextField("url", e)} + /> +
+
+ + {updatedEvent.event_type && this.renderDatePickerTable()} + +
+ +
+ +
+
+ + {t("Cancel")} + +
+ +
+ {isNewEvent ? ( + + ) : + ( + + )} +
+
+ +
+ {errors && showErrors && this.getErrorMessages(errors)} +
+
-
-
- ); + );} + } } diff --git a/webapp/src/pages/eventHome/EventHome.js b/webapp/src/pages/eventHome/EventHome.js index e4d8bd9c4..2fa1cc0ee 100755 --- a/webapp/src/pages/eventHome/EventHome.js +++ b/webapp/src/pages/eventHome/EventHome.js @@ -167,7 +167,7 @@ class EventHome extends Component { } + render={(props) => } /> } - {this.props.user && this.props.user.is_admin && + {this.props.user && this.props.user.is_admin && this.props.user.is_read_only && {this.props.t("Create New Event")} } diff --git a/webapp/src/utils/user.js b/webapp/src/utils/user.js index 1939905b2..17046fcd1 100644 --- a/webapp/src/utils/user.js +++ b/webapp/src/utils/user.js @@ -11,6 +11,19 @@ export const isEventAdmin = (user, event) => { ); }; + export const isEventReadOnly = (user, event) => { + if (!user) { + return false; + } + return ( + user.is_read_only || + (user.roles && + user.roles.some( + r => r.role === "read_only" && event && r.event_id === event.id + )) + ); + }; + export const isRegistrationAdmin = (user, event) => { if (!user) { return false; From d673c8952f4ab97da400e99ae34fe9d2fb386ff5 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Wed, 22 May 2024 08:24:18 +0200 Subject: [PATCH 4/8] Exposing only response list for read only role --- webapp/src/App.js | 83 +------ webapp/src/pages/eventHome/EventHome.js | 313 ++++++++++++------------ webapp/src/pages/home/Home.js | 15 +- 3 files changed, 180 insertions(+), 231 deletions(-) diff --git a/webapp/src/App.js b/webapp/src/App.js index c09b4f2f5..0c0bf1579 100755 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -60,7 +60,8 @@ class EventNav extends Component { } id="eventNavbar">
    - {this.props.user && + {!isEventReadOnly(this.props.user, this.props.event) && + this.props.user && this.props.event && this.props.event.is_application_open && (
  • @@ -74,7 +75,8 @@ class EventNav extends Component {
  • )} - {this.props.user && this.props.event && this.props.event.is_offer_open && ( + {!isEventReadOnly(this.props.user, this.props.event) && + this.props.user && this.props.event && this.props.event.is_offer_open && (
  • )} - {this.props.user && + {!isEventReadOnly(this.props.user, this.props.event) && + this.props.user && (
  • )} - {isEventAdmin(this.props.user, this.props.event) && ( + {isEventAdmin(this.props.user, this.props.event) && !isEventReadOnly(this.props.user, this.props.event) && ( - )} - {isEventReadOnly(this.props.user, this.props.event) && ( - - - {t('Edit Event Details')} - - - {t('Review Assignment')} - - - {t('Invited Guests')} - - - {t('Response List')} - - - {t('Review Dashboard')} - - - {t('Configure Tags')} - - - {t('Offers')} - -
    Form Settings
    - - {t('Application Form')} - - - {t('Review Form')} - -
    - )} + )} {isEventReviewer(this.props.user, this.props.event) && this.props.event && this.props.event.is_review_open && ( @@ -300,7 +235,7 @@ class EventNav extends Component {
)} - {(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)) && !isEventReadOnly(this.props.user, this.props.event) && this.props.event && this.props.event.is_registration_open && (
  • diff --git a/webapp/src/pages/eventHome/EventHome.js b/webapp/src/pages/eventHome/EventHome.js index 2fa1cc0ee..e93c5418f 100755 --- a/webapp/src/pages/eventHome/EventHome.js +++ b/webapp/src/pages/eventHome/EventHome.js @@ -23,7 +23,7 @@ import Offer from "../offer"; import OfferAdmin from "../offerAdmin"; import { InvoiceList } from "../invoices"; import EventStatus from "../../components/EventStatus"; -import { isEventAdmin } from "../../utils/user"; +import { isEventAdmin, isEventReadOnly } from "../../utils/user"; import ResponseList from "../ResponseList/ResponseList"; import ResponsePage from "../ResponsePage/ResponsePage"; import ReviewDashboard from "../reviewDashboard"; @@ -48,7 +48,7 @@ class EventInfo extends Component {

    {event.description}

    - {isEventAdmin(this.props.user, this.props.event) + {isEventAdmin(this.props.user, this.props.event) && }
    ); @@ -127,159 +127,170 @@ class EventHome extends Component { ); } - return ( -
    - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> + if (!isEventReadOnly(this.props.user, this.props.event)) { + return ( +
    + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> } - /> - } - /> - } - /> - } - /> - } - /> + path={`${match.path}/eventAttendance`} + render={(props) => } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + +
    + ); + } + else{ + return ( } + exact + path={`${match.path}/`} + render={(props) => } /> - -
    - ); + ); + } } } diff --git a/webapp/src/pages/home/Home.js b/webapp/src/pages/home/Home.js index ac9e29eca..4f2c813e2 100644 --- a/webapp/src/pages/home/Home.js +++ b/webapp/src/pages/home/Home.js @@ -5,6 +5,7 @@ import { eventService } from "../../services/events/events.service"; import { organisationService } from "../../services/organisation/organisation.service"; import EventStatus from "../../components/EventStatus"; import { withTranslation } from 'react-i18next'; +import { isEventReadOnly } from '../../utils/user'; class Home extends Component { @@ -63,12 +64,14 @@ class Home extends Component { } statusDisplay(e) { - if (e.event_type === 'JOURNAL') { - return ; + if (!isEventReadOnly(this.props.user, e)) { + if (e.event_type === 'JOURNAL') { + return ; + } + else { + return ; + } } - else { - return ; - } } dateDisplay = (e) => { @@ -131,7 +134,7 @@ class Home extends Component {
  • } - {this.props.user && this.props.user.is_admin && this.props.user.is_read_only && + {this.props.user && this.props.user.is_admin && !isEventReadOnly(this.props.user, this.props.event) && {this.props.t("Create New Event")} } From 889e4029e2e8e44cad0bfd6af5fa6683ee6164a4 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Thu, 23 May 2024 08:28:30 +0200 Subject: [PATCH 5/8] set read only in users\model --- api/app/users/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/app/users/models.py b/api/app/users/models.py index 0660d0e1d..4c9bd2f09 100644 --- a/api/app/users/models.py +++ b/api/app/users/models.py @@ -61,14 +61,20 @@ def __init__(self, self.active = True self.is_admin = is_admin self.is_read_only = is_read_only + self.set_read_only() self.is_deleted = False self.deleted_datetime_utc = None self.verified_email = False self.agree_to_policy() + @property def full_name(self): return f"{self.firstname} {self.lastname}" + + def set_read_only(self): + if self.is_read_only: + self.is_admin = True def set_password(self, password): self.password = bcrypt.generate_password_hash(password).decode('utf-8') From a19d1fdcea72c4c65feb3b1f7afca9a5083f975a Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Thu, 30 May 2024 14:12:31 +0200 Subject: [PATCH 6/8] bug fix --- webapp/src/pages/eventHome/EventHome.js | 79 ++++++++++++------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/webapp/src/pages/eventHome/EventHome.js b/webapp/src/pages/eventHome/EventHome.js index f232eef15..ff8e62f50 100755 --- a/webapp/src/pages/eventHome/EventHome.js +++ b/webapp/src/pages/eventHome/EventHome.js @@ -263,11 +263,6 @@ class EventHome extends Component { languages={organisation && organisation.languages} />} /> - } - /> } - /> - } - /> - } - /> - } - /> - } - /> - + } - /> + path={`${match.path}/responsePage/:id`} + render={(props) => } + /> + } + /> + } + /> + } + /> + } + /> + } + /> +
    ); } } From 4f5e95759863e1c651d9ab2b95e06f49ef4b6ca1 Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Fri, 7 Jun 2024 08:29:06 +0200 Subject: [PATCH 7/8] added response viewer and editor --- api/app/events/api.py | 3 ++- api/app/users/api.py | 3 +-- api/app/users/models.py | 25 ++++++++----------- webapp/src/App.js | 17 ++++++++----- .../components/EventConfigComponent.js | 3 ++- webapp/src/pages/eventHome/EventHome.js | 4 +-- webapp/src/pages/home/Home.js | 9 ++++--- webapp/src/utils/user.js | 19 +++++++++++--- 8 files changed, 51 insertions(+), 32 deletions(-) diff --git a/api/app/events/api.py b/api/app/events/api.py index 1bfc68a69..901916c4a 100644 --- a/api/app/events/api.py +++ b/api/app/events/api.py @@ -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 or current_user.is_read_only): + 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']): diff --git a/api/app/users/api.py b/api/app/users/api.py index caff1441f..e99685425 100644 --- a/api/app/users/api.py +++ b/api/app/users/api.py @@ -61,7 +61,6 @@ def user_info(user, roles): 'email': user.email, 'title': user.user_title, 'is_admin': user.is_admin, - 'is_read_only': user.is_read_only, 'primary_language': user.user_primaryLanguage, 'roles': [{'event_id': event_role.event_id, 'role': event_role.role} for event_role in roles] } @@ -292,7 +291,7 @@ def get(self): current_user = user_repository.get_by_id(current_user_id) - if current_user.is_admin or current_user.is_read_only: + if current_user.is_admin: user = user_repository.get_by_id_with_response(user_id) if user is None: return USER_NOT_FOUND diff --git a/api/app/users/models.py b/api/app/users/models.py index 4c9bd2f09..94f19d28b 100644 --- a/api/app/users/models.py +++ b/api/app/users/models.py @@ -28,7 +28,6 @@ class AppUser(db.Model, UserMixin): password = db.Column(db.String(255), nullable=False) active = db.Column(db.Boolean(), nullable=False) is_admin = db.Column(db.Boolean(), nullable=False) - is_read_only = db.Column(db.Boolean(), nullable=False) # READ ONLY ACCESS is_deleted = db.Column(db.Boolean(), nullable=False) deleted_datetime_utc = db.Column(db.DateTime(), nullable=True) verified_email = db.Column(db.Boolean(), nullable=True) @@ -50,8 +49,7 @@ def __init__(self, user_title, password, organisation_id, - is_admin=False, - is_read_only=False): + is_admin=False): self.email = email self.firstname = firstname self.lastname = lastname @@ -60,8 +58,6 @@ def __init__(self, self.organisation_id = organisation_id self.active = True self.is_admin = is_admin - self.is_read_only = is_read_only - self.set_read_only() self.is_deleted = False self.deleted_datetime_utc = None self.verified_email = False @@ -71,10 +67,6 @@ def __init__(self, @property def full_name(self): return f"{self.firstname} {self.lastname}" - - def set_read_only(self): - if self.is_read_only: - self.is_admin = True def set_password(self, password): self.password = bcrypt.generate_password_hash(password).decode('utf-8') @@ -114,18 +106,23 @@ def _has_admin_role(self, event_id, admin_role_name): return False def _has_read_only_role(self, event_id): - if self.is_read_only: - return True - if self.event_roles is None: return False - for event_role in self.event_roles: - if event_role.event_id == event_id and event_role.role == "read_only": + 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') diff --git a/webapp/src/App.js b/webapp/src/App.js index 7376996b9..d532a408f 100755 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -22,7 +22,7 @@ import ReactGA from "react-ga"; import "./App.css"; import history from "./History"; -import { isEventAdmin, isEventReadOnly, 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"; @@ -60,7 +60,8 @@ class EventNav extends Component { } id="eventNavbar">
      - {!isEventReadOnly(this.props.user, this.props.event) && + {!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 && ( @@ -75,7 +76,8 @@ class EventNav extends Component { )} - {!isEventReadOnly(this.props.user, this.props.event) && + {!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 && (
    • )} - {!isEventReadOnly(this.props.user, this.props.event) && + {!isEventResponseViewerOnly(this.props.user, this.props.event) && + !isEventResponseEditorOnly(this.props.user, this.props.event) && this.props.user && (
    • @@ -135,7 +138,8 @@ class EventNav extends Component {
    )} - {isEventAdmin(this.props.user, this.props.event) && !isEventReadOnly(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) && ( )} - {(isRegistrationAdmin(this.props.user, this.props.event) || isRegistrationVolunteer(this.props.user, this.props.event)) && !isEventReadOnly(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 && (
  • diff --git a/webapp/src/pages/eventConfig/components/EventConfigComponent.js b/webapp/src/pages/eventConfig/components/EventConfigComponent.js index e433208f8..9baa20b5d 100644 --- a/webapp/src/pages/eventConfig/components/EventConfigComponent.js +++ b/webapp/src/pages/eventConfig/components/EventConfigComponent.js @@ -7,6 +7,7 @@ import FormTextBox from "../../../components/form/FormTextBox"; import FormTextArea from "../../../components/form/FormTextArea"; import FormDate from "../../../components/form/FormDate"; import FormSelect from "../../../components/form/FormSelect"; +import { isEventResponseEditorOnly, isEventResponseViewerOnly } from "../../../utils/user"; const APPLICATION_DATES = ["application_open", "application_close"]; const REVIEW_DATES = ["review_open", "review_close"]; @@ -450,7 +451,7 @@ class EventConfigComponent extends Component { } const t = this.props.t; - if (this.props.user.is_read_only){ + if (!isEventResponseViewerOnly(this.props.user, this.props.event) || !isEventResponseEditorOnly(this.props.user, this.props.event)){ return (
    diff --git a/webapp/src/pages/eventHome/EventHome.js b/webapp/src/pages/eventHome/EventHome.js index ff8e62f50..7a876d0c6 100755 --- a/webapp/src/pages/eventHome/EventHome.js +++ b/webapp/src/pages/eventHome/EventHome.js @@ -23,7 +23,7 @@ import Offer from "../offer"; import OfferAdmin from "../offerAdmin"; import { InvoiceAdminList } from "../invoices"; import EventStatus from "../../components/EventStatus"; -import { isEventAdmin, isEventReadOnly } from "../../utils/user"; +import { isEventAdmin, isEventResponseEditorOnly, isEventResponseViewerOnly} from "../../utils/user"; import ResponseList from "../ResponseList/ResponseList"; import ResponsePage from "../ResponsePage/ResponsePage"; import ReviewDashboard from "../reviewDashboard"; @@ -127,7 +127,7 @@ class EventHome extends Component { ); } - if (!isEventReadOnly(this.props.user, this.props.event)) { + if (!isEventResponseViewerOnly(this.props.user, this.props.event) && !isEventResponseEditorOnly(this.props.user, this.props.event)) { return (
    ; } @@ -134,7 +135,9 @@ class Home extends Component {
    } - {this.props.user && this.props.user.is_admin && !isEventReadOnly(this.props.user, this.props.event) && + {this.props.user && this.props.user.is_admin && + !isEventResponseViewerOnly(this.props.user, this.props.event) && + !isEventResponseEditorOnly(this.props.user, this.props.event) && {this.props.t("Create New Event")} } diff --git a/webapp/src/utils/user.js b/webapp/src/utils/user.js index 17046fcd1..e316e2406 100644 --- a/webapp/src/utils/user.js +++ b/webapp/src/utils/user.js @@ -11,15 +11,28 @@ export const isEventAdmin = (user, event) => { ); }; - export const isEventReadOnly = (user, event) => { + export const isEventResponseViewerOnly = (user, event) => { if (!user) { return false; } return ( - user.is_read_only || + //user.is_admin && (user.roles && user.roles.some( - r => r.role === "read_only" && event && r.event_id === event.id + r => r.role === "response_viewer" && event && r.event_id === event.id + )) + ); + }; + + export const isEventResponseEditorOnly = (user, event) => { + if (!user) { + return false; + } + return ( + //user.is_admin && + (user.roles && + user.roles.some( + r => r.role === "response_editor" && event && r.event_id === event.id )) ); }; From 7ae601f2fad86adcb305e06a9ca2ad360be0751f Mon Sep 17 00:00:00 2001 From: Muhammad Umair Date: Wed, 12 Jun 2024 14:09:06 +0200 Subject: [PATCH 8/8] Response Viewer and Editor added --- .../components/ResponseListComponent.js | 72 +++---- .../components/EventConfigComponent.js | 178 +----------------- webapp/src/pages/home/Home.js | 20 +- webapp/src/utils/user.js | 6 +- 4 files changed, 51 insertions(+), 225 deletions(-) diff --git a/webapp/src/pages/ResponseList/components/ResponseListComponent.js b/webapp/src/pages/ResponseList/components/ResponseListComponent.js index e6876a66d..6435475fa 100644 --- a/webapp/src/pages/ResponseList/components/ResponseListComponent.js +++ b/webapp/src/pages/ResponseList/components/ResponseListComponent.js @@ -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) { @@ -373,42 +374,41 @@ class ResponseListComponent extends Component { />
    - -
    -
    -

    {t("Assign Reviewer")}

    - -
    -

    {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)")}

    - -
    - -
    -
    - -
    - - {reviewerAssignError && - {JSON.stringify(this.state.reviewerAssignError)} - } - - {reviewerAssignSuccess && - Assigned {{numReviewsAssigned}} reviews to {{assignedReviewerEmail}} - } -
    -
    -
    + + {(!isEventResponseViewerOnly(this.props.user && this.props.event) && +
    +
    +

    {t("Assign Reviewer")}

    +
    +

    {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)")}

    + +
    + +
    +
    + +
    + {reviewerAssignError && + {JSON.stringify(this.state.reviewerAssignError)} + } + {reviewerAssignSuccess && + Assigned {{numReviewsAssigned}} reviews to {{assignedReviewerEmail}} + } +
    +
    +
    + )} -
    -
    -
    - - -
    - -
    -
    - - {this.props.organisation.languages.map((lang) => ( -
    - -
    - -
    -
    - ))} - -
    - - -
    - -
    -
    - -
    - - -
    - -
    -
    - -
    - - -
    - -
    -
    - - {this.props.organisation.languages.map((lang) => ( -
    - - -
    - -
    -
    - ))} - -
    - - -
    - -
    -
    - -
    - - -
    - -
    -
    - - {updatedEvent.event_type && this.renderDatePickerTable()} - -
    -
    -
    - {errors && showErrors && this.getErrorMessages(errors)} -
    -
    -
  • - );} - - else { + return (
    @@ -840,8 +667,7 @@ class EventConfigComponent extends Component {
    - );} - + ); } } diff --git a/webapp/src/pages/home/Home.js b/webapp/src/pages/home/Home.js index eb84fdfbf..790f10900 100644 --- a/webapp/src/pages/home/Home.js +++ b/webapp/src/pages/home/Home.js @@ -5,7 +5,7 @@ import { eventService } from "../../services/events/events.service"; import { organisationService } from "../../services/organisation/organisation.service"; import EventStatus from "../../components/EventStatus"; import { withTranslation } from 'react-i18next'; -import { isEventResponseEditorOnly, isEventResponseViewerOnly } from '../../utils/user'; +import { isEventResponseEditorOnly, isEventResponseViewerOnly, isEventReadOnly } from '../../utils/user'; class Home extends Component { @@ -16,6 +16,7 @@ class Home extends Component { awards: null, journals: null, organisation: null, + event: null, errors: [] } } @@ -38,7 +39,8 @@ class Home extends Component { journals: response.events.filter(e => e.event_type === 'JOURNAL'), calls: response.events.filter(e => e.event_type === "CALL" && (e.is_event_opening || e.is_event_open)), programmes: response.events.filter(e => e.event_type === "PROGRAMME" && (e.is_event_opening || e.is_event_open)), - attended: response.events.filter(e => !e.is_event_opening) + attended: response.events.filter(e => !e.is_event_opening), + event: response.events }); } }); @@ -64,8 +66,8 @@ class Home extends Component { } statusDisplay(e) { - if (isEventResponseViewerOnly(this.props.user, e) && - isEventResponseEditorOnly(this.props.user, e)) { + if (!isEventResponseViewerOnly(this.props.user, e) && + !isEventResponseEditorOnly(this.props.user, e)) { if (e.event_type === 'JOURNAL') { return ; } @@ -118,7 +120,7 @@ class Home extends Component { if (this.state.organisation && this.state.organisation.name === "AI4D Africa" && this.props.i18n.language === "fr") { logo = "ai4d_logo_fr.png"; } - + return (
    @@ -135,10 +137,10 @@ class Home extends Component {
    } - {this.props.user && this.props.user.is_admin && - !isEventResponseViewerOnly(this.props.user, this.props.event) && - !isEventResponseEditorOnly(this.props.user, this.props.event) && - {this.props.t("Create New Event")} + {!isEventResponseEditorOnly(this.props.user, this.state.event) && + !isEventResponseViewerOnly(this.props.user, this.state.event) && + this.props.user && this.props.user.is_admin && + {this.props.t("Create New Event")} } {this.renderEventTable(this.state.upcomingEvents, "Upcoming Events")} diff --git a/webapp/src/utils/user.js b/webapp/src/utils/user.js index e316e2406..fa86217e1 100644 --- a/webapp/src/utils/user.js +++ b/webapp/src/utils/user.js @@ -16,10 +16,9 @@ export const isEventAdmin = (user, event) => { return false; } return ( - //user.is_admin && (user.roles && user.roles.some( - r => r.role === "response_viewer" && event && r.event_id === event.id + r => r.role === "response-viewer" && event && r.event_id === event.id )) ); }; @@ -29,10 +28,9 @@ export const isEventAdmin = (user, event) => { return false; } return ( - //user.is_admin && (user.roles && user.roles.some( - r => r.role === "response_editor" && event && r.event_id === event.id + r => r.role === "response-editor" && event && r.event_id === event.id )) ); };