diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb
index de51676..76bb5d3 100644
--- a/app/controllers/issues_controller.rb
+++ b/app/controllers/issues_controller.rb
@@ -19,6 +19,7 @@ class IssuesController < ApplicationController
menu_item :new_issue, :only => :new
before_filter :find_issue, :only => [:show, :edit, :reply]
+ before_filter :check_issue_perms, :only => [:show, :edit]
before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
before_filter :find_project, :only => [:new, :update_form, :preview]
before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :update_form, :context_menu]
@@ -63,6 +64,7 @@ class IssuesController < ApplicationController
:conditions => @query.statement,
:limit => limit,
:offset => @issue_pages.current.offset
+ @issues.map!{ |issue| issue_without_hidden_fields(issue) } # don't show forbidden fields
respond_to do |format|
format.html {
if @query.grouped?
@@ -113,6 +115,8 @@ class IssuesController < ApplicationController
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@edit_allowed = User.current.allowed_to?(:edit_issues, @project)
+ @issue = issue_without_hidden_fields(@issue)
+ @journals = journals_without_hidden_entries(@journals)
@priorities = IssuePriority.all
@time_entry = TimeEntry.new
respond_to do |format|
@@ -355,6 +359,9 @@ class IssuesController < ApplicationController
events += Version.find(:all, :include => :project,
:conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
+
+ events += @project.related_versions.select { |v| ((v.project.id != @project.id) && (@gantt.date_from..@gantt.date_to).include?(v.effective_date)) }
+
@gantt.events = events
end
@@ -387,7 +394,9 @@ class IssuesController < ApplicationController
)
events += Version.find(:all, :include => :project,
:conditions => ["(#{@query.project_statement}) AND effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
-
+
+ events += @project.related_versions.select { |v| ((v.project.id != @project.id) && (@calendar.startdt..@calendar.enddt).include?(v.effective_date)) }
+
@calendar.events = events
end
@@ -471,6 +480,55 @@ private
render_404
end
+ def issue_fixed_version_allowed?(issue)
+ ((not issue.fixed_version) ||
+ (User.current.allowed_to?(:view_issues,
+ issue.fixed_version.project)))
+ end
+
+ def check_issue_perms
+ @forbidden_fixed_version = (not issue_fixed_version_allowed?(@issue))
+ end
+
+ ## Filter journals to remove non allowed entries. We want here
+ ## to handle the _specific case_ of a user 'Joe' browsing 'Project
+ ## B' issues. 'Project B' is a subproject of 'Project A' and
+ ## inherits its versions. Joe can't see 'Project A, but issue
+ ## fixed_versions has been set at some point to a version belonging
+ ## to 'Project A', by an other member of 'Project B'. Joe should not
+ ## see this sensitive information.
+ def journals_without_hidden_entries(journals)
+ p = @project
+ forbidden_parents = []
+ while p = p.parent do
+ forbidden_parents.push p.id unless User.current.allowed_to?(:view_issues, p)
+ end
+
+ unless (journals.empty? || forbidden_parents.empty?)
+ journals = journals.dup
+ journals.delete_if do |j|
+ j.details.map{ |d| (d.property == 'attr' &&
+ d.prop_key == 'fixed_version_id' &&
+ (
+ (d.old_value && forbidden_parents.include?(Version.find(d.old_value.to_i).project_id)) ||
+ (d.value && forbidden_parents.include?(Version.find(d.value.to_i).project_id))
+ )
+ ) }.include?(true)
+ end
+ end
+ journals
+ end
+
+ ## Filter issue to remove non allowed fields. For more information,
+ ## see documentation of journals_without_hidden_entries
+ def issue_without_hidden_fields(issue)
+ unless issue_fixed_version_allowed?(issue)
+ issue = issue.dup
+ issue.fixed_version = nil
+ end
+ issue
+ end
+
# Retrieve query from session or build a new query
def retrieve_query
if !params[:query_id].blank?
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index f3280cc..fafa3b9 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -221,7 +221,7 @@ class ProjectsController < ApplicationController
def add_file
if request.post?
- container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
+ container = (params[:version_id].blank? ? @project : @project.related_versions.find { |v| v.id.to_s == params[:version_id] })
attachments = attach_files(container, params[:attachments])
if !attachments.empty? && Setting.notified_events.include?('file_added')
Mailer.deliver_attachments_added(attachments)
@@ -229,7 +229,7 @@ class ProjectsController < ApplicationController
redirect_to :controller => 'projects', :action => 'list_files', :id => @project
return
end
- @versions = @project.versions.sort
+ @versions = @project.related_versions.sort
end
def list_files
@@ -240,7 +240,7 @@ class ProjectsController < ApplicationController
'downloads' => "#{Attachment.table_name}.downloads"
@containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
- @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
+ @containers += Version.find(:all, :include => :attachments, :order => sort_clause, :conditions => ['versions.id IN (?)', @project.related_versions.map {|v| v.id}]).sort.reverse
render :layout => !request.xhr?
end
@@ -248,14 +248,13 @@ class ProjectsController < ApplicationController
def changelog
@trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
retrieve_selected_tracker_ids(@trackers)
- @versions = @project.versions.sort
+ @versions = @project.related_versions.sort
end
def roadmap
@trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true])
retrieve_selected_tracker_ids(@trackers)
- @versions = @project.versions.sort
- @versions = @versions.select {|v| !v.completed? } unless params[:completed]
+ @versions = retrieve_related_versions
end
def activity
@@ -316,6 +315,12 @@ private
render_404
end
+ def retrieve_related_versions
+ related_versions=@project.related_versions
+ # should this test be moved to projects_helper.rb : version_visible_issues ?
+ params[:completed] ? related_versions : related_versions.select {|v| !v.completed? }
+ end
+
def retrieve_selected_tracker_ids(selectable_trackers)
if ids = params[:tracker_ids]
@selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index d192a19..a2ce58c 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -31,7 +31,7 @@ class ReportsController < ApplicationController
render :template => "reports/issue_report_details"
when "version"
@field = "fixed_version_id"
- @rows = @project.versions.sort
+ @rows = @project.related_versions.sort
@data = issues_by_version
@report_title = l(:field_version)
render :template => "reports/issue_report_details"
@@ -67,7 +67,7 @@ class ReportsController < ApplicationController
render :template => "reports/issue_report_details"
else
@trackers = @project.trackers
- @versions = @project.versions.sort
+ @versions = @project.related_versions.sort
@priorities = IssuePriority.all
@categories = @project.issue_categories
@assignees = @project.members.collect { |m| m.user }
diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb
index c269432..127e6ef 100644
--- a/app/controllers/versions_controller.rb
+++ b/app/controllers/versions_controller.rb
@@ -19,7 +19,11 @@ class VersionsController < ApplicationController
menu_item :roadmap
before_filter :find_project, :authorize
+ helper :projects
+ include ProjectsHelper
+
def show
+ @issues = version_visible_issues(@version,@project,@project.trackers.collect { |x| x.id.to_s })
end
def edit
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 912450c..bfb2293 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -33,6 +33,43 @@ module ProjectsHelper
]
tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
end
+
+ ## return an array of the issues attached to version that a user should see
+ ## As a convenience, allow limitation by trackers with Array of tracker_ids
+ def version_visible_issues(version,project,tracker_ids=[],user=User.current)
+ # Returns void when user can not see issues or if tracker_ids is void
+ return [] if ((not user.allowed_to?(:view_issues,
+ version.project)) or
+ tracker_ids.empty?)
+ # user can see project issues, carry on, retrieve them ...
+ issues = version.
+ fixed_issues.find(:all,
+ :include => [:status, :tracker],
+ :conditions => ["tracker_id in (#{tracker_ids.join(',')})"],
+ :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
+ # Only keep issues from self, parent or children,
+ related_projects = project.self_and_descendants.map { |t_item| t_item.id }
+
+ # do we need parents at all?
+ p = project
+ while p = p.parent do
+ related_projects.push p.id
+ end
+ related_projects = related_projects.select{|x| user.allowed_to?(:view_issues, Project.find(x))}
+
+ # now only keep related issues. (issues from brother projects are discarded)
+ issues.select {|x| related_projects.include?(x.project.id) }
+ end
+
+ def grouped_versions_for_select(versions_list)
+ return [] if versions_list == nil
+
+ grouped_structure = []
+ versions_list.each do |prj, ver|
+ grouped_structure.push [prj.name, ver.collect { |v| [v.name, v.id] }]
+ end
+ grouped_structure
+ end
def parent_project_select_tag(project)
options = '' + project_tree_options_for_select(project.possible_parents, :selected => project.parent)
diff --git a/app/models/project.rb b/app/models/project.rb
index 8799e3b..1ebf97a 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -274,6 +274,35 @@ class Project < ActiveRecord::Base
members.select {|m| m.mail_notification? || m.user.mail_notification?}.collect {|m| m.user.mail}
end
+ ## Return versions attached to self, and to parents if relevant.
+ ## Sorting is done in the SQL request to take advantage of cache
+ def related_versions(user=User.current)
+ p = self
+ parent_ids = [self.id]
+ while p = p.parent do
+ parent_ids.push p.id if user.allowed_to?(:view_issues, p)
+ end
+
+ Version.find(:all,
+ :conditions => {:project_id => parent_ids},
+ :order => "effective_date ASC, name ASC ")
+ end
+
+ def grouped_versions(version_list=nil, user=User.current)
+ version_list ||= related_versions(user)
+
+ p, i = self, 0
+ parent_nesting = {}
+ parent_nesting[p.id] = i
+
+ while p = p.parent do
+ i += 1
+ parent_nesting[p.id] = i
+ end
+
+ version_list.group_by(&:project).sort_by { |item| parent_nesting[item.first.id] }
+ end
+
# Returns an array of all custom fields enabled for project issues
# (explictly associated custom fields and custom fields enabled for all projects)
def all_issue_custom_fields
diff --git a/app/models/query.rb b/app/models/query.rb
index 7ce95f0..174a22d 100644
--- a/app/models/query.rb
+++ b/app/models/query.rb
@@ -89,6 +89,7 @@ class Query < ActiveRecord::Base
@@operators_by_filter_type = { :list => [ "=", "!" ],
:list_status => [ "o", "=", "!", "c", "*" ],
:list_optional => [ "=", "!", "!*", "*" ],
+ :list_grouped => [ "=", "!", "!*", "*" ],
:list_subprojects => [ "*", "!*", "=" ],
:date => [ "
<%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
-<%= content_tag('p', f.select(:fixed_version_id, - (@project.versions.sort.collect {|v| [v.name, v.id]}), - { :include_blank => true })) unless @project.versions.empty? %> +<%= unless (@issue.fixed_version && + (not User.current.allowed_to?(:view_issues, + @issue.fixed_version.project)) || + @project.related_versions.empty?) then + content_tag('p', f.select_ex(:fixed_version_id, + grouped_versions_for_select(@project.grouped_versions), + { :include_blank => true })) + end %>diff --git a/app/views/issues/context_menu.rhtml b/app/views/issues/context_menu.rhtml index ae9a1af..49e51b4 100644 --- a/app/views/issues/context_menu.rhtml +++ b/app/views/issues/context_menu.rhtml @@ -27,14 +27,24 @@ <% end -%> - <% unless @project.nil? || @project.versions.empty? -%> + <% unless @project.nil? || @project.related_versions.empty? -%>
<%= select_tag "version_id", content_tag('option', '') + - options_from_collection_for_select(@versions, "id", "name") %>
+ grouped_options_for_select(grouped_versions_for_select(@project.grouped_versions)) %> <% end %><%= render :partial => 'attachments/form' %>
diff --git a/app/views/projects/roadmap.rhtml b/app/views/projects/roadmap.rhtml index bcba13a..7f3f583 100644 --- a/app/views/projects/roadmap.rhtml +++ b/app/views/projects/roadmap.rhtml @@ -4,28 +4,36 @@<%= l(:label_no_data) %>
<% else %>