diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index 20be939..1d20731 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -7,6 +7,8 @@ def index authorize Plan @plans = current_user.active_plans + # Exclude any plans where the user is a reviewer. They access those plans via Admin -> Plans menu + @plans.delete_if{ |p| p.reviewable_by?(current_user) } @paginable = params[:page] @organisationally_or_publicly_visible = @paginable.nil? ? Plan.organisationally_or_publicly_visible(current_user) : Plan.organisationally_or_publicly_visible(current_user).page(params[:page]) @@ -179,6 +181,8 @@ @plan = Plan.find(params[:id]) if @plan.present? authorize @plan + # Get the roles where the user is not a reviewer + @plan_roles = @plan.roles.select{ |r| !r.reviewer? } else redirect_to(plans_path) end @@ -338,6 +342,25 @@ end end + def request_feedback + @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.') + else + flash[:alert] = alert + end + rescue Exception + flash[: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 def plan_params params.require(:plan).permit(:org_id, :org_name, :funder_id, :funder_name, :template_id, :title, :visibility, diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index afa3b5e..79646ae 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -1,6 +1,6 @@ class UserMailer < ActionMailer::Base default from: Rails.configuration.branding[:organisation][:email] - + def welcome_notification(user) @user = user FastGettext.with_locale FastGettext.default_locale do @@ -31,10 +31,10 @@ def project_access_removed_notification(user, plan, current_user) @user = user @plan = plan - @current_user = current_user + @current_user = current_user FastGettext.with_locale FastGettext.default_locale do mail(to: @user.email, - subject: "#{_('Permissions removed on a DMP in')} #{Rails.configuration.branding[:application][:name]}") + subject: "#{_('Permissions removed on a DMP in %{tool_name}') %{ :tool_name => Rails.configuration.branding[:application][:name] }}") end end @@ -46,25 +46,34 @@ end end - def feedback_notification(user, plan) - @user = user - - if user.org.present? - @org = org + def feedback_notification(recipient, plan, requestor) + @user = requestor + + if @user.org.present? + @org = @user.org @plan = plan + @recipient = recipient - # Use the generic feedback message unless the Org has specified one - subject = org.feedback_email_subject ||= EMAIL_FEEDBACK_REQUESTED_CONFIRMATION_SUBJECT + FastGettext.with_locale FastGettext.default_locale do + mail(to: recipient.email, + subject: _("%{application_name}: %{user_name} requested feedback on a plan") % {application_name: Rails.configuration.branding[:application][:name], user_name: @user.name(false)}) + end + end + end + + def feedback_confirmation(recipient, plan, requestor) + @user = requestor + + if @user.org.present? + @org = @user.org + @plan = plan + + # Use the generic feedback confirmation message unless the Org has specified one + subject = feedback_constant_to_text(@org.feedback_email_subject.present? ? @org.feedback_email_subject : feedback_confirmation_default_subject) + message = feedback_constant_to_text(@org.feedback_email_msg.present? ? @org.feedback_email_msg : feedback_confirmation_default_message) - # Send an email to all of the org admins as well as the Org's administrator email - emails = user.org.users.select{ |usr| usr.can_org_admin? && usr != user } - emails << user.org.contact_email if user.org.contact_email.present? - - emails.each do |email| - @email = email - FastGettext.with_locale FastGettext.default_locale do - mail(to: email, subject: subject) - end + FastGettext.with_locale FastGettext.default_locale do + mail(to: recipient.email, subject: subject, body: message) end end end @@ -77,6 +86,7 @@ subject: _('DMP Visibility Changed: %{plan_title}') %{ :plan_title => @plan.title }) end end + # @param commenter - User who wrote the comment # @param plan - Plan for which the comment is associated to def new_comment(commenter, plan) @@ -91,4 +101,22 @@ end end end + + def feedback_confirmation_default_subject + _('%{application_name}: Your plan has been submitted for feedback') + end + + def feedback_confirmation_default_message + _('

Hello %{user_name}.

'\ + '

