From e339b791066fc0860949e339020d6af98e1c12e7 Mon Sep 17 00:00:00 2001
From: MAEDA Go
Date: Sun, 6 Sep 2026 13:16:47 +0900
Subject: [PATCH] Add "Export issues" and "Export time entries" permissions
---
app/controllers/application_controller.rb | 23 ++++++
app/controllers/gantts_controller.rb | 6 ++
app/controllers/issues_controller.rb | 10 +++
app/controllers/reports_controller.rb | 6 ++
app/controllers/timelog_controller.rb | 10 +++
app/views/gantts/_chart.html.erb | 2 +
app/views/issues/index.html.erb | 5 ++
app/views/issues/show.html.erb | 2 +-
app/views/reports/_details.html.erb | 2 +
app/views/timelog/index.html.erb | 5 +-
app/views/timelog/report.html.erb | 4 +-
config/locales/en.yml | 2 +
...ues_and_export_time_entries_permissions.rb | 14 ++++
lib/redmine/default_data/loader.rb | 6 ++
lib/redmine/preparation.rb | 2 +
test/fixtures/roles.yml | 10 +++
test/functional/gantts_controller_test.rb | 19 +++++
test/functional/issues_controller_test.rb | 74 +++++++++++++++++++
test/functional/reports_controller_test.rb | 32 ++++++++
test/functional/timelog_controller_test.rb | 49 ++++++++++++
20 files changed, 280 insertions(+), 3 deletions(-)
create mode 100644 db/migrate/20260906000000_add_export_issues_and_export_time_entries_permissions.rb
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 6dc17d26b..9273d5e36 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -565,6 +565,29 @@ class ApplicationController < ActionController::Base
private :valid_back_url?
helper_method :valid_back_url?
+ # Returns true if the current user has the given export permission on
+ # the projects of all the records in scope (issues or time entries)
+ def export_allowed?(permission, scope)
+ user = User.current
+ return true if user.admin?
+
+ if @project
+ # Deny if the user does not have the permission on @project itself
+ return false unless user.allowed_to?(permission, @project)
+ # @project has no subprojects, so no further check is needed
+ return true if @project.leaf?
+ else
+ # Cross-project view: deny if none of the user's roles has the permission
+ return false unless user.allowed_to?(permission, nil, :global => true)
+ end
+
+ # Deny if the scope contains records of a project where
+ # the user does not have the permission
+ !scope.where.not(:project_id => Project.allowed_to(user, permission).select(:id)).exists?
+ end
+ private :export_allowed?
+ helper_method :export_allowed?
+
# Redirects to the request referer if present, redirects to args or call block otherwise.
def redirect_to_referer_or(*args, &block)
if referer = request.headers["Referer"]
diff --git a/app/controllers/gantts_controller.rb b/app/controllers/gantts_controller.rb
index 66c93b34a..433c26af8 100644
--- a/app/controllers/gantts_controller.rb
+++ b/app/controllers/gantts_controller.rb
@@ -37,6 +37,12 @@ class GanttsController < ApplicationController
@query.group_by = nil
@gantt.query = @query if @query.valid?
+ @export_allowed = export_allowed?(:export_issues, @query.valid? ? @query.base_scope : Issue.none)
+ if (request.format.pdf? || request.format.png?) && !@export_allowed
+ deny_access
+ return
+ end
+
basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
respond_to do |format|
diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index 9ae4d4393..8734a0035 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -48,6 +48,11 @@ class IssuesController < ApplicationController
retrieve_query(IssueQuery, use_session)
if @query.valid?
+ if (request.format.csv? || request.format.pdf?) && !export_allowed?(:export_issues, @query.base_scope)
+ deny_access
+ return
+ end
+
respond_to do |format|
format.html do
@issue_count = @query.issue_count
@@ -95,6 +100,11 @@ class IssuesController < ApplicationController
end
def show
+ if request.format.pdf? && !User.current.allowed_to?(:export_issues, @project)
+ deny_access
+ return
+ end
+
if !api_request? || include_in_api_response?('journals')
@journals = @issue.visible_journals_with_index
@journals.reverse! if User.current.wants_comments_in_reverse_order?
diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index 94a875540..163b04090 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -44,6 +44,12 @@ class ReportsController < ApplicationController
def issue_report_details
with_subprojects = Setting.display_subprojects_issues?
+ @export_allowed = export_allowed?(:export_issues, Issue.visible(User.current, :project => @project, :with_subprojects => with_subprojects))
+ if request.format.csv? && !@export_allowed
+ deny_access
+ return
+ end
+
case params[:detail]
when "tracker"
@field = "tracker_id"
diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb
index 6dd3edf18..16a844efa 100644
--- a/app/controllers/timelog_controller.rb
+++ b/app/controllers/timelog_controller.rb
@@ -43,6 +43,11 @@ class TimelogController < ApplicationController
def index
retrieve_time_entry_query
+ if request.format.csv? && !export_allowed?(:export_time_entries, @query.base_scope)
+ deny_access
+ return
+ end
+
scope = time_entry_scope.
preload(:issue => [:project, :tracker, :status, :assigned_to, :priority]).
preload(:project, :user)
@@ -74,6 +79,11 @@ class TimelogController < ApplicationController
def report
retrieve_time_entry_query
+ if request.format.csv? && !export_allowed?(:export_time_entries, @query.base_scope)
+ deny_access
+ return
+ end
+
scope = time_entry_scope
@report = Redmine::Helpers::TimeReport.new(@project, params[:criteria], params[:columns], scope)
diff --git a/app/views/gantts/_chart.html.erb b/app/views/gantts/_chart.html.erb
index 10667b656..52df9ea0a 100644
--- a/app/views/gantts/_chart.html.erb
+++ b/app/views/gantts/_chart.html.erb
@@ -249,7 +249,9 @@
+<% if @export_allowed %>
<% other_formats_links do |f| %>
<%= f.link_to_with_query_parameters 'PDF', gantt.params %>
<%= f.link_to_with_query_parameters('PNG', gantt.params) if gantt.respond_to?('to_image') %>
<% end %>
+<% end %>
diff --git a/app/views/issues/index.html.erb b/app/views/issues/index.html.erb
index 70f0b740a..aa8829164 100644
--- a/app/views/issues/index.html.erb
+++ b/app/views/issues/index.html.erb
@@ -34,12 +34,16 @@
<% end %>
+<% export_allowed = export_allowed?(:export_issues, @query.base_scope) %>
<% other_formats_links do |f| %>
+ <% if export_allowed %>
<%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '350px'); return false;" %>
<%= f.link_to_with_query_parameters 'PDF' %>
+ <% end %>
<%= f.link_to_with_query_parameters 'Atom', :key => User.current.atom_key %>
<% end %>
+<% if export_allowed %>
<%= l(:label_export_options, :export_format => 'CSV') %>
<%= form_tag(_project_issues_path(@project, :format => 'csv'), :method => :get, :id => 'csv-export-form') do %>
@@ -72,6 +76,7 @@
<% end %>
+<% end %>
<% end %>
<%= call_hook(:view_issues_index_bottom, { :issues => @issues, :project => @project, :query => @query }) %>
diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb
index 13d35cc82..eaeecf44b 100644
--- a/app/views/issues/show.html.erb
+++ b/app/views/issues/show.html.erb
@@ -155,7 +155,7 @@ end %>
<%= render partial: 'action_menu_edit' unless User.current.wants_comments_in_reverse_order? %>
<% other_formats_links do |f| %>
- <%= f.link_to 'PDF' %>
+ <%= f.link_to 'PDF' if User.current.allowed_to?(:export_issues, @project) %>
<%= f.link_to 'Atom', :url => {:key => User.current.atom_key} %>
<% end %>
diff --git a/app/views/reports/_details.html.erb b/app/views/reports/_details.html.erb
index 959d589fa..566177a05 100644
--- a/app/views/reports/_details.html.erb
+++ b/app/views/reports/_details.html.erb
@@ -36,6 +36,7 @@
+<% if @export_allowed %>
<% other_formats_links do |f| %>
<%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %>
<% end %>
@@ -50,6 +51,7 @@
<% end %>
+<% end %>
diff --git a/app/views/timelog/index.html.erb b/app/views/timelog/index.html.erb
index d9985e922..5e61dd2c0 100644
--- a/app/views/timelog/index.html.erb
+++ b/app/views/timelog/index.html.erb
@@ -28,11 +28,13 @@
<%= render :partial => 'list', :locals => { :entries => @entries }%>
+<% export_allowed = export_allowed?(:export_time_entries, @query.base_scope) %>
<% other_formats_links do |f| %>
- <%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %>
+ <%= f.link_to_with_query_parameters('CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;") if export_allowed %>
<%= f.link_to_with_query_parameters 'Atom', :key => User.current.atom_key %>
<% end %>
+<% if export_allowed %>
<%= l(:label_export_options, :export_format => 'CSV') %>
<%= form_tag(_time_entries_path(@project, nil, :format => 'csv'), :method => :get, :id => 'csv-export-form') do %>
@@ -62,6 +64,7 @@
<% end %>
<% end %>
+<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'timelog/sidebar' %>
diff --git a/app/views/timelog/report.html.erb b/app/views/timelog/report.html.erb
index 1aa446860..6b0357578 100644
--- a/app/views/timelog/report.html.erb
+++ b/app/views/timelog/report.html.erb
@@ -66,10 +66,10 @@
+<% if export_allowed?(:export_time_entries, @query.base_scope) %>
<% other_formats_links do |f| %>
<%= f.link_to_with_query_parameters 'CSV', {}, :onclick => "showModal('csv-export-options', '330px'); return false;" %>
<% end %>
-<% end %>
<%= l(:label_export_options, :export_format => 'CSV') %>
<%= export_csv_encoding_select_tag %>
@@ -81,6 +81,8 @@
<% end %>
<% end %>
+<% end %>
+<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'sidebar' %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index bdbad19a0..6bea95fc6 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -612,6 +612,7 @@ en:
permission_export_wiki_pages: Export wiki pages
permission_manage_subtasks: Manage subtasks
permission_manage_related_issues: Manage related issues
+ permission_export_issues: Export issues
permission_import_issues: Import issues
permission_log_time_for_other_users: Log spent time for other users
permission_view_project: View projects
@@ -1112,6 +1113,7 @@ en:
label_member_management_all_roles: All roles
label_member_management_selected_roles_only: Only these roles
label_import_issues: Import issues
+ permission_export_time_entries: Export time entries
permission_import_time_entries: Import time entries
label_select_file_to_import: Select the file to import
label_fields_separator: Field separator
diff --git a/db/migrate/20260906000000_add_export_issues_and_export_time_entries_permissions.rb b/db/migrate/20260906000000_add_export_issues_and_export_time_entries_permissions.rb
new file mode 100644
index 000000000..d5eb06c61
--- /dev/null
+++ b/db/migrate/20260906000000_add_export_issues_and_export_time_entries_permissions.rb
@@ -0,0 +1,14 @@
+class AddExportIssuesAndExportTimeEntriesPermissions < ActiveRecord::Migration[8.1]
+ def up
+ Role.find_each do |role|
+ role.add_permission!(:export_issues) if role.has_permission?(:view_issues)
+ role.add_permission!(:export_time_entries) if role.has_permission?(:view_time_entries)
+ end
+ end
+
+ def down
+ Role.find_each do |role|
+ role.remove_permission!(:export_issues, :export_time_entries)
+ end
+ end
+end
diff --git a/lib/redmine/default_data/loader.rb b/lib/redmine/default_data/loader.rb
index 541bca177..fbbf5352a 100644
--- a/lib/redmine/default_data/loader.rb
+++ b/lib/redmine/default_data/loader.rb
@@ -59,6 +59,7 @@ module Redmine
:manage_versions,
:manage_categories,
:view_issues,
+ :export_issues,
:add_issues,
:edit_issues,
:view_private_notes,
@@ -71,6 +72,7 @@ module Redmine
:view_calendar,
:log_time,
:view_time_entries,
+ :export_time_entries,
:view_news,
:comment_news,
:view_documents,
@@ -95,6 +97,7 @@ module Redmine
:position => 3,
:permissions => [
:view_issues,
+ :export_issues,
:add_issues,
:add_issue_notes,
:save_queries,
@@ -102,6 +105,7 @@ module Redmine
:view_calendar,
:log_time,
:view_time_entries,
+ :export_time_entries,
:view_news,
:comment_news,
:view_documents,
@@ -116,12 +120,14 @@ module Redmine
]
)
Role.non_member.update_attribute :permissions, [:view_issues,
+ :export_issues,
:add_issues,
:add_issue_notes,
:save_queries,
:view_gantt,
:view_calendar,
:view_time_entries,
+ :export_time_entries,
:view_news,
:comment_news,
:view_documents,
diff --git a/lib/redmine/preparation.rb b/lib/redmine/preparation.rb
index fdd713303..acbc2bbbe 100644
--- a/lib/redmine/preparation.rb
+++ b/lib/redmine/preparation.rb
@@ -82,6 +82,7 @@ module Redmine
map.permission :view_issue_watchers, {}, :read => true
map.permission :add_issue_watchers, {:watchers => [:new, :create, :append, :autocomplete_for_user, :autocomplete_for_mention]}
map.permission :delete_issue_watchers, {:watchers => :destroy}
+ map.permission :export_issues, {}, :read => true
map.permission :import_issues, {}
# Issue categories
map.permission :manage_categories, {:projects => :settings, :issue_categories => [:index, :show, :new, :create, :edit, :update, :destroy]}, :require => :member
@@ -100,6 +101,7 @@ module Redmine
{:projects => :settings, :project_enumerations => [:update, :destroy]},
:require => :member
map.permission :log_time_for_other_users, :require => :member
+ map.permission :export_time_entries, {}, :read => true
map.permission :import_time_entries, {}
end
diff --git a/test/fixtures/roles.yml b/test/fixtures/roles.yml
index df15c9c0f..280cc8214 100644
--- a/test/fixtures/roles.yml
+++ b/test/fixtures/roles.yml
@@ -17,6 +17,7 @@ roles_001:
- :manage_versions
- :manage_categories
- :view_issues
+ - :export_issues
- :add_issues
- :edit_issues
- :copy_issues
@@ -36,6 +37,7 @@ roles_001:
- :view_calendar
- :log_time
- :view_time_entries
+ - :export_time_entries
- :edit_time_entries
- :delete_time_entries
- :import_time_entries
@@ -91,6 +93,7 @@ roles_002:
- :manage_versions
- :manage_categories
- :view_issues
+ - :export_issues
- :add_issues
- :edit_issues
- :copy_issues
@@ -104,6 +107,7 @@ roles_002:
- :view_calendar
- :log_time
- :view_time_entries
+ - :export_time_entries
- :edit_own_time_entries
- :view_news
- :manage_news
@@ -144,6 +148,7 @@ roles_003:
- :manage_versions
- :manage_categories
- :view_issues
+ - :export_issues
- :add_issues
- :edit_issues
- :manage_issue_relations
@@ -154,6 +159,7 @@ roles_003:
- :view_calendar
- :log_time
- :view_time_entries
+ - :export_time_entries
- :view_news
- :manage_news
- :comment_news
@@ -185,6 +191,7 @@ roles_004:
permissions: |
---
- :view_issues
+ - :export_issues
- :add_issues
- :edit_issues
- :manage_issue_relations
@@ -194,6 +201,7 @@ roles_004:
- :view_calendar
- :log_time
- :view_time_entries
+ - :export_time_entries
- :view_news
- :comment_news
- :view_documents
@@ -217,10 +225,12 @@ roles_005:
permissions: |
---
- :view_issues
+ - :export_issues
- :add_issue_notes
- :view_gantt
- :view_calendar
- :view_time_entries
+ - :export_time_entries
- :view_news
- :view_documents
- :view_wiki_pages
diff --git a/test/functional/gantts_controller_test.rb b/test/functional/gantts_controller_test.rb
index 65dc0fc47..69cbf0716 100644
--- a/test/functional/gantts_controller_test.rb
+++ b/test/functional/gantts_controller_test.rb
@@ -171,6 +171,25 @@ class GanttsControllerTest < Redmine::ControllerTest
assert @response.body.starts_with?('%PDF')
end
+ def test_gantt_pdf_and_png_without_export_issues_permission_should_be_denied
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+
+ get :show, :params => {:project_id => 1, :format => 'pdf'}
+ assert_response :forbidden
+
+ get :show, :params => {:project_id => 1, :format => 'png'}
+ assert_response :forbidden
+ end
+
+ def test_gantt_should_not_show_export_links_without_export_issues_permission
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+ get :show, :params => {:project_id => 1}
+ assert_response :success
+ assert_select 'p.other-formats', 0
+ end
+
if Object.const_defined?(:MiniMagick) && convert_installed?
def test_gantt_should_export_to_png
get(
diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb
index 3b016776b..6c572e802 100644
--- a/test/functional/issues_controller_test.rb
+++ b/test/functional/issues_controller_test.rb
@@ -823,6 +823,63 @@ class IssuesControllerTest < Redmine::ControllerTest
assert_equal Setting.issue_list_default_columns.size + 2, lines[0].split(',').size
end
+ def test_index_csv_and_pdf_without_export_issues_permission_should_be_denied
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+
+ get :index, :params => {:project_id => 1, :format => 'csv'}
+ assert_response :forbidden
+
+ get :index, :params => {:project_id => 1, :format => 'pdf'}
+ assert_response :forbidden
+ end
+
+ def test_index_csv_across_projects_without_export_issues_permission_should_be_denied
+ @request.session[:user_id] = 2
+ Role.all.each {|role| role.remove_permission! :export_issues}
+ get :index, :params => {:format => 'csv'}
+ assert_response :forbidden
+ end
+
+ def test_index_csv_should_require_export_issues_permission_on_every_project_of_the_issues
+ # User 2 is Developer on project 2 and has visible issues there
+ @request.session[:user_id] = 2
+ Role.find_by_name('Developer').remove_permission! :export_issues
+
+ get :index, :params => {:format => 'csv'}
+ assert_response :forbidden
+
+ get :index, :params => {:project_id => 1, :format => 'csv'}
+ assert_response :success
+ end
+
+ def test_index_should_not_show_export_links_without_export_issues_permission_on_every_project_of_the_issues
+ @request.session[:user_id] = 2
+ Role.find_by_name('Developer').remove_permission! :export_issues
+
+ get :index
+ assert_response :success
+ assert_select 'p.other-formats a.csv', 0
+ assert_select 'p.other-formats a.pdf', 0
+
+ get :index, :params => {:project_id => 1}
+ assert_response :success
+ assert_select 'p.other-formats a.csv'
+ assert_select 'p.other-formats a.pdf'
+ end
+
+ def test_index_should_not_show_export_links_without_export_issues_permission
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+ get :index, :params => {:project_id => 1}
+ assert_response :success
+
+ assert_select 'p.other-formats a.csv', 0
+ assert_select 'p.other-formats a.pdf', 0
+ assert_select 'p.other-formats a.atom'
+ assert_select '#csv-export-options', 0
+ end
+
def test_index_csv_filename_without_query_name_param
get :index, :params => {:format => 'csv'}
assert_response :success
@@ -3122,6 +3179,23 @@ class IssuesControllerTest < Redmine::ControllerTest
assert @response.body.starts_with?('%PDF')
end
+ def test_show_pdf_without_export_issues_permission_should_be_denied
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+ get :show, :params => {:id => 1, :format => 'pdf'}
+ assert_response :forbidden
+ end
+
+ def test_show_should_not_show_pdf_link_without_export_issues_permission
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+ get :show, :params => {:id => 1}
+ assert_response :success
+
+ assert_select 'p.other-formats a.pdf', 0
+ assert_select 'p.other-formats a.atom'
+ end
+
def test_export_to_pdf_with_utf8_u_fffd
issue = Issue.generate!(:subject => "�")
["en", "zh", "zh-TW", "ja", "ko", "ar"].each do |lang|
diff --git a/test/functional/reports_controller_test.rb b/test/functional/reports_controller_test.rb
index 96a56feeb..498bdad4c 100644
--- a/test/functional/reports_controller_test.rb
+++ b/test/functional/reports_controller_test.rb
@@ -261,6 +261,38 @@ class ReportsControllerTest < Redmine::ControllerTest
end
end
+ def test_issue_report_details_csv_without_export_issues_permission_should_be_denied
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+ get :issue_report_details, :params => {:id => 1, :detail => 'tracker', :format => 'csv'}
+ assert_response :forbidden
+ end
+
+ def test_issue_report_details_csv_should_require_export_issues_permission_on_subprojects
+ # User 2 is a non member of the public subproject 3 which has visible issues
+ @request.session[:user_id] = 2
+ Role.non_member.remove_permission! :export_issues
+
+ with_settings :display_subprojects_issues => '1' do
+ get :issue_report_details, :params => {:id => 1, :detail => 'tracker', :format => 'csv'}
+ assert_response :forbidden
+ end
+
+ with_settings :display_subprojects_issues => '0' do
+ get :issue_report_details, :params => {:id => 1, :detail => 'tracker', :format => 'csv'}
+ assert_response :success
+ end
+ end
+
+ def test_issue_report_details_should_not_show_csv_link_without_export_issues_permission
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_issues
+ get :issue_report_details, :params => {:id => 1, :detail => 'tracker'}
+ assert_response :success
+ assert_select 'p.other-formats', 0
+ assert_select '#csv-export-options', 0
+ end
+
def test_issue_report_details_with_tracker_detail_should_csv_export
project = Project.find(1)
tracker = project.trackers.find_by(:name => 'Support request')
diff --git a/test/functional/timelog_controller_test.rb b/test/functional/timelog_controller_test.rb
index e177eda07..45c0fef1b 100644
--- a/test/functional/timelog_controller_test.rb
+++ b/test/functional/timelog_controller_test.rb
@@ -1728,6 +1728,55 @@ class TimelogControllerTest < Redmine::ControllerTest
end
end
+ def test_index_should_not_include_csv_export_without_export_time_entries_permission
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_time_entries
+ get :index, :params => {:project_id => 1}
+ assert_response :success
+
+ assert_select 'p.other-formats a.csv', 0
+ assert_select 'p.other-formats a.atom'
+ assert_select '#csv-export-options', 0
+ end
+
+ def test_index_csv_should_require_export_time_entries_permission_on_every_project_of_the_entries
+ # User 2 is a non member of the public subproject 3 which has visible time entries
+ @request.session[:user_id] = 2
+ Role.non_member.remove_permission! :export_time_entries
+
+ get :index, :params => {:format => 'csv'}
+ assert_response :forbidden
+
+ with_settings :display_subprojects_issues => '0' do
+ get :index, :params => {:project_id => 1, :format => 'csv'}
+ assert_response :success
+ end
+ end
+
+ def test_index_csv_without_export_time_entries_permission_should_be_denied
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_time_entries
+ get :index, :params => {:project_id => 1, :format => 'csv'}
+ assert_response :forbidden
+ end
+
+ def test_report_should_not_include_csv_export_without_export_time_entries_permission
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_time_entries
+ get :report, :params => {:project_id => 1, :columns => 'month', :criteria => ['project']}
+ assert_response :success
+
+ assert_select 'p.other-formats', 0
+ assert_select '#csv-export-options', 0
+ end
+
+ def test_report_csv_without_export_time_entries_permission_should_be_denied
+ @request.session[:user_id] = 2
+ Role.find_by_name('Manager').remove_permission! :export_time_entries
+ get :report, :params => {:project_id => 1, :columns => 'month', :criteria => ['project'], :format => 'csv'}
+ assert_response :forbidden
+ end
+
def test_index_csv_all_projects
with_settings :date_format => '%m/%d/%Y' do
get :index, :params => {:format => 'csv'}
--
2.55.0