From 9434754cca88fa5e02f4e39d946790d821a809e9 Mon Sep 17 00:00:00 2001 From: TheSprtCz Date: Thu, 25 Aug 2016 08:46:26 +0200 Subject: [PATCH] Added checkbox to registration form for University Supervisor - new Domain class RoleRequest - RoleRequest is created when registrating, but is enabled after confirming account. - Added new notifications when creating, approving or declining RoleRequest - RoleRequests can be found under Members tab - Members tab now has 2 tabs inside, Members and RoleRequests - Only Admin can approve, decline RoleRequest --- grails-app/conf/BootStrap.groovy | 4 + grails-app/conf/TestBootStrap.groovy | 7 +- .../theses/RoleRequestController.groovy | 127 ++++++++++++++++++ .../theses/auth/RegistrationController.groovy | 19 ++- .../redhat/theses/auth/UserController.groovy | 1 + .../com/redhat/theses/RoleRequest.groovy | 10 ++ grails-app/i18n/messages.properties | 19 +++ grails-app/i18n/messages_cs.properties | 19 +++ grails-app/migrations/changelog.groovy | 2 + grails-app/migrations/roleRequest.groovy | 34 +++++ .../RoleRequestListenerService.groovy | 75 +++++++++++ .../listeners/UserListenerService.groovy | 13 ++ grails-app/views/layouts/_navigation.gsp | 2 +- grails-app/views/layouts/user/listLayout.gsp | 31 +++++ grails-app/views/registration/_form.gsp | 10 ++ grails-app/views/roleRequest/list.gsp | 77 +++++++++++ grails-app/views/roleRequest/show.gsp | 58 ++++++++ grails-app/views/user/list.gsp | 111 ++++++++------- .../theses/events/RoleRequestEvent.groovy | 15 +++ ...pplicationControllerIntegrationSpec.groovy | 4 +- .../theses/RoleRequestIntegrationSpec.groovy | 121 +++++++++++++++++ 21 files changed, 704 insertions(+), 55 deletions(-) create mode 100644 grails-app/controllers/com/redhat/theses/RoleRequestController.groovy create mode 100644 grails-app/domain/com/redhat/theses/RoleRequest.groovy create mode 100644 grails-app/migrations/roleRequest.groovy create mode 100644 grails-app/services/com/redhat/theses/listeners/RoleRequestListenerService.groovy create mode 100644 grails-app/views/layouts/user/listLayout.gsp create mode 100644 grails-app/views/roleRequest/list.gsp create mode 100644 grails-app/views/roleRequest/show.gsp create mode 100644 src/groovy/com/redhat/theses/events/RoleRequestEvent.groovy create mode 100644 test/integration/com/redhat/theses/RoleRequestIntegrationSpec.groovy diff --git a/grails-app/conf/BootStrap.groovy b/grails-app/conf/BootStrap.groovy index aea369c..096d556 100644 --- a/grails-app/conf/BootStrap.groovy +++ b/grails-app/conf/BootStrap.groovy @@ -82,6 +82,8 @@ class BootStrap { enabled: true, roles: [Role.STUDENT] ).save(flush: true) + new RoleRequest(applicant: example2, status: AppStatus.PENDING, enabled: true).save(flush: true) + def example3 = new User( email: 'example3@example.com', fullName: 'Example Tri', @@ -89,6 +91,8 @@ class BootStrap { enabled: true, roles: [Role.STUDENT] ).save(flush: true) + new RoleRequest(applicant: example3, status: AppStatus.PENDING, enabled: true).save(flush: true) + def supervisor1 = new User( email: 'supervisor1@example.com', fullName: 'Supervisor Jedna', diff --git a/grails-app/conf/TestBootStrap.groovy b/grails-app/conf/TestBootStrap.groovy index 04b9979..3bbd606 100644 --- a/grails-app/conf/TestBootStrap.groovy +++ b/grails-app/conf/TestBootStrap.groovy @@ -209,7 +209,12 @@ they are done with their thesis and application management.''', //Invalid Applications new Application(applicant: example2, topic: tmsTopic, university: first, note: 'I want to do it', type: Type.BACHELOR, status: AppStatus.PENDING).save(flush: true) - } + + //Role requests + new RoleRequest(applicant: example1, status: AppStatus.PENDING, enabled: true).save(flush: true) + new RoleRequest(applicant: example2, status: AppStatus.PENDING, enabled: true).save(flush: true) + new RoleRequest(applicant: example3, status: AppStatus.DECLINED, enabled: true).save(flush: true) + } } } } diff --git a/grails-app/controllers/com/redhat/theses/RoleRequestController.groovy b/grails-app/controllers/com/redhat/theses/RoleRequestController.groovy new file mode 100644 index 0000000..8ac275f --- /dev/null +++ b/grails-app/controllers/com/redhat/theses/RoleRequestController.groovy @@ -0,0 +1,127 @@ +package com.redhat.theses + +import com.redhat.theses.auth.User +import com.redhat.theses.auth.Role +import com.redhat.theses.events.RoleRequestEvent +import grails.plugins.springsecurity.Secured +import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils + +class RoleRequestController { + + /** + * Dependency injection of com.redhat.theses.FilterService + */ + def filterService + + /** + * Dependency injection of grails.plugins.springsecurity.SpringSecurityService + */ + def springSecurityService + + static defaultAction = "list" + + @Secured(['ROLE_ADMIN']) + def list() { + if (!params.filter) { + params.filter = [:] + } + if (!params?.filtering) { + params.filter << [status: AppStatus.PENDING] + } + else { + params.type = [applicant: [fullName: "ilike"]] + } + params.filter << [enabled: true] + + def requestInstanceList = filterService.filter(params, RoleRequest) + def requestInstanceTotal = filterService.count(params, RoleRequest) + + [requestInstanceList: requestInstanceList, requestInstanceTotal: requestInstanceTotal] + } + + def show(Integer id) { + def requestInstance = RoleRequest.get(id) + + if (!requestInstance) { + flash.message = message(code: 'request.not.found', args: [id]) + redirect(action: "list") + return + } + + def user = springSecurityService.currentUser + + if (!SpringSecurityUtils.ifAnyGranted('ROLE_ADMIN') && user != requestInstance.applicant) { + flash.message = message(code: 'security.denied.message') + redirect(action: 'list') + return + } + + def canManage = SpringSecurityUtils.ifAnyGranted('ROLE_ADMIN') + + [requestInstance: requestInstance, canManage: canManage] + } + + @Secured('ROLE_ADMIN') + def decline(Long id) { + def requestInstance = RoleRequest.get(id) + if (!requestInstance) { + flash.message = message(code: 'request.not.found', args: [id]) + redirect(action: 'list') + return + } + + User user = springSecurityService.currentUser + + if (!SpringSecurityUtils.ifAnyGranted('ROLE_ADMIN')) { + + flash.message = message(code: 'security.denied.message') + redirect(action: 'list') + return + } + + requestInstance.status = AppStatus.DECLINED + + if (!requestInstance.save()) { + flash.message = message(code: 'request.save.error') + redirect(uri:'/') + return + } + + event("RoleRequestDeclined", new RoleRequestEvent(requestInstance, user)) + flash.message = message(code: 'request.declined', args: [id]) + redirect(action: 'list') + } + + @Secured('ROLE_ADMIN') + def approve(Long id) { + def requestInstance = RoleRequest.get(id) + if (!requestInstance) { + flash.message = message(code: 'request.not.found', args: [id]) + redirect(action: 'list') + return + } + + User user = springSecurityService.currentUser + + if (!SpringSecurityUtils.ifAnyGranted('ROLE_ADMIN')) { + + flash.message = message(code: 'security.denied.message') + redirect(action: 'list') + return + } + + requestInstance.status = AppStatus.APPROVED + + requestInstance.applicant.roles.add(Role.SUPERVISOR) + + if (!requestInstance.save() || !requestInstance.applicant.save()) { + flash.message = message(code: 'request.save.error') + redirect(uri:'/') + return + } + + event("RoleRequestApproved", new RoleRequestEvent(requestInstance, user)) + flash.message = message(code: 'request.approved', args: [id]) + redirect(action: 'list') + } +} diff --git a/grails-app/controllers/com/redhat/theses/auth/RegistrationController.groovy b/grails-app/controllers/com/redhat/theses/auth/RegistrationController.groovy index d3e6af9..acc6d64 100644 --- a/grails-app/controllers/com/redhat/theses/auth/RegistrationController.groovy +++ b/grails-app/controllers/com/redhat/theses/auth/RegistrationController.groovy @@ -1,6 +1,8 @@ package com.redhat.theses.auth import com.redhat.theses.util.Util +import com.redhat.theses.AppStatus +import com.redhat.theses.RoleRequest class RegistrationController { @@ -54,17 +56,30 @@ class RegistrationController { render(view: "index", model: [registrationCommand: registrationCommand, config: configuration.getConfig()]) return } + + def createRoleRequest = params.supervisorRequest && !Util.hasAnyDomain(user.email, configuration.defaultSupervisors) + + if (createRoleRequest) { + new RoleRequest(applicant: user, status: AppStatus.PENDING, enabled: false).save() + } + log.info("User ${user.email} registration completed.") - redirect(action: 'complete') + redirect(action: 'complete', params: [isSupervisor: createRoleRequest]) } def complete() { + def content = message(code: 'registration.complete.message') + + if (params.isSupervisor == "true") { + content += "

" + message(code: 'registration.complete.request.message') + } + render view: '/emailConfirmation/lifecycle', model: [ redirect: false, title: message(code: 'registration.complete.title'), header: message(code: 'registration.complete.header'), - content: message(code: 'registration.complete.message') + content: content ] } diff --git a/grails-app/controllers/com/redhat/theses/auth/UserController.groovy b/grails-app/controllers/com/redhat/theses/auth/UserController.groovy index 5918bec..122a4fd 100644 --- a/grails-app/controllers/com/redhat/theses/auth/UserController.groovy +++ b/grails-app/controllers/com/redhat/theses/auth/UserController.groovy @@ -29,6 +29,7 @@ class UserController { static defaultAction = "list" + @Secured(['ROLE_ADMIN']) def list(Integer max) { params.max = Util.max(max, MAX_USERS) diff --git a/grails-app/domain/com/redhat/theses/RoleRequest.groovy b/grails-app/domain/com/redhat/theses/RoleRequest.groovy new file mode 100644 index 0000000..e09a493 --- /dev/null +++ b/grails-app/domain/com/redhat/theses/RoleRequest.groovy @@ -0,0 +1,10 @@ +package com.redhat.theses; + +import com.redhat.theses.auth.User + +class RoleRequest { + + User applicant + AppStatus status + boolean enabled +} diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties index 1fa920c..1791872 100644 --- a/grails-app/i18n/messages.properties +++ b/grails-app/i18n/messages.properties @@ -183,6 +183,7 @@ user.accountExpired.label=Account Expired user.accountLocked.label=Account Locked user.roles.label=Roles user.role.label=Role +user.list.label=List of Members user.passwordExpired.label=Password Expired user.sendMail.label=I want to receive mail from my subscriptions user.list.manage.label=Manage Users @@ -271,6 +272,17 @@ application.topic.disabled=You cannot apply for this topic because the topic is application.status.select.label=-- select status -- +# ROLE REQUEST +request.no.requests.found=No Role Requests found +request.list.label=List of Role Requests +request.show.title=Role Request - {0} +request.subject.approved=You have been given University Supervisor permissions. +request.subject.declined=Your request for University Supervisor permissions has been declined. +request.approved=Role Request #{0} has been approved +request.declined=Role Request #{0} has been declined +request.not.found=Role Request with id {0} was not found +request.save.error=Unable to save Role Request, please try again later. + # COMMENT comment.label=Comment comment.title=Comments @@ -472,6 +484,10 @@ feed.thesis.created=User {0} created new thesis feed.thesis.updated=User {0} updated thesis {2}. feed.thesis.deleted=User {0} deleted topic {2}. +feed.request.created=User {0} asked for University Supervisor permissions. +feed.request.approved=User {0} has given you University Supervisor permissions as you requested. +feed.request.declined=Your request for University Supervisor permissions has been declined by {0}. + feed.comment.created=User {0} commented on {2}. feed.application.created=User {0} created new application for topic {3}. @@ -700,11 +716,13 @@ registration.register.button=Register registration.allowedEmails.message=Please use your university email address. You can register with email address hosted at the following internet domains: registration.complete.title=Your account has been created +registration.complete.request.message=Administrator was also notified and will review your request to have University Supervisor permissions. registration.complete.header=Your account has been created registration.complete.message=Your account has been created and a confirmation email has been send to your email address, please confirm your registration within 24 hours by clicking on the link in the sent email. registration.confirmed.title=Your registration is confirmed registration.confirmed.header=Your registration is confirmed registration.confirmed.message=You have successfully confirmed your account. You will be redirected to the login page shortly. +registration.supervisor.request=I am Supervisor from university # Profile profile.edit.title=Edit Profile @@ -781,3 +799,4 @@ info.filter.user=Filter members by user's full name or email. You may also put i info.filter.topic=Filter topics by title/description/lead paragraph in English, technical supervisor's full name, academic supervisor's full name or university name. You may put in only a part of the full name or title. By default, this page shows only enabled topics, you can show also disabled topic by unchecking option 'Show only enabled topics'. info.filter.thesis=Filter thesis by one of the following fields. You may also put in only a part of the title/academic supervisor's full name/asignee's full name. info.filter.application=Filter applications by applicant's full name, topic title (primary title, i.e. title in english) or status. You may also put in only a part of applicant's full name or topic's title. +info.filter.request=Filter applications by applicant's full name or status. \ No newline at end of file diff --git a/grails-app/i18n/messages_cs.properties b/grails-app/i18n/messages_cs.properties index 40fcafc..acbb762 100644 --- a/grails-app/i18n/messages_cs.properties +++ b/grails-app/i18n/messages_cs.properties @@ -182,6 +182,7 @@ user.accountExpired.label=Účet vypršel user.accountLocked.label=Účet zablokován user.roles.label=Role user.role.label=Role +user.list.label=Uživatelé user.passwordExpired.label=Heslo vypršelo user.sendMail.label=Odesílat emaily z mých odběrů user.list.manage.label=Spravovat uživatele @@ -270,6 +271,17 @@ application.topic.disabled=Nemůžete podat přihlášku pro toto téma, protož application.status.select.label=-- vyberte stav přihlášky -- +# ROLE REQUEST +request.no.requests.found=Žádné Žádosti o pravomoce nenalezeny. +request.list.label=Žádosti o pravomoce +request.show.title=Žádost o pravomoce - {0} +request.subject.approved=Byly ti přiděleny práva Univerzitního vedoucího. +request.subject.declined=Váš požadavek na práva Univerzitního vedoucího byl zamítnut. +request.approved=Žádost o pravomoce #{0} byla schálena +request.declined=Žádost o pravomoce #{0} byla zamítnuta +request.not.found=Žádost o pravomoce s id {0} nebyla nalezena +request.save.error=Nepodařilo se uložit Žádost o pravomoce, zkuste to prosím později. + # COMMENT comment.label=Komentář comment.title=Komentáře @@ -467,6 +479,10 @@ feed.topic.created=Uživatel {0} vytvořil nové téma {0} upravil téma {2}. feed.topic.deleted=Uživatel {0} smazal téma {2}. +feed.request.created=Uživatel {0} požádal o práva Univerzitního vedoucího. +feed.request.approved=Uživatel {0} Vám přidělil práva Univerzitního vedoucího, jak jste si přál. +feed.request.declined=Váš požadavek o práva Univerzitního vedoucího byl zamítnut uživatelem {0}. + feed.thesis.created=Uživatel {0} vytvořil novou kvalifikační práci {2}. feed.thesis.updated=Uživatel {0} upravil kvalifikační práci {2}. feed.thesis.deleted=Uživatel {0} smazal kvalifikační práci {2}. @@ -700,9 +716,11 @@ registration.allowedEmails.message=Prosím použíjte svůj univerzitní registration.complete.title=Váš účet byl vytvořen registration.complete.header=Váš účet byl vytvořen registration.complete.message=Váš účet byl vytvořen a potvrzující email byl odeslán na vaši emailovou adresu, prosím potvrďte vaši registraci do 24 hodin kliknutím na odkaz v odeslaném emailu. +registration.complete.request.message=Administrátor byl upozorněn a zhodnotí Váš požadavek na práva Univerzitního vedoucího. registration.confirmed.title=Vaše registrace byla úspěšně potvrzena registration.confirmed.header=Vaše registrace byla úspěšně potvrzena registration.confirmed.message=Úspěšně jste potvrdil vaši registraci. Za moment budete přesměrováni na přihlašovací stránku. +registration.supervisor.request=Jsem Univerzitní vedoucí # Profile profile.edit.title=Upravení profilu @@ -779,3 +797,4 @@ info.filter.user=Uživatele lze filtrovat podle jejich jména nebo e-mailové ad info.filter.topic=Témata můžete filtrovat podle primárního názvu/popisu/perexu, jména technického vedoucího, podle univerzitního vedoucího nebo jména univerzity. Můžete také zadat pouze část jména nebo názvu. Tato stránka zobrazuje standardně pouze povolená témata, pokud potřebujete zobrazit i zakázaná témata, odškrtněte možnost „Zobrazit pouze povolená témata”. info.filter.thesis=Kvalifikační práce můžete filtrovat jedním z následujících polí. Při filtrování lze také zadat pouze část názvu nebo jména. info.filter.application=Přihlášky můžete filtrovat podle primárního názvu témata (tj. anglického názvu), podle jména uživatele, který přihlášku podal, nebo podle stavu přihlášky. Lze také zadat pouze část jména nebo názvu. Standardně se zobrazují pouze přihlášky, které ještě nejsou schválené. +info.filter.request=Přihlášky můžete filtrovat podle jména uživatele a stavu požadavku. diff --git a/grails-app/migrations/changelog.groovy b/grails-app/migrations/changelog.groovy index 6a8d6be..84a328f 100644 --- a/grails-app/migrations/changelog.groovy +++ b/grails-app/migrations/changelog.groovy @@ -4,4 +4,6 @@ databaseChangeLog = { include file: 'application-approved-replaced-by-status.groovy' include file: 'add-links-to-theses.groovy' + + include file: 'roleRequest.groovy' } diff --git a/grails-app/migrations/roleRequest.groovy b/grails-app/migrations/roleRequest.groovy new file mode 100644 index 0000000..1b14825 --- /dev/null +++ b/grails-app/migrations/roleRequest.groovy @@ -0,0 +1,34 @@ +databaseChangeLog = { + + changeSet(author: "phala (generated)", id: "1472640506724-1") { + createTable(tableName: "role_request") { + column(name: "id", type: "int8") { + constraints(nullable: "false", primaryKey: "true", primaryKeyName: "role_requestPK") + } + + column(name: "version", type: "int8") { + constraints(nullable: "false") + } + + column(name: "applicant_id", type: "int8") { + constraints(nullable: "false") + } + + column(name: "enabled", type: "bool") { + constraints(nullable: "false") + } + + column(name: "status", type: "varchar(255)") { + constraints(nullable: "false") + } + } + } + + changeSet(author: "phala (generated)", id: "1472640506724-3") { + dropIndex(indexName: "unique_applicant_id", tableName: "application") + } + + changeSet(author: "phala (generated)", id: "1472640506724-2") { + addForeignKeyConstraint(baseColumnNames: "applicant_id", baseTableName: "role_request", constraintName: "FK581F43C67E712562", deferrable: "false", initiallyDeferred: "false", referencedColumnNames: "id", referencedTableName: "user", referencesUniqueColumn: "false") + } +} diff --git a/grails-app/services/com/redhat/theses/listeners/RoleRequestListenerService.groovy b/grails-app/services/com/redhat/theses/listeners/RoleRequestListenerService.groovy new file mode 100644 index 0000000..eed2e0f --- /dev/null +++ b/grails-app/services/com/redhat/theses/listeners/RoleRequestListenerService.groovy @@ -0,0 +1,75 @@ +package com.redhat.theses.listeners + +import com.redhat.theses.RoleRequest +import com.redhat.theses.Feed +import com.redhat.theses.events.RoleRequestEvent +import com.redhat.theses.util.Util +import com.redhat.theses.auth.User +import com.redhat.theses.auth.Role +import grails.events.Listener +import org.springframework.context.i18n.LocaleContextHolder as LCH + +class RoleRequestListenerService { + + def messageSource + /** + * Dependency injection of com.redhat.theses.SubscriptionService + */ + def subscriptionService + + /** + * Dependency injection of org.codehaus.groovy.grails.web.mapping.LinkGenerator + */ + def grailsLinkGenerator + + /** + * Creates new feed and notifies administrator about creation of new Role Request + */ + @Listener(topic = "RoleRequestCreated") + def roleRequestCreated(RoleRequestEvent e) { + def request = e.request + def args = [ + e.user.fullName, + grailsLinkGenerator.link(controller: 'user', action: 'show', id: e.user.id, absolute: true), + grailsLinkGenerator.link(controller: 'roleRequest', action: 'show', id: request.id, absolute: true) + ] + + def feed = new Feed(messageCode: 'feed.request.created', args: args, user: e.user).save() + + for (User admin : User.executeQuery("from User u inner join u.roles roles where roles = :role", [role: Role.ADMIN])) { + subscriptionService.notify(admin, feed, e.user.fullName) + } + } + + /** + * Creates new feed and notifies applicant about approval of their Role Request + */ + @Listener(topic = "RoleRequestApproved") + def roleRequestApproved(RoleRequestEvent e) { + def request = RoleRequest.get(e.request.id) + def args = [ + e.user.fullName, + grailsLinkGenerator.link(controller: 'user', action: 'show', id: e.user.id, absolute: true), + ] + + def feed = new Feed(messageCode: 'feed.request.approved', args: args, user: e.user).save() + + subscriptionService.notify(request.applicant, feed, messageSource.getMessage('request.subject.approved', [].toArray(), LCH.locale)) + } + + /** + * Creates new feed and notifies applicant that their Role Request has been declined + */ + @Listener(topic = "RoleRequestDeclined") + def roleRequestDeclined(RoleRequestEvent e) { + def request = RoleRequest.get(e.request.id) + def args = [ + e.user.fullName, + grailsLinkGenerator.link(controller: 'user', action: 'show', id: e.user.id, absolute: true), + ] + + def feed = new Feed(messageCode: 'feed.request.declined', args: args, user: e.user).save() + + subscriptionService.notify(request.applicant, feed, messageSource.getMessage('request.subject.declined', [].toArray(), LCH.locale)) + } +} diff --git a/grails-app/services/com/redhat/theses/listeners/UserListenerService.groovy b/grails-app/services/com/redhat/theses/listeners/UserListenerService.groovy index 5a9ee52..6a29818 100644 --- a/grails-app/services/com/redhat/theses/listeners/UserListenerService.groovy +++ b/grails-app/services/com/redhat/theses/listeners/UserListenerService.groovy @@ -1,7 +1,9 @@ package com.redhat.theses.listeners import com.redhat.theses.auth.User +import com.redhat.theses.RoleRequest import com.redhat.theses.events.EmailChangedEvent import com.redhat.theses.events.LostPasswordEvent +import com.redhat.theses.events.RoleRequestEvent import com.redhat.theses.events.UserCreatedEvent import grails.events.Listener import org.apache.commons.lang.RandomStringUtils @@ -15,6 +17,7 @@ import java.sql.SQLException */ class UserListenerService { + def grailsEvents /** * Dependency injection of com.grailsrocks.emailconfirmation.EmailConfirmationService */ @@ -68,8 +71,16 @@ class UserListenerService { // set the user enabled def user = User.get(info.id) user.enabled = true + + def roleRequest = RoleRequest.findByApplicant(user) + try { user.save() + if (roleRequest) { + roleRequest.enabled = true + roleRequest.save() + grailsEvents.event('app', "RoleRequestCreated", new RoleRequestEvent(roleRequest, user)) + } log.info "User ${info.email} successfully confirmed with application id data ${info.id}" } catch (Exception ex) { log.error(ex.getMessage()) @@ -98,6 +109,8 @@ class UserListenerService { User.withoutHibernateFilters { user = User.findByEmail(info.email) } + RoleRequest roleRequest = RoleRequest.findByApplicant(user) + roleRequest?.delete() user.delete() log.info "User with email ${info.email} deleted" } catch (Exception ex) { diff --git a/grails-app/views/layouts/_navigation.gsp b/grails-app/views/layouts/_navigation.gsp index 0d8b84f..0f39e11 100644 --- a/grails-app/views/layouts/_navigation.gsp +++ b/grails-app/views/layouts/_navigation.gsp @@ -10,7 +10,7 @@ -
  • +
  • diff --git a/grails-app/views/layouts/user/listLayout.gsp b/grails-app/views/layouts/user/listLayout.gsp new file mode 100644 index 0000000..8fa610c --- /dev/null +++ b/grails-app/views/layouts/user/listLayout.gsp @@ -0,0 +1,31 @@ +<%@ page import="com.redhat.theses.auth.Role; com.redhat.theses.util.Util; com.redhat.theses.auth.User" %> + + + + <g:layoutTitle /> + + + +
    + +
    + +
    +
    + + + +
    diff --git a/grails-app/views/registration/_form.gsp b/grails-app/views/registration/_form.gsp index a24598e..dc53a33 100644 --- a/grails-app/views/registration/_form.gsp +++ b/grails-app/views/registration/_form.gsp @@ -67,3 +67,13 @@ + +
    +
    + +
    +
    diff --git a/grails-app/views/roleRequest/list.gsp b/grails-app/views/roleRequest/list.gsp new file mode 100644 index 0000000..78534fa --- /dev/null +++ b/grails-app/views/roleRequest/list.gsp @@ -0,0 +1,77 @@ +<%@ page import="com.redhat.theses.AppStatus; com.redhat.theses.util.Util; com.redhat.theses.auth.User"%> + + + + + +<g:message code="request.list.label" /> + + + roleRequest + + +

    + +
    + + + + + + + + + + + + + + + + + +
    + ${fieldValue(bean: request.applicant, field: "fullName")} + + + + +
    +
    + + + +
    + +

    +
    +
    + + +
    +

    +
    + +
    + +

    +
    + + + + + + +
    +
    +
    + + \ No newline at end of file diff --git a/grails-app/views/roleRequest/show.gsp b/grails-app/views/roleRequest/show.gsp new file mode 100644 index 0000000..36615f6 --- /dev/null +++ b/grails-app/views/roleRequest/show.gsp @@ -0,0 +1,58 @@ +<%@ page import="com.redhat.theses.AppStatus; com.redhat.theses.util.Util; com.redhat.theses.Category" %> + + + + + <g:message code="request.show.title" args="[requestInstance.applicant.fullName]"/> + + + +
    +

    + +

    + +
    +
    + +
    +
    + + + +
    +
    + +
    +
    + +
    +
    +
    + + + + + + diff --git a/grails-app/views/user/list.gsp b/grails-app/views/user/list.gsp index 0075183..6b61861 100644 --- a/grails-app/views/user/list.gsp +++ b/grails-app/views/user/list.gsp @@ -3,69 +3,82 @@ - + <g:message code="user.list.title" /> -
    -

    + list + + +

    + +

    - - - - - - +
    - - - -
    + + + + + + + - - - - - - - - -
    + +
    - - - - -
    + + + + + + + + + +
    -

    +

    + +

    - - + -
    + - + diff --git a/src/groovy/com/redhat/theses/events/RoleRequestEvent.groovy b/src/groovy/com/redhat/theses/events/RoleRequestEvent.groovy new file mode 100644 index 0000000..c912de1 --- /dev/null +++ b/src/groovy/com/redhat/theses/events/RoleRequestEvent.groovy @@ -0,0 +1,15 @@ +package com.redhat.theses.events + +import com.redhat.theses.RoleRequest +import com.redhat.theses.auth.User + + +class RoleRequestEvent { + def user + def request + + RoleRequestEvent(RoleRequest request, User user) { + this.request = request + this.user = user + } +} diff --git a/test/integration/com/redhat/theses/ApplicationControllerIntegrationSpec.groovy b/test/integration/com/redhat/theses/ApplicationControllerIntegrationSpec.groovy index b123154..a300a87 100644 --- a/test/integration/com/redhat/theses/ApplicationControllerIntegrationSpec.groovy +++ b/test/integration/com/redhat/theses/ApplicationControllerIntegrationSpec.groovy @@ -78,7 +78,7 @@ class ApplicationControllerIntegrationSpec extends IntegrationSpec { } void "Can not save without proper parameters"() { - given: "some, but not all parameters" + given: "some but not all parameters" controller.params.application = [:] controller.params.application.topic = Topic.findByTitle('Thesis management system') when: "trying to save" @@ -89,7 +89,7 @@ class ApplicationControllerIntegrationSpec extends IntegrationSpec { controller.modelAndView?.model?.universities?.size() == 1 } - @Unroll("Can not decline application #id out of as #username, should redirect to #redirectUrl") + @Unroll("Can not decline application #id as #username, should redirect to #redirectUrl") void "Can not decline application"() { given: SpringSecurityUtils.reauthenticate(username, password) diff --git a/test/integration/com/redhat/theses/RoleRequestIntegrationSpec.groovy b/test/integration/com/redhat/theses/RoleRequestIntegrationSpec.groovy new file mode 100644 index 0000000..602d4c7 --- /dev/null +++ b/test/integration/com/redhat/theses/RoleRequestIntegrationSpec.groovy @@ -0,0 +1,121 @@ +package com.redhat.theses + +import com.redhat.theses.auth.User +import com.redhat.theses.auth.Role +import com.redhat.theses.AppStatus +import grails.plugin.spock.IntegrationSpec +import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils +import spock.lang.Shared +import spock.lang.Unroll + +class RoleRequestIntegrationSpec extends IntegrationSpec { + @Shared controller = new RoleRequestController() + + def setupSpec() { + SpringSecurityUtils.reauthenticate("example1@example.com", "example2") + } + + def cleanupSpec() { + Feed.getAll()*.delete(flush: true) + Notification.getAll()*.delete(flush: true) + } + + @Unroll("Can list role requests with filter #customFilter, should return #expectedCount entries") + void "Test list"() { + given: "parameters to filter" + controller.params.filter = [:] + controller.params.filtering = true + controller.params.filter << customFilter + + def model = controller.list() + expect: + model?.requestInstanceList?.size() == expectedCount + where: + customFilter | expectedCount + [status: AppStatus.PENDING] | 2 + [status: AppStatus.DECLINED] | 1 + [applicant: User.findByEmail("example1@example.com")] | 1 + [applicant: User.findByEmail("example2@example.com")] | 1 + } + + @Unroll("Can not decline application #id as #username, should redirect to #redirectUrl") + void "Can not decline role request"() { + given: + SpringSecurityUtils.reauthenticate(username, password) + controller.decline(id) + expect: + controller.response?.redirectUrl.endsWith("list") + where: + username | password | id + "owner1@example.com" | "owner1" | 1 + "supervisor1@example.com" | "supervisor1" | 2 + "example1@example.com" | "example2" | 2 + } + + void "Can decline role request as administrator"() { + given: + SpringSecurityUtils.reauthenticate("admin@example.com", "admin") + when: + controller.decline(1) + then: + RoleRequest.get(1)?.status == AppStatus.DECLINED + controller.flash.message != null + controller.response?.redirectUrl.endsWith("list") + } + + void "Can approve role request as administrator"() { + given: + SpringSecurityUtils.reauthenticate("admin@example.com", "admin") + when: + controller.approve(1) + then: + controller.flash.message != null + RoleRequest.get(1).applicant.roles.contains(Role.SUPERVISOR) + } + + @Unroll("Can not approve role request #id as #username, should redirect to #redirectUrl") + void "Can not approve role request"() { + given: + SpringSecurityUtils.reauthenticate(username, password) + controller.approve(id) + expect: + controller.modelAndView?.viewName == null + controller.response?.redirectUrl?.endsWith("list") + controller.flash.message != null + where: + username | password | id + "example1@example.com" | "example2" | 1 + "supervisor1@example.com" | "supervisor1" | 2 + "admin@example.com" | "admin" | 4 + } + + @Unroll("Can show role request #id as #username") + void "Can show role request"() { + given: + SpringSecurityUtils.reauthenticate(username, password) + controller.show(id) + expect: + controller.flash.message == null + where: + username | password | id + "example1@example.com" | "example2" | 1 + "example3@example.com" | "example3" | 3 + "admin@example.com" | "admin" | 2 + "admin@example.com" | "admin" | 3 + } + + @Unroll("Cannot show role request #id as #username with bad permissions") + void "Cannot show role request with bad permissions"() { + given: + SpringSecurityUtils.reauthenticate(username, password) + controller.show(id) + expect: + controller.response?.redirectUrl?.endsWith("list") + controller.flash.message != null + where: + username | password | id + "example1@example.com" | "example2" | 2 + "owner1@example.com" | "owner2" | 3 + "supervisor1@example.com" | "supervisor2" | 3 + } +}