Your plan "%{plan_name}" has been submitted for feedback from an administrator at your organisation. '\ + 'If you have questions pertaining to this action, please contact us at %{organisation_email}.

') + end + + private + def feedback_constant_to_text(text) + return _("#{text}") % {application_name: Rails.configuration.branding[:application][:name], + user_name: @user.name, + plan_name: @plan.title, + organisation_email: @org.contact_email} + end end diff --git a/app/models/org.rb b/app/models/org.rb index dde3833..9a93736 100644 --- a/app/models/org.rb +++ b/app/models/org.rb @@ -142,6 +142,11 @@ end end + def org_admins + User.joins(:perms).where("users.org_id = ? AND perms.name IN (?)", self.id, + ['grant_permissions', 'modify_templates', 'modify_guidance', 'change_org_details']) + end + private ## # checks size of logo and resizes if necessary diff --git a/app/models/plan.rb b/app/models/plan.rb index 91e17a5..96086dd 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -38,7 +38,7 @@ :exported_plans, :project, :title, :template, :grant_number, :identifier, :principal_investigator, :principal_investigator_identifier, :description, :data_contact, :funder_name, :visibility, :exported_plans, - :roles, :users, :org, :data_contact_email, :data_contact_phone, + :roles, :users, :org, :data_contact_email, :data_contact_phone, :feedback_requested, :principal_investigator_email, :as => [:default, :admin] accepts_nested_attributes_for :roles @@ -189,8 +189,40 @@ end return ggroups.uniq end + + ## + # Sets up the plan for feedback: + # emails confirmation messages to owners + # 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 + + # 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 + 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 + admins.each do |admin| + UserMailer.feedback_notification(admin, self, user).deliver_now + end + true + else + false + end + end ## @@ -320,6 +352,17 @@ end ## + # determines if the plan is reviewable by the specified user + # + # @param user_id [Integer] the id for the user + # @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? + end + + ## # determines whether or not the specified user has any rol on the plan # # @param user_id [Integer] the id for the user @@ -573,6 +616,15 @@ end ## + # the owner and co-owners of the project + # + # @return [Users] + def owner_and_coowners + vals = Role.access_values_for(:creator).concat(Role.access_values_for(:administrator)) + User.joins(:roles).where("roles.plan_id = ? AND roles.access IN (?)", self.id, vals) + end + + ## # the time the project was last updated, formatted as a date # # @return [Date] last update as a date diff --git a/app/models/role.rb b/app/models/role.rb index 28f63f1..7ccdde4 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -15,6 +15,7 @@ 2 => :administrator, # 2 3 => :editor, # 4 4 => :commenter, # 8 + 5 => :reviewer, # 16 column: 'access' validates :user, :plan, :access, presence: {message: _("can't be blank")} @@ -22,6 +23,7 @@ ## # return the access level for the current project group + # 5 if the user is a reviewer # 3 if the user is an administrator # 2 if the user is an editor # 1 if the user can only read @@ -29,7 +31,9 @@ # # @return [Integer] def access_level - if self.administrator? + if self.reviewer? + return 5 + elsif self.administrator? return 3 elsif self.editor? return 2 @@ -41,6 +45,11 @@ # This method becomes useful for generatic template messages (e.g. permissions change notification mailer) def self.access_level_messages { + 5 => { + :type => _('reviewer'), + :placeholder1 => _('read the plan and provide feedback.'), + :placeholder2 => nil + }, 3 => { :type => _('co-owner'), :placeholder1 => _('write and edit the plan in a collaborative manner.'), @@ -82,4 +91,20 @@ # 12 - editor + commenter # 13 - creator + editor + commenter # 14 - administrator + editor + commenter -# 15 - creator + administrator + editor + commenter \ No newline at end of file +# 15 - creator + administrator + editor + commenter +# 16 - reviewer +# 17 - creator + reviewer +# 18 - administrator + reviewer +# 19 - creator + administrator + reviewer +# 20 - editor + reviewer +# 21 - creator + editor + reviewer +# 22 - administraor + editor + reviewer +# 23 - creator + editor + administrator + reviewer +# 24 - commenter + reviewer +# 25 - creator + commenter + reviewer +# 26 - administrator + commenter + reviewer +# 27 - creator + administrator + commenter + reviewer +# 28 - editor + commenter + reviewer +# 29 - creator + editor + commenter + reviewer +# 30 - administrator + editor + commenter + reviewer +# 31 - creator + administrator + editor + commenter + reviewer \ No newline at end of file diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb index 8fd7e88..11dd045 100644 --- a/app/policies/plan_policy.rb +++ b/app/policies/plan_policy.rb @@ -53,4 +53,7 @@ @plan.readable_by?(@user.id) && Role.find_by(user_id: @user.id, plan_id: @plan.id).active end + def request_feedback? + @plan.owned_by?(@user.id) && Role.find_by(user_id: @user.id, plan_id: @plan.id).active + end end diff --git a/app/views/orgs/_feedback_form.html.erb b/app/views/orgs/_feedback_form.html.erb index dce7405..2d651fd 100644 --- a/app/views/orgs/_feedback_form.html.erb +++ b/app/views/orgs/_feedback_form.html.erb @@ -11,12 +11,11 @@
<%= f.label :feedback_email_subject, _('Subject'), class: "control-label" %> - <%= f.text_field :feedback_email_subject, class: "form-control", - placeholder: "#{_('A user has requested feedback for their DMP in')} #{Rails.configuration.branding[:application][:name]}" %> + <%= f.text_field :feedback_email_subject, class: "form-control" %>
-
+
<%= f.label :feedback_email_msg, _('Message'), class: "control-label" %> <%= f.text_area :feedback_email_msg, class: "form-control" %>
@@ -26,8 +25,8 @@
<%= f.button(_('Save'), id:"save_org_submit", class: "btn btn-primary", type: "submit") %> <%= f.button(_('Reset to defaults'), id:"reset-to-default-feedback-email", class: "btn btn-default", type: "button") %> -
<%= EMAIL_FEEDBACK_REQUESTED_CONFIRMATION_SUBJECT %>
-
<%= EMAIL_FEEDBACK_REQUESTED_CONFIRMATION_MESSAGE %>
+
<%= UserMailer.feedback_confirmation_default_subject %>
+
<%= UserMailer.feedback_confirmation_default_message %>
<% end %> \ No newline at end of file diff --git a/app/views/orgs/_profile_form.html.erb b/app/views/orgs/_profile_form.html.erb index af73e7e..a6d5b06 100644 --- a/app/views/orgs/_profile_form.html.erb +++ b/app/views/orgs/_profile_form.html.erb @@ -74,7 +74,7 @@
<% if current_user.can_super_admin? %> -
+

