diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index cce8567..996ed7b 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -11,7 +11,7 @@ # create answer if we don't have one already @answer = nil # if defined within the transaction block, was not accessable afterward # ensure user has access to plan BEFORE creating/finding answer - rails Pundit::NotAuthorizedError unless Plan.find(params[:note][:plan_id]).readable_by?(@note.user_id) + raise Pundit::NotAuthorizedError unless Plan.find(params[:note][:plan_id]).readable_by?(@note.user_id) Answer.transaction do if params[:note][:answer_id].present? @answer = Answer.find(params[:note][:answer_id]) diff --git a/app/controllers/org_admin/plans_controller.rb b/app/controllers/org_admin/plans_controller.rb new file mode 100644 index 0000000..65a07e9 --- /dev/null +++ b/app/controllers/org_admin/plans_controller.rb @@ -0,0 +1,25 @@ +module OrgAdmin + class PlansController < ApplicationController + after_action :verify_authorized + + def index + authorize Plan + + vals = Role.access_values_for(:reviewer) + @feedback_plans = Plan.joins(:roles).where('roles.user_id = ? and roles.access IN (?)', current_user.id, vals) + @plans = current_user.org.plans + end + + # GET org_admin/plans/:id/feedback_complete + def feedback_complete + plan = Plan.find(params[:id]) + authorize plan + + if plan.complete_feedback(current_user) + redirect_to org_admin_plans_path, notice: _('%{plan_owner} has been notified that you have finished providing feedback') % { plan_owner: plan.owner.name(false) } + else + redirect_to org_admin_plans_path, alert: _('Unable to notify user that you have finished providing feedback.') + end + end + end +end \ No newline at end of file diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index a5ee45a..80fff04 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -342,22 +342,19 @@ end def request_feedback - @plan = Plan.find(params[:id]) - authorize @plan + plan = Plan.find(params[:id]) + authorize plan alert = _('Unable to submit your request for feedback at this time.') begin - if @plan.request_feedback(current_user) - flash[:notice] = _('Your request for feedback has been submitted.') + if plan.request_feedback(current_user) + redirect_to share_plan_path(plan), notice: _('Your request for feedback has been submitted.') else - flash[:alert] = alert + redirect_to share_plan_path(plan), alert: alert end rescue Exception - flash[:alert] = alert + redirect_to share_plan_path(plan), alert: alert end - # Get the roles where the user is not a reviewer - @plan_roles = @plan.roles.select{ |r| !r.reviewer? } - render 'share' end private diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 76fed1b..35492c1 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -63,6 +63,17 @@ end end + def feedback_complete(recipient, plan, requestor) + @requestor = requestor + @user = recipient + @plan = plan + + FastGettext.with_locale FastGettext.default_locale do + mail(to: recipient.email, + subject: _("%{application_name}: Expert feedback has been provided for %{plan_title}") % {application_name: Rails.configuration.branding[:application][:name], plan_title: @plan.title}) + end + end + def feedback_confirmation(recipient, plan, requestor) user = requestor diff --git a/app/models/org.rb b/app/models/org.rb index 9a93736..a5ee99d 100644 --- a/app/models/org.rb +++ b/app/models/org.rb @@ -146,6 +146,11 @@ User.joins(:perms).where("users.org_id = ? AND perms.name IN (?)", self.id, ['grant_permissions', 'modify_templates', 'modify_guidance', 'change_org_details']) end + + def plans + Plan.includes(:template, :phases, :roles, :users).joins(:roles, :users).where('users.org_id = ? AND roles.access IN (?)', + self.id, Role.access_values_for(:owner).concat(Role.access_values_for(:administrator))) + end private ## diff --git a/app/models/plan.rb b/app/models/plan.rb index cdc9882..23bd463 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -196,36 +196,69 @@ # emails org admins and org contact # adds org admins to plan with the 'reviewer' Role def request_feedback(user) - val = Role.access_values_for(:reviewer, :commenter).min - self.feedback_requested = true + Plan.transaction do + begin + val = Role.access_values_for(:reviewer, :commenter).min + self.feedback_requested = true - # Share the plan with each org admin as the reviewer role - admins = user.org.org_admins - admins.each do |admin| - role = Role.new(user: admin, access: val) - self.roles << role unless self.users.include?(admin) - end + # Share the plan with each org admin as the reviewer role + admins = user.org.org_admins + admins.each do |admin| + self.roles << Role.new(user: admin, access: val) + end - if self.save! - # Send an email confirmation to the owners and co-owners - self.owner_and_coowners.each do |owner| - UserMailer.feedback_confirmation(owner, self, user).deliver_now + if self.save! + # Send an email confirmation to the owners and co-owners + deliver_if(recipients: self.owner_and_coowners, key: 'users.feedback_requested') do |r| + UserMailer.feedback_confirmation(r, self, user).deliver_now + end + # Send an email to all of the org admins as well as the Org's administrator email + if user.org.contact_email.present? + admins << User.new(email: user.org.contact_email, firstname: user.org.contact_name) + end + deliver_if(recipients: admins, key: 'admins.feedback_requested') do |r| + UserMailer.feedback_notification(r, self, user).deliver_now + end + true + else + false + end + rescue Exception => e + Rails.logger.error e + false end - - # Send an email to all of the org admins as well as the Org's administrator email - if user.org.contact_email.present? - admins << User.new(email: user.org.contact_email, firstname: user.org.contact_name) - end - - deliver_if(recipients: admins, key: 'admins.feedback_requested') do |r| - UserMailer.feedback_notification(r, self, user).deliver_now - end - true - else - false end end + ## + # Finalizes the feedback for the plan: + # emails confirmation messages to owners + # sets flag on plans.feedback_requested to false + # removes org admins from the 'reviewer' Role for the Plan + def complete_feedback(org_admin) + Plan.transaction do + begin + self.feedback_requested = false + + # Remove the org admins reviewer role from the plan + vals = Role.access_values_for(:reviewer) + self.roles.delete(Role.where(plan: self, access: vals)) + + if self.save! + # Send an email confirmation to the owners and co-owners + deliver_if(recipients: self.owner_and_coowners, key: 'users.feedback_provided') do |r| + UserMailer.feedback_notification(r, self, org_admin).deliver_now + end + true + else + false + end + rescue Exception => e + Rails.logger.error e + false + end + end + end ## # returns the guidances associated with the project's organisation, for a specified question @@ -314,8 +347,7 @@ # @return [Boolean] true if user can edit the plan def editable_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? && role.editor? + has_role(user_id, :editor) end ## @@ -327,8 +359,7 @@ # @return [Boolean] true if the user can read the plan def readable_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? + has_role(user_id, :commenter) end ## @@ -338,8 +369,7 @@ # @return [Boolean] true if the user can administer the plan def administerable_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? && role.administrator? + has_role(user_id, :administrator) end ## @@ -349,8 +379,7 @@ # @return [Boolean] true if the user can administer the plan def owned_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? && role.creator? + has_role(user_id, :creator) end ## @@ -360,8 +389,7 @@ # @return [Boolean] true if the user can administer the plan def reviewable_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? && role.reviewer? + has_role(user_id, :reviewer) end ## @@ -609,12 +637,8 @@ # # @return [User] the creater of the project def owner - self.roles.each do |role| - if role.creator? - return role.user - end - end - return nil + vals = Role.access_values_for(:creator) + User.joins(:roles).where('roles.plan_id = ? AND roles.access IN (?)', self.id, vals).first end ## @@ -725,6 +749,15 @@ private + # Returns whether or not the user has the specified role for the plan + def has_role(user_id, role_as_sym) + if user_id.is_a?(Integer) && role_as_sym.is_a?(Symbol) + vals = Role.access_values_for(role_as_sym) + self.roles.where(user_id: user_id, access: vals).first.present? + else + false + end + end ## # adds a user to the project diff --git a/app/models/user.rb b/app/models/user.rb index c028a92..e46f805 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -263,12 +263,23 @@ # # @return [JSON] with symbols as keys def get_preferences(key) - if self.pref.present? && self.pref.settings[key.to_s].present? - return self.pref.settings[key.to_s].deep_symbolize_keys - elsif Pref.default_settings - return Pref.default_settings[key.to_sym] || Pref.default_settings[key.to_s] + defaults = Pref.default_settings[key.to_sym] || Pref.default_settings[key.to_s] + + if self.pref.present? + existing = self.pref.settings[key.to_s].deep_symbolize_keys + + # Check for new preferences + defaults.keys.each do |grp| + defaults[grp].keys.each do |pref, v| + # If the group isn't present in the saved values add all of it's preferences + existing[grp] = defaults[grp] if existing[grp].nil? + # If the preference isn't present in the saved values add the default + existing[grp][pref] = defaults[grp][pref] if existing[grp][pref].nil? + end + end + existing else - return nil + defaults end end diff --git a/app/policies/org_admin/plan_policy.rb b/app/policies/org_admin/plan_policy.rb new file mode 100644 index 0000000..9e75820 --- /dev/null +++ b/app/policies/org_admin/plan_policy.rb @@ -0,0 +1,10 @@ +class PlanPolicy < ApplicationPolicy + attr_reader :user + + def initialize(user) + raise Pundit::NotAuthorizedError, _("must be logged in") unless user + raise Pundit::NotAuthorizedError, _("are not authorized to view that plan") unless user.can_org_admin? + @user = user + end + +end diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb index 78c674b..cbe3c4c 100644 --- a/app/policies/plan_policy.rb +++ b/app/policies/plan_policy.rb @@ -58,6 +58,10 @@ end def request_feedback? - @plan.owned_by?(@user.id) && Role.find_by(user_id: @user.id, plan_id: @plan.id).active + @plan.administerable_by?(@user.id) && Role.find_by(user_id: @user.id, plan_id: @plan.id).active + end + + def feedback_complete? + @plan.reviewable_by?(@user.id) && Role.find_by(user_id: @user.id, plan_id: @plan.id).active end end diff --git a/app/views/layouts/_branding.html.erb b/app/views/layouts/_branding.html.erb index eb05a15..2b89344 100644 --- a/app/views/layouts/_branding.html.erb +++ b/app/views/layouts/_branding.html.erb @@ -41,6 +41,11 @@