##// END OF EJS Templates
Use safe_attributes....
Jean-Philippe Lang -
r15287:dca56a0350c3
parent child
Show More
@@ -1,107 +1,110
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class TrackersController < ApplicationController
18 class TrackersController < ApplicationController
19 layout 'admin'
19 layout 'admin'
20
20
21 before_action :require_admin, :except => :index
21 before_action :require_admin, :except => :index
22 before_action :require_admin_or_api_request, :only => :index
22 before_action :require_admin_or_api_request, :only => :index
23 accept_api_auth :index
23 accept_api_auth :index
24
24
25 def index
25 def index
26 @trackers = Tracker.sorted.to_a
26 @trackers = Tracker.sorted.to_a
27 respond_to do |format|
27 respond_to do |format|
28 format.html { render :layout => false if request.xhr? }
28 format.html { render :layout => false if request.xhr? }
29 format.api
29 format.api
30 end
30 end
31 end
31 end
32
32
33 def new
33 def new
34 @tracker ||= Tracker.new(params[:tracker])
34 @tracker ||= Tracker.new
35 @tracker.safe_attributes = params[:tracker]
35 @trackers = Tracker.sorted.to_a
36 @trackers = Tracker.sorted.to_a
36 @projects = Project.all
37 @projects = Project.all
37 end
38 end
38
39
39 def create
40 def create
40 @tracker = Tracker.new(params[:tracker])
41 @tracker = Tracker.new
42 @tracker.safe_attributes = params[:tracker]
41 if @tracker.save
43 if @tracker.save
42 # workflow copy
44 # workflow copy
43 if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
45 if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
44 @tracker.workflow_rules.copy(copy_from)
46 @tracker.workflow_rules.copy(copy_from)
45 end
47 end
46 flash[:notice] = l(:notice_successful_create)
48 flash[:notice] = l(:notice_successful_create)
47 redirect_to trackers_path
49 redirect_to trackers_path
48 return
50 return
49 end
51 end
50 new
52 new
51 render :action => 'new'
53 render :action => 'new'
52 end
54 end
53
55
54 def edit
56 def edit
55 @tracker ||= Tracker.find(params[:id])
57 @tracker ||= Tracker.find(params[:id])
56 @projects = Project.all
58 @projects = Project.all
57 end
59 end
58
60
59 def update
61 def update
60 @tracker = Tracker.find(params[:id])
62 @tracker = Tracker.find(params[:id])
61 if @tracker.update_attributes(params[:tracker])
63 @tracker.safe_attributes = params[:tracker]
64 if @tracker.save
62 respond_to do |format|
65 respond_to do |format|
63 format.html {
66 format.html {
64 flash[:notice] = l(:notice_successful_update)
67 flash[:notice] = l(:notice_successful_update)
65 redirect_to trackers_path(:page => params[:page])
68 redirect_to trackers_path(:page => params[:page])
66 }
69 }
67 format.js { render :nothing => true }
70 format.js { render :nothing => true }
68 end
71 end
69 else
72 else
70 respond_to do |format|
73 respond_to do |format|
71 format.html {
74 format.html {
72 edit
75 edit
73 render :action => 'edit'
76 render :action => 'edit'
74 }
77 }
75 format.js { render :nothing => true, :status => 422 }
78 format.js { render :nothing => true, :status => 422 }
76 end
79 end
77 end
80 end
78 end
81 end
79
82
80 def destroy
83 def destroy
81 @tracker = Tracker.find(params[:id])
84 @tracker = Tracker.find(params[:id])
82 unless @tracker.issues.empty?
85 unless @tracker.issues.empty?
83 flash[:error] = l(:error_can_not_delete_tracker)
86 flash[:error] = l(:error_can_not_delete_tracker)
84 else
87 else
85 @tracker.destroy
88 @tracker.destroy
86 end
89 end
87 redirect_to trackers_path
90 redirect_to trackers_path
88 end
91 end
89
92
90 def fields
93 def fields
91 if request.post? && params[:trackers]
94 if request.post? && params[:trackers]
92 params[:trackers].each do |tracker_id, tracker_params|
95 params[:trackers].each do |tracker_id, tracker_params|
93 tracker = Tracker.find_by_id(tracker_id)
96 tracker = Tracker.find_by_id(tracker_id)
94 if tracker
97 if tracker
95 tracker.core_fields = tracker_params[:core_fields]
98 tracker.core_fields = tracker_params[:core_fields]
96 tracker.custom_field_ids = tracker_params[:custom_field_ids]
99 tracker.custom_field_ids = tracker_params[:custom_field_ids]
97 tracker.save
100 tracker.save
98 end
101 end
99 end
102 end
100 flash[:notice] = l(:notice_successful_update)
103 flash[:notice] = l(:notice_successful_update)
101 redirect_to fields_trackers_path
104 redirect_to fields_trackers_path
102 return
105 return
103 end
106 end
104 @trackers = Tracker.sorted.to_a
107 @trackers = Tracker.sorted.to_a
105 @custom_fields = IssueCustomField.all.sort
108 @custom_fields = IssueCustomField.all.sort
106 end
109 end
107 end
110 end
@@ -1,137 +1,146
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Tracker < ActiveRecord::Base
18 class Tracker < ActiveRecord::Base
19 include Redmine::SafeAttributes
19
20
20 CORE_FIELDS_UNDISABLABLE = %w(project_id tracker_id subject description priority_id is_private).freeze
21 CORE_FIELDS_UNDISABLABLE = %w(project_id tracker_id subject description priority_id is_private).freeze
21 # Fields that can be disabled
22 # Fields that can be disabled
22 # Other (future) fields should be appended, not inserted!
23 # Other (future) fields should be appended, not inserted!
23 CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio).freeze
24 CORE_FIELDS = %w(assigned_to_id category_id fixed_version_id parent_issue_id start_date due_date estimated_hours done_ratio).freeze
24 CORE_FIELDS_ALL = (CORE_FIELDS_UNDISABLABLE + CORE_FIELDS).freeze
25 CORE_FIELDS_ALL = (CORE_FIELDS_UNDISABLABLE + CORE_FIELDS).freeze
25
26
26 before_destroy :check_integrity
27 before_destroy :check_integrity
27 belongs_to :default_status, :class_name => 'IssueStatus'
28 belongs_to :default_status, :class_name => 'IssueStatus'
28 has_many :issues
29 has_many :issues
29 has_many :workflow_rules, :dependent => :delete_all do
30 has_many :workflow_rules, :dependent => :delete_all do
30 def copy(source_tracker)
31 def copy(source_tracker)
31 WorkflowRule.copy(source_tracker, nil, proxy_association.owner, nil)
32 WorkflowRule.copy(source_tracker, nil, proxy_association.owner, nil)
32 end
33 end
33 end
34 end
34
35
35 has_and_belongs_to_many :projects
36 has_and_belongs_to_many :projects
36 has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :association_foreign_key => 'custom_field_id'
37 has_and_belongs_to_many :custom_fields, :class_name => 'IssueCustomField', :join_table => "#{table_name_prefix}custom_fields_trackers#{table_name_suffix}", :association_foreign_key => 'custom_field_id'
37 acts_as_positioned
38 acts_as_positioned
38
39
39 attr_protected :fields_bits
40 attr_protected :fields_bits
40
41
41 validates_presence_of :default_status
42 validates_presence_of :default_status
42 validates_presence_of :name
43 validates_presence_of :name
43 validates_uniqueness_of :name
44 validates_uniqueness_of :name
44 validates_length_of :name, :maximum => 30
45 validates_length_of :name, :maximum => 30
45
46
46 scope :sorted, lambda { order(:position) }
47 scope :sorted, lambda { order(:position) }
47 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
48 scope :named, lambda {|arg| where("LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip)}
48
49
49 # Returns the trackers that are visible by the user.
50 # Returns the trackers that are visible by the user.
50 #
51 #
51 # Examples:
52 # Examples:
52 # project.trackers.visible(user)
53 # project.trackers.visible(user)
53 # => returns the trackers that are visible by the user in project
54 # => returns the trackers that are visible by the user in project
54 #
55 #
55 # Tracker.visible(user)
56 # Tracker.visible(user)
56 # => returns the trackers that are visible by the user in at least on project
57 # => returns the trackers that are visible by the user in at least on project
57 scope :visible, lambda {|*args|
58 scope :visible, lambda {|*args|
58 user = args.shift || User.current
59 user = args.shift || User.current
59 condition = Project.allowed_to_condition(user, :view_issues) do |role, user|
60 condition = Project.allowed_to_condition(user, :view_issues) do |role, user|
60 unless role.permissions_all_trackers?(:view_issues)
61 unless role.permissions_all_trackers?(:view_issues)
61 tracker_ids = role.permissions_tracker_ids(:view_issues)
62 tracker_ids = role.permissions_tracker_ids(:view_issues)
62 if tracker_ids.any?
63 if tracker_ids.any?
63 "#{Tracker.table_name}.id IN (#{tracker_ids.join(',')})"
64 "#{Tracker.table_name}.id IN (#{tracker_ids.join(',')})"
64 else
65 else
65 '1=0'
66 '1=0'
66 end
67 end
67 end
68 end
68 end
69 end
69 joins(:projects).where(condition).distinct
70 joins(:projects).where(condition).distinct
70 }
71 }
71
72
73 safe_attributes 'name',
74 'default_status_id',
75 'is_in_roadmap',
76 'core_fields',
77 'position',
78 'custom_field_ids',
79 'project_ids'
80
72 def to_s; name end
81 def to_s; name end
73
82
74 def <=>(tracker)
83 def <=>(tracker)
75 position <=> tracker.position
84 position <=> tracker.position
76 end
85 end
77
86
78 # Returns an array of IssueStatus that are used
87 # Returns an array of IssueStatus that are used
79 # in the tracker's workflows
88 # in the tracker's workflows
80 def issue_statuses
89 def issue_statuses
81 @issue_statuses ||= IssueStatus.where(:id => issue_status_ids).to_a.sort
90 @issue_statuses ||= IssueStatus.where(:id => issue_status_ids).to_a.sort
82 end
91 end
83
92
84 def issue_status_ids
93 def issue_status_ids
85 if new_record?
94 if new_record?
86 []
95 []
87 else
96 else
88 @issue_status_ids ||= WorkflowTransition.where(:tracker_id => id).distinct.pluck(:old_status_id, :new_status_id).flatten.uniq
97 @issue_status_ids ||= WorkflowTransition.where(:tracker_id => id).distinct.pluck(:old_status_id, :new_status_id).flatten.uniq
89 end
98 end
90 end
99 end
91
100
92 def disabled_core_fields
101 def disabled_core_fields
93 i = -1
102 i = -1
94 @disabled_core_fields ||= CORE_FIELDS.select { i += 1; (fields_bits || 0) & (2 ** i) != 0}
103 @disabled_core_fields ||= CORE_FIELDS.select { i += 1; (fields_bits || 0) & (2 ** i) != 0}
95 end
104 end
96
105
97 def core_fields
106 def core_fields
98 CORE_FIELDS - disabled_core_fields
107 CORE_FIELDS - disabled_core_fields
99 end
108 end
100
109
101 def core_fields=(fields)
110 def core_fields=(fields)
102 raise ArgumentError.new("Tracker.core_fields takes an array") unless fields.is_a?(Array)
111 raise ArgumentError.new("Tracker.core_fields takes an array") unless fields.is_a?(Array)
103
112
104 bits = 0
113 bits = 0
105 CORE_FIELDS.each_with_index do |field, i|
114 CORE_FIELDS.each_with_index do |field, i|
106 unless fields.include?(field)
115 unless fields.include?(field)
107 bits |= 2 ** i
116 bits |= 2 ** i
108 end
117 end
109 end
118 end
110 self.fields_bits = bits
119 self.fields_bits = bits
111 @disabled_core_fields = nil
120 @disabled_core_fields = nil
112 core_fields
121 core_fields
113 end
122 end
114
123
115 # Returns the fields that are disabled for all the given trackers
124 # Returns the fields that are disabled for all the given trackers
116 def self.disabled_core_fields(trackers)
125 def self.disabled_core_fields(trackers)
117 if trackers.present?
126 if trackers.present?
118 trackers.map(&:disabled_core_fields).reduce(:&)
127 trackers.map(&:disabled_core_fields).reduce(:&)
119 else
128 else
120 []
129 []
121 end
130 end
122 end
131 end
123
132
124 # Returns the fields that are enabled for one tracker at least
133 # Returns the fields that are enabled for one tracker at least
125 def self.core_fields(trackers)
134 def self.core_fields(trackers)
126 if trackers.present?
135 if trackers.present?
127 trackers.uniq.map(&:core_fields).reduce(:|)
136 trackers.uniq.map(&:core_fields).reduce(:|)
128 else
137 else
129 CORE_FIELDS.dup
138 CORE_FIELDS.dup
130 end
139 end
131 end
140 end
132
141
133 private
142 private
134 def check_integrity
143 def check_integrity
135 raise Exception.new("Cannot delete tracker") if Issue.where(:tracker_id => self.id).any?
144 raise Exception.new("Cannot delete tracker") if Issue.where(:tracker_id => self.id).any?
136 end
145 end
137 end
146 end
General Comments 0
You need to be logged in to leave comments. Login now