<%= _('Organisational Configuration Information') %>

<% shibboleth = @org.org_identifiers.select{ |ids| ids.identifier_scheme == IdentifierScheme.find_by(name: 'shibboleth')} %> diff --git a/app/views/plans/_share_form.html.erb b/app/views/plans/_share_form.html.erb index c90e816..f6f3b18 100644 --- a/app/views/plans/_share_form.html.erb +++ b/app/views/plans/_share_form.html.erb @@ -30,8 +30,7 @@ - <% plan_roles = @plan.roles.where(active: true) %> - <% plan_roles.each do |role| %> + <% @plan_roles.each do |role| %> <%= role.user.name %> @@ -86,5 +85,16 @@ <%= f.button(_('Submit'), class: "btn btn-primary", type: "submit") %>
+
+<% end %> +<% if plan.owner_and_coowners.include?(current_user) && current_user.org.feedback_enabled? %> +

<%= _('Request expert feedback') %>

+

<%= _('Click below to give data management staff at your organisation access to read and comment on your plan.') %>

+

<%= _('You can continue to edit and download the plan in the interim.') %>

+ +
+ <%= link_to _('Request feedback'), request_feedback_plan_path, class: "btn btn-default#{' disabled' if @plan.feedback_requested?}" %> + <%= _("Feedback has been requested.") if @plan.feedback_requested? %> +
<% end %> \ No newline at end of file diff --git a/app/views/user_mailer/feedback_notification.html.erb b/app/views/user_mailer/feedback_notification.html.erb index cc5f2a9..eabb5d5 100644 --- a/app/views/user_mailer/feedback_notification.html.erb +++ b/app/views/user_mailer/feedback_notification.html.erb @@ -1,6 +1,15 @@ <% FastGettext.with_locale FastGettext.default_locale do %> -<% if @org.feedback_email_msg.present? %> - <%= raw @org.feedback_email_msg %> -<% else %> - <%= raw EMAIL_FEEDBACK_REQUESTED_CONFIRMATION_MESSAGE %> -<% end %> + <% + recipient_name = @recipient.name(false) + requestor_name = @user.name(false) + plan_name = @plan.title + tool_name = Rails.configuration.branding[:application][:name] + helpdesk = Rails.configuration.branding[:application][:email] + contact_url = "#{new_contact_url}" + %> + +

