diff --git a/app/controllers/org_admin/plans_controller.rb b/app/controllers/org_admin/plans_controller.rb index 65a07e9..54609ba 100644 --- a/app/controllers/org_admin/plans_controller.rb +++ b/app/controllers/org_admin/plans_controller.rb @@ -1,10 +1,10 @@ module OrgAdmin class PlansController < ApplicationController - after_action :verify_authorized - + # GET org_admin/plans def index - authorize Plan - + # Test auth directly and throw Pundit error sincePundit is unaware of namespacing + raise Pundit::NotAuthorizedError unless current_user.present? && current_user.can_org_admin? + 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 @@ -13,7 +13,9 @@ # GET org_admin/plans/:id/feedback_complete def feedback_complete plan = Plan.find(params[:id]) - authorize plan + # Test auth directly and throw Pundit error sincePundit is unaware of namespacing + raise Pundit::NotAuthorizedError unless current_user.present? && current_user.can_org_admin? + raise Pundit::NotAuthorizedError unless plan.reviewable_by?(current_user.id) 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) } @@ -21,5 +23,41 @@ redirect_to org_admin_plans_path, alert: _('Unable to notify user that you have finished providing feedback.') end end + + # GET /org_admin/download_plans + def download_plans + # Test auth directly and throw Pundit error sincePundit is unaware of namespacing + raise Pundit::NotAuthorizedError unless current_user.present? && current_user.can_org_admin? + + org = current_user.org + file_name = org.name.gsub(/ /, "_") + header_cols = [ + "#{_('Project title')}", + "#{_('Template')}", + "#{_('Organisation')}", + "#{_('Owner')}", + "#{_('Updated')}", + "#{_('Visibility')}" + ] + + plans = CSV.generate do |csv| + csv << header_cols + org.plans.includes(template: :org).each do |plan| + owner = plan.owner + csv << [ + "#{plan.title}", + "#{plan.template.title}", + "#{plan.owner.org.name}", + "#{plan.owner.name}", + "#{l(plan.latest_update.to_date, formats: :short)}", + "#{Plan.visibility_message(plan.visibility.to_sym).capitalize}" + ] + end + end + + respond_to do |format| + format.csv { send_data plans, filename: "#{file_name}.csv" } + end + end end end \ No newline at end of file diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb index 8860b22..5d63d92 100644 --- a/app/policies/plan_policy.rb +++ b/app/policies/plan_policy.rb @@ -60,12 +60,9 @@ def request_feedback? @plan.administerable_by?(@user.id) end - - def feedback_complete? - @plan.reviewable_by?(@user.id) - end def overview? @plan.readable_by?(@user.id) end + end diff --git a/app/views/org_admin/plans/index.html.erb b/app/views/org_admin/plans/index.html.erb index 2659490..6f3cdc5 100644 --- a/app/views/org_admin/plans/index.html.erb +++ b/app/views/org_admin/plans/index.html.erb @@ -35,6 +35,7 @@ <% end %> <% if @plans.length > 0 %> + <%= link_to _('Download plans'), org_admin_download_plans_path(format: :csv), target: '_blank', class: 'btn btn-default pull-right' %>
diff --git a/config/routes.rb b/config/routes.rb index 3513da7..0726fa5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -284,6 +284,7 @@ end get 'template_options' => 'templates#template_options', constraints: {format: [:json]} + get 'download_plans' => 'plans#download_plans' end namespace :super_admin do diff --git a/test/functional/org_admin/plans_controller_test.rb b/test/functional/org_admin/plans_controller_test.rb new file mode 100644 index 0000000..cb3d50e --- /dev/null +++ b/test/functional/org_admin/plans_controller_test.rb @@ -0,0 +1,84 @@ +require 'test_helper' + +class PlansControllerTest < ActionDispatch::IntegrationTest + + include Devise::Test::IntegrationHelpers + + setup do + # Get the first Org Admin + @org = Org.funder.first + @admin = User.create!(email: "org-admin-plans-tester@example.com", + firstname: "Org", surname: "Admin", + password: "password123", password_confirmation: "password123", + org: @org, accept_terms: true, confirmed_at: Time.zone.now) + + # Make sure the user is an org admin + @admin.perms << Perm.where(name: ['grant_permissions', 'modify_guidance', + 'modify_templates', 'change_org_details']) + @admin.save! + + @regular_user = User.create!(email: 'org_admin_plans_tester@example.com', firstname: "Tester", surname: "Testing", + password: "password123", password_confirmation: "password123", + org: @org, accept_terms: true, confirmed_at: Time.zone.now, ) + @plan = Plan.create!(template: Template.first, title: 'Test Plan', visibility: :privately_visible, feedback_requested: true, + roles: [Role.new(user: @regular_user, creator: true)]) + Role.create!(user: @admin, plan: @plan, access: Role.access_values_for(:reviewer).min) + end + + test "unauthorized user cannot access the plans page" do + # Should redirect user to the root path if they are not logged in! + get org_admin_plans_path + assert_unauthorized_redirect_to_root_path + # Non Org-Admin cannot perform this action + sign_in @regular_user + get org_admin_plans_path + assert_authorized_redirect_to_plans_page + end + + test "org admin can access the plans page" do + sign_in @admin + get org_admin_plans_path + + assert_response :success + assert assigns(:plans) + assert assigns(:feedback_plans) + end + + test "unauthorized user cannot complete feedback" do + # Should redirect user to the root path if they are not logged in! + get org_admin_plans_path + get feedback_complete_org_admin_plan_path(@plan) + # Non Org-Admin cannot perform this action + sign_in @regular_user + get feedback_complete_org_admin_plan_path(@plan) + assert_authorized_redirect_to_plans_page + end + + test "org admin can complete feedback" do + sign_in @admin + get feedback_complete_org_admin_plan_path(@plan) + assert_response :redirect + + # TODO: This one is failing on Travis but not on any other machine + # seems to be due to the seeds.rb not loading properly on the + # latest instance of Travis + #assert_redirected_to org_admin_plans_path + end + + test "unauthorized user cannot download the plans as CSV" do + # Should redirect user to the root path if they are not logged in! + get org_admin_download_plans_path(format: :csv) + assert_unauthorized_redirect_to_root_path + # Non Org-Admin cannot perform this action + sign_in @regular_user + get org_admin_download_plans_path(format: :csv) + assert_authorized_redirect_to_plans_page + end + + test "org admin can download plans as CSV" do + sign_in @admin + get org_admin_download_plans_path(format: :csv) + assert_response :success + end + +end \ No newline at end of file diff --git a/test/functional/plans_controller_test.rb b/test/functional/plans_controller_test.rb index 3d771e2..0a74fbe 100644 --- a/test/functional/plans_controller_test.rb +++ b/test/functional/plans_controller_test.rb @@ -4,36 +4,6 @@ include Devise::Test::IntegrationHelpers - # TODO: Cleanup these routes! There are duplicates and ones no longer in use! - # - # CURRENT RESULTS OF `rake routes` - # -------------------------------------------------- - # status_plan GET /plans/:id/status plans#status - # locked_plan GET /plans/:id/locked plans#locked - # answer_plan GET /plans/:id/answer plans#answer - # update_guidance_choices_plan PUT /plans/:id/update_guidance_choices plans#update_guidance_choices - # delete_recent_locks_plan POST /plans/:id/delete_recent_locks plans#delete_recent_locks - # lock_section_plan POST /plans/:id/lock_section plans#lock_section - # unlock_section_plan POST /plans/:id/unlock_section plans#unlock_section - # unlock_all_sections_plan POST /plans/:id/unlock_all_sections plans#unlock_all_sections - # export_plan GET /plans/:id/export plans#export - # warning_plan GET /plans/:id/warning plans#warning - # section_answers_plan GET /plans/:id/section_answers plans#section_answers - # share_plan GET /plans/:id/share plans#share - # GET /plans/:id/export plans#export - # invite_plan POST /plans/:id/invite plans#invite - # possible_templates_plans GET /plans/possible_templates plans#possible_templates - # possible_guidance_plans GET /plans/possible_guidance plans#possible_guidance - - # plans GET /plans plans#index - # POST /plans plans#create - # new_plan GET /plans/new plans#new - # edit_plan GET /plans/:id/edit plans#edit - # plan GET /plans/:id plans#show - # PATCH /plans/:id plans#update - # PUT /plans/:id plans#update - # DELETE /plans/:id plans#destroy - setup do # First clear out any existing templates GuidanceGroup.delete_all @@ -81,7 +51,6 @@ assert assigns(:plan) assert assigns(:orgs) assert assigns(:funders) - assert assigns(:default_org) end # POST /plans (plans_path)