<%= _('Hello %{user_name},') % {user_name: recipient_name} %>

+

<%= _('%{requestor} has requested feedback on a plan "%{plan_name}." To add comments, please visit the \'Plans\' page under the Admin menu in %{application_name} and open the plan.') % {requestor: requestor_name, plan_name: plan_name, application_name: tool_name} %>

+

<%= _('All the best,
The %{application_name} team') % {application_name: tool_name} %>

+

<%= _('You may change your notification preferences on your profile page. Please do not reply to this email. If you have questions or need help, please contact us at %{help_desk} or visit %{contact_url}') % {help_desk: helpdesk, contact_url: contact_url} %>

+<% end %> \ No newline at end of file diff --git a/config/initializers/constants.rb b/config/initializers/constants.rb index d8952b8..c3f0124 100644 --- a/config/initializers/constants.rb +++ b/config/initializers/constants.rb @@ -1,11 +1,3 @@ LANGUAGES = (ActiveRecord::Base.connection.table_exists? 'languages') ? Language.sorted_by_abbreviation : [] MANY_LANGUAGES = LANGUAGES.length > 1 TABLE_FILTER_MIN_ROWS = 10 - -# Default Feedback Request Email sent to the requesting user -# This email can be overriden by local Org Admins on the Org Details page -# TODO: Move this to a location that gets loaded AFTER FastGettext so that we can use fastgettext to manage localisations -EMAIL_FEEDBACK_REQUESTED_CONFIRMATION_SUBJECT = 'Your DMP has been submitted for feedback in %{application_name}' -EMAIL_FEEDBACK_REQUESTED_CONFIRMATION_MESSAGE = '

Hello [user_name],

'\ - '

Your DMP "[plan_name]" has been submitted for feedback from an administrator at your institution. If you have questions pertaining to this action, please contact your local administrator at [organisation_email].

'\ - '

All the best,
The %{application_name} team.

' diff --git a/config/routes.rb b/config/routes.rb index 2636d98..155ae89 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -220,6 +220,7 @@ post 'invite' post 'visibility', constraints: {format: [:json]} post 'set_test', constraints: {format: [:json]} + get 'request_feedback' end collection do diff --git a/db/migrate/20171102185518_add_feedback_requested_to_plans.rb b/db/migrate/20171102185518_add_feedback_requested_to_plans.rb new file mode 100644 index 0000000..9293868 --- /dev/null +++ b/db/migrate/20171102185518_add_feedback_requested_to_plans.rb @@ -0,0 +1,5 @@ +class AddFeedbackRequestedToPlans < ActiveRecord::Migration + def change + add_column :plans, :feedback_requested, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index fbe8055..ec518b0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171102164156) do +ActiveRecord::Schema.define(version: 20171102185518) do create_table "annotations", force: :cascade do |t| t.integer "question_id", limit: 4 @@ -230,6 +230,7 @@ t.string "data_contact_phone", limit: 255 t.string "principal_investigator_email", limit: 255 t.string "principal_investigator_phone", limit: 255 + t.boolean "feedback_requested", default: false end add_index "plans", ["template_id"], name: "index_plans_on_template_id"