@@ -1,60 +1,73 | |||||
1 | class ContextMenusController < ApplicationController |
|
1 | class ContextMenusController < ApplicationController | |
2 | helper :watchers |
|
2 | helper :watchers | |
3 | helper :issues |
|
3 | helper :issues | |
4 |
|
4 | |||
5 | def issues |
|
5 | def issues | |
6 | @issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project) |
|
6 | @issues = Issue.visible.all(:conditions => {:id => params[:ids]}, :include => :project) | |
7 |
|
7 | |||
8 | if (@issues.size == 1) |
|
8 | if (@issues.size == 1) | |
9 | @issue = @issues.first |
|
9 | @issue = @issues.first | |
10 | @allowed_statuses = @issue.new_statuses_allowed_to(User.current) |
|
10 | @allowed_statuses = @issue.new_statuses_allowed_to(User.current) | |
11 | else |
|
11 | else | |
12 | @allowed_statuses = @issues.map do |i| |
|
12 | @allowed_statuses = @issues.map do |i| | |
13 | i.new_statuses_allowed_to(User.current) |
|
13 | i.new_statuses_allowed_to(User.current) | |
14 | end.inject do |memo,s| |
|
14 | end.inject do |memo,s| | |
15 | memo & s |
|
15 | memo & s | |
16 | end |
|
16 | end | |
17 | end |
|
17 | end | |
18 | @projects = @issues.collect(&:project).compact.uniq |
|
18 | @projects = @issues.collect(&:project).compact.uniq | |
19 | @project = @projects.first if @projects.size == 1 |
|
19 | @project = @projects.first if @projects.size == 1 | |
20 |
|
20 | |||
21 | @can = {:edit => User.current.allowed_to?(:edit_issues, @projects), |
|
21 | @can = {:edit => User.current.allowed_to?(:edit_issues, @projects), | |
22 | :log_time => (@project && User.current.allowed_to?(:log_time, @project)), |
|
22 | :log_time => (@project && User.current.allowed_to?(:log_time, @project)), | |
23 | :update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)), |
|
23 | :update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)), | |
24 | :move => (@project && User.current.allowed_to?(:move_issues, @project)), |
|
24 | :move => (@project && User.current.allowed_to?(:move_issues, @project)), | |
25 | :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), |
|
25 | :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)), | |
26 | :delete => User.current.allowed_to?(:delete_issues, @projects) |
|
26 | :delete => User.current.allowed_to?(:delete_issues, @projects) | |
27 | } |
|
27 | } | |
28 | if @project |
|
28 | if @project | |
29 | if @issue |
|
29 | if @issue | |
30 | @assignables = @issue.assignable_users |
|
30 | @assignables = @issue.assignable_users | |
31 | else |
|
31 | else | |
32 | @assignables = @project.assignable_users |
|
32 | @assignables = @project.assignable_users | |
33 | end |
|
33 | end | |
34 | @trackers = @project.trackers |
|
34 | @trackers = @project.trackers | |
35 | else |
|
35 | else | |
36 | #when multiple projects, we only keep the intersection of each set |
|
36 | #when multiple projects, we only keep the intersection of each set | |
37 | @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a} |
|
37 | @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a} | |
38 | @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t} |
|
38 | @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t} | |
39 | end |
|
39 | end | |
40 |
|
40 | |||
41 | @priorities = IssuePriority.active.reverse |
|
41 | @priorities = IssuePriority.active.reverse | |
42 | @statuses = IssueStatus.find(:all, :order => 'position') |
|
42 | @statuses = IssueStatus.find(:all, :order => 'position') | |
43 | @back = back_url |
|
43 | @back = back_url | |
44 |
|
44 | |||
|
45 | @options_by_custom_field = {} | |||
|
46 | if @can[:edit] | |||
|
47 | custom_fields = @issues.map(&:available_custom_fields).inject {|memo, f| memo & f}.select do |f| | |||
|
48 | %w(bool list user version).include?(f.field_format) && !f.multiple? | |||
|
49 | end | |||
|
50 | custom_fields.each do |field| | |||
|
51 | values = field.possible_values_options(@projects) | |||
|
52 | if values.any? | |||
|
53 | @options_by_custom_field[field] = values | |||
|
54 | end | |||
|
55 | end | |||
|
56 | end | |||
|
57 | ||||
45 | render :layout => false |
|
58 | render :layout => false | |
46 | end |
|
59 | end | |
47 |
|
60 | |||
48 | def time_entries |
|
61 | def time_entries | |
49 | @time_entries = TimeEntry.all( |
|
62 | @time_entries = TimeEntry.all( | |
50 | :conditions => {:id => params[:ids]}, :include => :project) |
|
63 | :conditions => {:id => params[:ids]}, :include => :project) | |
51 | @projects = @time_entries.collect(&:project).compact.uniq |
|
64 | @projects = @time_entries.collect(&:project).compact.uniq | |
52 | @project = @projects.first if @projects.size == 1 |
|
65 | @project = @projects.first if @projects.size == 1 | |
53 | @activities = TimeEntryActivity.shared.active |
|
66 | @activities = TimeEntryActivity.shared.active | |
54 | @can = {:edit => User.current.allowed_to?(:edit_time_entries, @projects), |
|
67 | @can = {:edit => User.current.allowed_to?(:edit_time_entries, @projects), | |
55 | :delete => User.current.allowed_to?(:edit_time_entries, @projects) |
|
68 | :delete => User.current.allowed_to?(:edit_time_entries, @projects) | |
56 | } |
|
69 | } | |
57 | @back = back_url |
|
70 | @back = back_url | |
58 | render :layout => false |
|
71 | render :layout => false | |
59 | end |
|
72 | end | |
60 | end |
|
73 | end |
@@ -1,36 +1,43 | |||||
1 | # encoding: utf-8 |
|
1 | # encoding: utf-8 | |
2 | # |
|
2 | # | |
3 | # Redmine - project management software |
|
3 | # Redmine - project management software | |
4 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
4 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
5 | # |
|
5 | # | |
6 | # This program is free software; you can redistribute it and/or |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU General Public License |
|
7 | # modify it under the terms of the GNU General Public License | |
8 | # as published by the Free Software Foundation; either version 2 |
|
8 | # as published by the Free Software Foundation; either version 2 | |
9 | # of the License, or (at your option) any later version. |
|
9 | # of the License, or (at your option) any later version. | |
10 | # |
|
10 | # | |
11 | # This program is distributed in the hope that it will be useful, |
|
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. |
|
14 | # GNU General Public License for more details. | |
15 | # |
|
15 | # | |
16 | # You should have received a copy of the GNU General Public License |
|
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program; if not, write to the Free Software |
|
17 | # along with this program; if not, write to the Free Software | |
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 |
|
19 | |||
20 | module ContextMenusHelper |
|
20 | module ContextMenusHelper | |
21 | def context_menu_link(name, url, options={}) |
|
21 | def context_menu_link(name, url, options={}) | |
22 | options[:class] ||= '' |
|
22 | options[:class] ||= '' | |
23 | if options.delete(:selected) |
|
23 | if options.delete(:selected) | |
24 | options[:class] << ' icon-checked disabled' |
|
24 | options[:class] << ' icon-checked disabled' | |
25 | options[:disabled] = true |
|
25 | options[:disabled] = true | |
26 | end |
|
26 | end | |
27 | if options.delete(:disabled) |
|
27 | if options.delete(:disabled) | |
28 | options.delete(:method) |
|
28 | options.delete(:method) | |
29 | options.delete(:confirm) |
|
29 | options.delete(:confirm) | |
30 | options.delete(:onclick) |
|
30 | options.delete(:onclick) | |
31 | options[:class] << ' disabled' |
|
31 | options[:class] << ' disabled' | |
32 | url = '#' |
|
32 | url = '#' | |
33 | end |
|
33 | end | |
34 | link_to h(name), url, options |
|
34 | link_to h(name), url, options | |
35 | end |
|
35 | end | |
|
36 | ||||
|
37 | def bulk_update_custom_field_context_menu_link(field, text, value) | |||
|
38 | context_menu_link h(text), | |||
|
39 | {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'custom_field_values' => {field.id => value}}, :back_url => @back}, | |||
|
40 | :method => :post, | |||
|
41 | :selected => (@issue && @issue.custom_field_value(field) == value) | |||
|
42 | end | |||
36 | end |
|
43 | end |
@@ -1,217 +1,221 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 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 CustomField < ActiveRecord::Base |
|
18 | class CustomField < ActiveRecord::Base | |
19 | include Redmine::SubclassFactory |
|
19 | include Redmine::SubclassFactory | |
20 |
|
20 | |||
21 | has_many :custom_values, :dependent => :delete_all |
|
21 | has_many :custom_values, :dependent => :delete_all | |
22 | acts_as_list :scope => 'type = \'#{self.class}\'' |
|
22 | acts_as_list :scope => 'type = \'#{self.class}\'' | |
23 | serialize :possible_values |
|
23 | serialize :possible_values | |
24 |
|
24 | |||
25 | validates_presence_of :name, :field_format |
|
25 | validates_presence_of :name, :field_format | |
26 | validates_uniqueness_of :name, :scope => :type |
|
26 | validates_uniqueness_of :name, :scope => :type | |
27 | validates_length_of :name, :maximum => 30 |
|
27 | validates_length_of :name, :maximum => 30 | |
28 | validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats |
|
28 | validates_inclusion_of :field_format, :in => Redmine::CustomFieldFormat.available_formats | |
29 |
|
29 | |||
30 | validate :validate_custom_field |
|
30 | validate :validate_custom_field | |
31 | before_validation :set_searchable |
|
31 | before_validation :set_searchable | |
32 |
|
32 | |||
33 | def initialize(attributes=nil, *args) |
|
33 | def initialize(attributes=nil, *args) | |
34 | super |
|
34 | super | |
35 | self.possible_values ||= [] |
|
35 | self.possible_values ||= [] | |
36 | end |
|
36 | end | |
37 |
|
37 | |||
38 | def set_searchable |
|
38 | def set_searchable | |
39 | # make sure these fields are not searchable |
|
39 | # make sure these fields are not searchable | |
40 | self.searchable = false if %w(int float date bool).include?(field_format) |
|
40 | self.searchable = false if %w(int float date bool).include?(field_format) | |
41 | # make sure only these fields can have multiple values |
|
41 | # make sure only these fields can have multiple values | |
42 | self.multiple = false unless %w(list user version).include?(field_format) |
|
42 | self.multiple = false unless %w(list user version).include?(field_format) | |
43 | true |
|
43 | true | |
44 | end |
|
44 | end | |
45 |
|
45 | |||
46 | def validate_custom_field |
|
46 | def validate_custom_field | |
47 | if self.field_format == "list" |
|
47 | if self.field_format == "list" | |
48 | errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty? |
|
48 | errors.add(:possible_values, :blank) if self.possible_values.nil? || self.possible_values.empty? | |
49 | errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array |
|
49 | errors.add(:possible_values, :invalid) unless self.possible_values.is_a? Array | |
50 | end |
|
50 | end | |
51 |
|
51 | |||
52 | if regexp.present? |
|
52 | if regexp.present? | |
53 | begin |
|
53 | begin | |
54 | Regexp.new(regexp) |
|
54 | Regexp.new(regexp) | |
55 | rescue |
|
55 | rescue | |
56 | errors.add(:regexp, :invalid) |
|
56 | errors.add(:regexp, :invalid) | |
57 | end |
|
57 | end | |
58 | end |
|
58 | end | |
59 |
|
59 | |||
60 | if default_value.present? && !valid_field_value?(default_value) |
|
60 | if default_value.present? && !valid_field_value?(default_value) | |
61 | errors.add(:default_value, :invalid) |
|
61 | errors.add(:default_value, :invalid) | |
62 | end |
|
62 | end | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
65 | def possible_values_options(obj=nil) |
|
65 | def possible_values_options(obj=nil) | |
66 | case field_format |
|
66 | case field_format | |
67 | when 'user', 'version' |
|
67 | when 'user', 'version' | |
68 | if obj.respond_to?(:project) && obj.project |
|
68 | if obj.respond_to?(:project) && obj.project | |
69 | case field_format |
|
69 | case field_format | |
70 | when 'user' |
|
70 | when 'user' | |
71 | obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]} |
|
71 | obj.project.users.sort.collect {|u| [u.to_s, u.id.to_s]} | |
72 | when 'version' |
|
72 | when 'version' | |
73 | obj.project.shared_versions.sort.collect {|u| [u.to_s, u.id.to_s]} |
|
73 | obj.project.shared_versions.sort.collect {|u| [u.to_s, u.id.to_s]} | |
74 | end |
|
74 | end | |
75 | elsif obj.is_a?(Array) |
|
75 | elsif obj.is_a?(Array) | |
76 | obj.collect {|o| possible_values_options(o)}.inject {|memo, v| memo & v} |
|
76 | obj.collect {|o| possible_values_options(o)}.inject {|memo, v| memo & v} | |
77 | else |
|
77 | else | |
78 | [] |
|
78 | [] | |
79 | end |
|
79 | end | |
|
80 | when 'bool' | |||
|
81 | [[l(:general_text_Yes), '1'], [l(:general_text_No), '0']] | |||
80 | else |
|
82 | else | |
81 |
read_attribute |
|
83 | read_attribute(:possible_values) || [] | |
82 | end |
|
84 | end | |
83 | end |
|
85 | end | |
84 |
|
86 | |||
85 | def possible_values(obj=nil) |
|
87 | def possible_values(obj=nil) | |
86 | case field_format |
|
88 | case field_format | |
87 | when 'user', 'version' |
|
89 | when 'user', 'version' | |
88 | possible_values_options(obj).collect(&:last) |
|
90 | possible_values_options(obj).collect(&:last) | |
|
91 | when 'bool' | |||
|
92 | ['1', '0'] | |||
89 | else |
|
93 | else | |
90 | read_attribute :possible_values |
|
94 | read_attribute :possible_values | |
91 | end |
|
95 | end | |
92 | end |
|
96 | end | |
93 |
|
97 | |||
94 | # Makes possible_values accept a multiline string |
|
98 | # Makes possible_values accept a multiline string | |
95 | def possible_values=(arg) |
|
99 | def possible_values=(arg) | |
96 | if arg.is_a?(Array) |
|
100 | if arg.is_a?(Array) | |
97 | write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?}) |
|
101 | write_attribute(:possible_values, arg.compact.collect(&:strip).select {|v| !v.blank?}) | |
98 | else |
|
102 | else | |
99 | self.possible_values = arg.to_s.split(/[\n\r]+/) |
|
103 | self.possible_values = arg.to_s.split(/[\n\r]+/) | |
100 | end |
|
104 | end | |
101 | end |
|
105 | end | |
102 |
|
106 | |||
103 | def cast_value(value) |
|
107 | def cast_value(value) | |
104 | casted = nil |
|
108 | casted = nil | |
105 | unless value.blank? |
|
109 | unless value.blank? | |
106 | case field_format |
|
110 | case field_format | |
107 | when 'string', 'text', 'list' |
|
111 | when 'string', 'text', 'list' | |
108 | casted = value |
|
112 | casted = value | |
109 | when 'date' |
|
113 | when 'date' | |
110 | casted = begin; value.to_date; rescue; nil end |
|
114 | casted = begin; value.to_date; rescue; nil end | |
111 | when 'bool' |
|
115 | when 'bool' | |
112 | casted = (value == '1' ? true : false) |
|
116 | casted = (value == '1' ? true : false) | |
113 | when 'int' |
|
117 | when 'int' | |
114 | casted = value.to_i |
|
118 | casted = value.to_i | |
115 | when 'float' |
|
119 | when 'float' | |
116 | casted = value.to_f |
|
120 | casted = value.to_f | |
117 | when 'user', 'version' |
|
121 | when 'user', 'version' | |
118 | casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i)) |
|
122 | casted = (value.blank? ? nil : field_format.classify.constantize.find_by_id(value.to_i)) | |
119 | end |
|
123 | end | |
120 | end |
|
124 | end | |
121 | casted |
|
125 | casted | |
122 | end |
|
126 | end | |
123 |
|
127 | |||
124 | # Returns a ORDER BY clause that can used to sort customized |
|
128 | # Returns a ORDER BY clause that can used to sort customized | |
125 | # objects by their value of the custom field. |
|
129 | # objects by their value of the custom field. | |
126 | # Returns false, if the custom field can not be used for sorting. |
|
130 | # Returns false, if the custom field can not be used for sorting. | |
127 | def order_statement |
|
131 | def order_statement | |
128 | return nil if multiple? |
|
132 | return nil if multiple? | |
129 | case field_format |
|
133 | case field_format | |
130 | when 'string', 'text', 'list', 'date', 'bool' |
|
134 | when 'string', 'text', 'list', 'date', 'bool' | |
131 | # COALESCE is here to make sure that blank and NULL values are sorted equally |
|
135 | # COALESCE is here to make sure that blank and NULL values are sorted equally | |
132 | "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" + |
|
136 | "COALESCE((SELECT cv_sort.value FROM #{CustomValue.table_name} cv_sort" + | |
133 | " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" + |
|
137 | " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" + | |
134 | " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" + |
|
138 | " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" + | |
135 | " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')" |
|
139 | " AND cv_sort.custom_field_id=#{id} LIMIT 1), '')" | |
136 | when 'int', 'float' |
|
140 | when 'int', 'float' | |
137 | # Make the database cast values into numeric |
|
141 | # Make the database cast values into numeric | |
138 | # Postgresql will raise an error if a value can not be casted! |
|
142 | # Postgresql will raise an error if a value can not be casted! | |
139 | # CustomValue validations should ensure that it doesn't occur |
|
143 | # CustomValue validations should ensure that it doesn't occur | |
140 | "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" + |
|
144 | "(SELECT CAST(cv_sort.value AS decimal(60,3)) FROM #{CustomValue.table_name} cv_sort" + | |
141 | " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" + |
|
145 | " WHERE cv_sort.customized_type='#{self.class.customized_class.name}'" + | |
142 | " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" + |
|
146 | " AND cv_sort.customized_id=#{self.class.customized_class.table_name}.id" + | |
143 | " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)" |
|
147 | " AND cv_sort.custom_field_id=#{id} AND cv_sort.value <> '' AND cv_sort.value IS NOT NULL LIMIT 1)" | |
144 | else |
|
148 | else | |
145 | nil |
|
149 | nil | |
146 | end |
|
150 | end | |
147 | end |
|
151 | end | |
148 |
|
152 | |||
149 | def <=>(field) |
|
153 | def <=>(field) | |
150 | position <=> field.position |
|
154 | position <=> field.position | |
151 | end |
|
155 | end | |
152 |
|
156 | |||
153 | def self.customized_class |
|
157 | def self.customized_class | |
154 | self.name =~ /^(.+)CustomField$/ |
|
158 | self.name =~ /^(.+)CustomField$/ | |
155 | begin; $1.constantize; rescue nil; end |
|
159 | begin; $1.constantize; rescue nil; end | |
156 | end |
|
160 | end | |
157 |
|
161 | |||
158 | # to move in project_custom_field |
|
162 | # to move in project_custom_field | |
159 | def self.for_all |
|
163 | def self.for_all | |
160 | find(:all, :conditions => ["is_for_all=?", true], :order => 'position') |
|
164 | find(:all, :conditions => ["is_for_all=?", true], :order => 'position') | |
161 | end |
|
165 | end | |
162 |
|
166 | |||
163 | def type_name |
|
167 | def type_name | |
164 | nil |
|
168 | nil | |
165 | end |
|
169 | end | |
166 |
|
170 | |||
167 | # Returns the error messages for the given value |
|
171 | # Returns the error messages for the given value | |
168 | # or an empty array if value is a valid value for the custom field |
|
172 | # or an empty array if value is a valid value for the custom field | |
169 | def validate_field_value(value) |
|
173 | def validate_field_value(value) | |
170 | errs = [] |
|
174 | errs = [] | |
171 | if value.is_a?(Array) |
|
175 | if value.is_a?(Array) | |
172 | if !multiple? |
|
176 | if !multiple? | |
173 | errs << ::I18n.t('activerecord.errors.messages.invalid') |
|
177 | errs << ::I18n.t('activerecord.errors.messages.invalid') | |
174 | end |
|
178 | end | |
175 | if is_required? && value.detect(&:present?).nil? |
|
179 | if is_required? && value.detect(&:present?).nil? | |
176 | errs << ::I18n.t('activerecord.errors.messages.blank') |
|
180 | errs << ::I18n.t('activerecord.errors.messages.blank') | |
177 | end |
|
181 | end | |
178 | value.each {|v| errs += validate_field_value_format(v)} |
|
182 | value.each {|v| errs += validate_field_value_format(v)} | |
179 | else |
|
183 | else | |
180 | if is_required? && value.blank? |
|
184 | if is_required? && value.blank? | |
181 | errs << ::I18n.t('activerecord.errors.messages.blank') |
|
185 | errs << ::I18n.t('activerecord.errors.messages.blank') | |
182 | end |
|
186 | end | |
183 | errs += validate_field_value_format(value) |
|
187 | errs += validate_field_value_format(value) | |
184 | end |
|
188 | end | |
185 | errs |
|
189 | errs | |
186 | end |
|
190 | end | |
187 |
|
191 | |||
188 | # Returns true if value is a valid value for the custom field |
|
192 | # Returns true if value is a valid value for the custom field | |
189 | def valid_field_value?(value) |
|
193 | def valid_field_value?(value) | |
190 | validate_field_value(value).empty? |
|
194 | validate_field_value(value).empty? | |
191 | end |
|
195 | end | |
192 |
|
196 | |||
193 | protected |
|
197 | protected | |
194 |
|
198 | |||
195 | # Returns the error message for the given value regarding its format |
|
199 | # Returns the error message for the given value regarding its format | |
196 | def validate_field_value_format(value) |
|
200 | def validate_field_value_format(value) | |
197 | errs = [] |
|
201 | errs = [] | |
198 | if value.present? |
|
202 | if value.present? | |
199 | errs << ::I18n.t('activerecord.errors.messages.invalid') unless regexp.blank? or value =~ Regexp.new(regexp) |
|
203 | errs << ::I18n.t('activerecord.errors.messages.invalid') unless regexp.blank? or value =~ Regexp.new(regexp) | |
200 | errs << ::I18n.t('activerecord.errors.messages.too_short', :count => min_length) if min_length > 0 and value.length < min_length |
|
204 | errs << ::I18n.t('activerecord.errors.messages.too_short', :count => min_length) if min_length > 0 and value.length < min_length | |
201 | errs << ::I18n.t('activerecord.errors.messages.too_long', :count => max_length) if max_length > 0 and value.length > max_length |
|
205 | errs << ::I18n.t('activerecord.errors.messages.too_long', :count => max_length) if max_length > 0 and value.length > max_length | |
202 |
|
206 | |||
203 | # Format specific validations |
|
207 | # Format specific validations | |
204 | case field_format |
|
208 | case field_format | |
205 | when 'int' |
|
209 | when 'int' | |
206 | errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value =~ /^[+-]?\d+$/ |
|
210 | errs << ::I18n.t('activerecord.errors.messages.not_a_number') unless value =~ /^[+-]?\d+$/ | |
207 | when 'float' |
|
211 | when 'float' | |
208 | begin; Kernel.Float(value); rescue; errs << ::I18n.t('activerecord.errors.messages.invalid') end |
|
212 | begin; Kernel.Float(value); rescue; errs << ::I18n.t('activerecord.errors.messages.invalid') end | |
209 | when 'date' |
|
213 | when 'date' | |
210 | errs << ::I18n.t('activerecord.errors.messages.not_a_date') unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end |
|
214 | errs << ::I18n.t('activerecord.errors.messages.not_a_date') unless value =~ /^\d{4}-\d{2}-\d{2}$/ && begin; value.to_date; rescue; false end | |
211 | when 'list' |
|
215 | when 'list' | |
212 | errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value) |
|
216 | errs << ::I18n.t('activerecord.errors.messages.inclusion') unless possible_values.include?(value) | |
213 | end |
|
217 | end | |
214 | end |
|
218 | end | |
215 | errs |
|
219 | errs | |
216 | end |
|
220 | end | |
217 | end |
|
221 | end |
@@ -1,124 +1,138 | |||||
1 | <ul> |
|
1 | <ul> | |
2 | <%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %> |
|
2 | <%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %> | |
3 |
|
3 | |||
4 | <% if !@issue.nil? -%> |
|
4 | <% if !@issue.nil? -%> | |
5 | <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, |
|
5 | <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, | |
6 | :class => 'icon-edit', :disabled => !@can[:edit] %></li> |
|
6 | :class => 'icon-edit', :disabled => !@can[:edit] %></li> | |
7 | <% else %> |
|
7 | <% else %> | |
8 | <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)}, |
|
8 | <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)}, | |
9 | :class => 'icon-edit', :disabled => !@can[:edit] %></li> |
|
9 | :class => 'icon-edit', :disabled => !@can[:edit] %></li> | |
10 | <% end %> |
|
10 | <% end %> | |
11 |
|
11 | |||
12 | <% if @allowed_statuses.present? %> |
|
12 | <% if @allowed_statuses.present? %> | |
13 | <li class="folder"> |
|
13 | <li class="folder"> | |
14 | <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a> |
|
14 | <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a> | |
15 | <ul> |
|
15 | <ul> | |
16 | <% @statuses.each do |s| -%> |
|
16 | <% @statuses.each do |s| -%> | |
17 | <li><%= context_menu_link h(s.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post, |
|
17 | <li><%= context_menu_link h(s.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post, | |
18 | :selected => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li> |
|
18 | :selected => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li> | |
19 | <% end -%> |
|
19 | <% end -%> | |
20 | </ul> |
|
20 | </ul> | |
21 | </li> |
|
21 | </li> | |
22 | <% end %> |
|
22 | <% end %> | |
23 |
|
23 | |||
24 | <% unless @trackers.nil? %> |
|
24 | <% unless @trackers.nil? %> | |
25 | <li class="folder"> |
|
25 | <li class="folder"> | |
26 | <a href="#" class="submenu"><%= l(:field_tracker) %></a> |
|
26 | <a href="#" class="submenu"><%= l(:field_tracker) %></a> | |
27 | <ul> |
|
27 | <ul> | |
28 | <% @trackers.each do |t| -%> |
|
28 | <% @trackers.each do |t| -%> | |
29 | <li><%= context_menu_link h(t.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'tracker_id' => t}, :back_url => @back}, :method => :post, |
|
29 | <li><%= context_menu_link h(t.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'tracker_id' => t}, :back_url => @back}, :method => :post, | |
30 | :selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li> |
|
30 | :selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li> | |
31 | <% end -%> |
|
31 | <% end -%> | |
32 | </ul> |
|
32 | </ul> | |
33 | </li> |
|
33 | </li> | |
34 | <% end %> |
|
34 | <% end %> | |
35 |
|
35 | |||
36 | <li class="folder"> |
|
36 | <li class="folder"> | |
37 | <a href="#" class="submenu"><%= l(:field_priority) %></a> |
|
37 | <a href="#" class="submenu"><%= l(:field_priority) %></a> | |
38 | <ul> |
|
38 | <ul> | |
39 | <% @priorities.each do |p| -%> |
|
39 | <% @priorities.each do |p| -%> | |
40 | <li><%= context_menu_link h(p.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'priority_id' => p}, :back_url => @back}, :method => :post, |
|
40 | <li><%= context_menu_link h(p.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'priority_id' => p}, :back_url => @back}, :method => :post, | |
41 | :selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li> |
|
41 | :selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li> | |
42 | <% end -%> |
|
42 | <% end -%> | |
43 | </ul> |
|
43 | </ul> | |
44 | </li> |
|
44 | </li> | |
45 |
|
45 | |||
46 | <% #TODO: allow editing versions when multiple projects %> |
|
46 | <% #TODO: allow editing versions when multiple projects %> | |
47 | <% unless @project.nil? || @project.shared_versions.open.empty? -%> |
|
47 | <% unless @project.nil? || @project.shared_versions.open.empty? -%> | |
48 | <li class="folder"> |
|
48 | <li class="folder"> | |
49 | <a href="#" class="submenu"><%= l(:field_fixed_version) %></a> |
|
49 | <a href="#" class="submenu"><%= l(:field_fixed_version) %></a> | |
50 | <ul> |
|
50 | <ul> | |
51 | <% @project.shared_versions.open.sort.each do |v| -%> |
|
51 | <% @project.shared_versions.open.sort.each do |v| -%> | |
52 | <li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => v}, :back_url => @back}, :method => :post, |
|
52 | <li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => v}, :back_url => @back}, :method => :post, | |
53 | :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li> |
|
53 | :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li> | |
54 | <% end -%> |
|
54 | <% end -%> | |
55 | <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => 'none'}, :back_url => @back}, :method => :post, |
|
55 | <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => 'none'}, :back_url => @back}, :method => :post, | |
56 | :selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li> |
|
56 | :selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li> | |
57 | </ul> |
|
57 | </ul> | |
58 | </li> |
|
58 | </li> | |
59 | <% end %> |
|
59 | <% end %> | |
60 | <% if @assignables.present? -%> |
|
60 | <% if @assignables.present? -%> | |
61 | <li class="folder"> |
|
61 | <li class="folder"> | |
62 | <a href="#" class="submenu"><%= l(:field_assigned_to) %></a> |
|
62 | <a href="#" class="submenu"><%= l(:field_assigned_to) %></a> | |
63 | <ul> |
|
63 | <ul> | |
64 | <% if @assignables.include?(User.current) %> |
|
64 | <% if @assignables.include?(User.current) %> | |
65 | <li><%= context_menu_link "<< #{l(:label_me)} >>", {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => User.current}, :back_url => @back}, :method => :post, |
|
65 | <li><%= context_menu_link "<< #{l(:label_me)} >>", {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => User.current}, :back_url => @back}, :method => :post, | |
66 | :disabled => !@can[:update] %></li> |
|
66 | :disabled => !@can[:update] %></li> | |
67 | <% end %> |
|
67 | <% end %> | |
68 | <% @assignables.each do |u| -%> |
|
68 | <% @assignables.each do |u| -%> | |
69 | <li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => u}, :back_url => @back}, :method => :post, |
|
69 | <li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => u}, :back_url => @back}, :method => :post, | |
70 | :selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li> |
|
70 | :selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li> | |
71 | <% end -%> |
|
71 | <% end -%> | |
72 | <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => 'none'}, :back_url => @back}, :method => :post, |
|
72 | <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => 'none'}, :back_url => @back}, :method => :post, | |
73 | :selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li> |
|
73 | :selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li> | |
74 | </ul> |
|
74 | </ul> | |
75 | </li> |
|
75 | </li> | |
76 | <% end %> |
|
76 | <% end %> | |
77 | <% unless @project.nil? || @project.issue_categories.empty? -%> |
|
77 | <% unless @project.nil? || @project.issue_categories.empty? -%> | |
78 | <li class="folder"> |
|
78 | <li class="folder"> | |
79 | <a href="#" class="submenu"><%= l(:field_category) %></a> |
|
79 | <a href="#" class="submenu"><%= l(:field_category) %></a> | |
80 | <ul> |
|
80 | <ul> | |
81 | <% @project.issue_categories.each do |u| -%> |
|
81 | <% @project.issue_categories.each do |u| -%> | |
82 | <li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'category_id' => u}, :back_url => @back}, :method => :post, |
|
82 | <li><%= context_menu_link h(u.name), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'category_id' => u}, :back_url => @back}, :method => :post, | |
83 | :selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li> |
|
83 | :selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li> | |
84 | <% end -%> |
|
84 | <% end -%> | |
85 | <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'category_id' => 'none'}, :back_url => @back}, :method => :post, |
|
85 | <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'category_id' => 'none'}, :back_url => @back}, :method => :post, | |
86 | :selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li> |
|
86 | :selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li> | |
87 | </ul> |
|
87 | </ul> | |
88 | </li> |
|
88 | </li> | |
89 | <% end -%> |
|
89 | <% end -%> | |
90 |
|
90 | |||
91 | <% if Issue.use_field_for_done_ratio? %> |
|
91 | <% if Issue.use_field_for_done_ratio? %> | |
92 | <li class="folder"> |
|
92 | <li class="folder"> | |
93 | <a href="#" class="submenu"><%= l(:field_done_ratio) %></a> |
|
93 | <a href="#" class="submenu"><%= l(:field_done_ratio) %></a> | |
94 | <ul> |
|
94 | <ul> | |
95 | <% (0..10).map{|x|x*10}.each do |p| -%> |
|
95 | <% (0..10).map{|x|x*10}.each do |p| -%> | |
96 | <li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'done_ratio' => p}, :back_url => @back}, :method => :post, |
|
96 | <li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_update', :ids => @issues.collect(&:id), :issue => {'done_ratio' => p}, :back_url => @back}, :method => :post, | |
97 | :selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li> |
|
97 | :selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li> | |
98 | <% end -%> |
|
98 | <% end -%> | |
99 | </ul> |
|
99 | </ul> | |
100 | </li> |
|
100 | </li> | |
101 | <% end %> |
|
101 | <% end %> | |
102 |
|
102 | |||
|
103 | <% @options_by_custom_field.each do |field, options| %> | |||
|
104 | <li class="folder"> | |||
|
105 | <a href="#" class="submenu"><%= h(field.name) %></a> | |||
|
106 | <ul> | |||
|
107 | <% options.each do |text, value| %> | |||
|
108 | <li><%= bulk_update_custom_field_context_menu_link(field, text, value || text) %></li> | |||
|
109 | <% end %> | |||
|
110 | <% unless field.is_required? %> | |||
|
111 | <li><%= bulk_update_custom_field_context_menu_link(field, l(:label_none), '') %></li> | |||
|
112 | <% end %> | |||
|
113 | </ul> | |||
|
114 | </li> | |||
|
115 | <% end %> | |||
|
116 | ||||
103 | <% if !@issue.nil? %> |
|
117 | <% if !@issue.nil? %> | |
104 | <% if @can[:log_time] -%> |
|
118 | <% if @can[:log_time] -%> | |
105 | <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, |
|
119 | <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, | |
106 | :class => 'icon-time-add' %></li> |
|
120 | :class => 'icon-time-add' %></li> | |
107 | <% end %> |
|
121 | <% end %> | |
108 | <% if User.current.logged? %> |
|
122 | <% if User.current.logged? %> | |
109 | <li><%= watcher_link(@issue, User.current) %></li> |
|
123 | <li><%= watcher_link(@issue, User.current) %></li> | |
110 | <% end %> |
|
124 | <% end %> | |
111 | <% end %> |
|
125 | <% end %> | |
112 |
|
126 | |||
113 | <% if @issue.present? %> |
|
127 | <% if @issue.present? %> | |
114 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, |
|
128 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue}, | |
115 | :class => 'icon-copy', :disabled => !@can[:copy] %></li> |
|
129 | :class => 'icon-copy', :disabled => !@can[:copy] %></li> | |
116 | <% else %> |
|
130 | <% else %> | |
117 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :copy => '1'}, |
|
131 | <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :copy => '1'}, | |
118 | :class => 'icon-copy', :disabled => !@can[:move] %></li> |
|
132 | :class => 'icon-copy', :disabled => !@can[:move] %></li> | |
119 | <% end %> |
|
133 | <% end %> | |
120 | <li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back), |
|
134 | <li><%= context_menu_link l(:button_delete), issues_path(:ids => @issues.collect(&:id), :back_url => @back), | |
121 | :method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li> |
|
135 | :method => :delete, :confirm => issues_destroy_confirmation_message(@issues), :class => 'icon-del', :disabled => !@can[:delete] %></li> | |
122 |
|
136 | |||
123 | <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %> |
|
137 | <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %> | |
124 | </ul> |
|
138 | </ul> |
@@ -1,52 +1,52 | |||||
1 | #context-menu { position: absolute; z-index: 40; font-size: 0.9em;} |
|
1 | #context-menu { position: absolute; z-index: 40; font-size: 0.9em;} | |
2 |
|
2 | |||
3 | #context-menu ul, #context-menu li, #context-menu a { |
|
3 | #context-menu ul, #context-menu li, #context-menu a { | |
4 | display:block; |
|
4 | display:block; | |
5 | margin:0; |
|
5 | margin:0; | |
6 | padding:0; |
|
6 | padding:0; | |
7 | border:0; |
|
7 | border:0; | |
8 | } |
|
8 | } | |
9 |
|
9 | |||
10 | #context-menu ul { |
|
10 | #context-menu ul { | |
11 | width:150px; |
|
11 | width:150px; | |
12 | border-top:1px solid #ddd; |
|
12 | border-top:1px solid #ddd; | |
13 | border-left:1px solid #ddd; |
|
13 | border-left:1px solid #ddd; | |
14 | border-bottom:1px solid #777; |
|
14 | border-bottom:1px solid #777; | |
15 | border-right:1px solid #777; |
|
15 | border-right:1px solid #777; | |
16 | background:white; |
|
16 | background:white; | |
17 | list-style:none; |
|
17 | list-style:none; | |
18 | } |
|
18 | } | |
19 |
|
19 | |||
20 | #context-menu li { |
|
20 | #context-menu li { | |
21 | position:relative; |
|
21 | position:relative; | |
22 | padding:1px; |
|
22 | padding:1px; | |
23 | z-index:39; |
|
23 | z-index:39; | |
24 | border:1px solid white; |
|
24 | border:1px solid white; | |
25 | } |
|
25 | } | |
26 | #context-menu li.folder ul { position:absolute; left:168px; /* IE6 */ top:-2px; max-height:300px; overflow:hidden; overflow-y: auto; } |
|
26 | #context-menu li.folder ul { position:absolute; left:168px; /* IE6 */ top:-2px; max-height:300px; overflow:hidden; overflow-y: auto; } | |
27 | #context-menu li.folder>ul { left:148px; } |
|
27 | #context-menu li.folder>ul { left:148px; } | |
28 |
|
28 | |||
29 | #context-menu.reverse-y li.folder>ul { top:auto; bottom:0; } |
|
29 | #context-menu.reverse-y li.folder>ul { top:auto; bottom:0; } | |
30 | #context-menu.reverse-x li.folder ul { left:auto; right:168px; /* IE6 */ } |
|
30 | #context-menu.reverse-x li.folder ul { left:auto; right:168px; /* IE6 */ } | |
31 | #context-menu.reverse-x li.folder>ul { right:148px; } |
|
31 | #context-menu.reverse-x li.folder>ul { right:148px; } | |
32 |
|
32 | |||
33 | #context-menu a { |
|
33 | #context-menu a { | |
34 | text-decoration:none !important; |
|
34 | text-decoration:none !important; | |
35 | background-repeat: no-repeat; |
|
35 | background-repeat: no-repeat; | |
36 | background-position: 1px 50%; |
|
36 | background-position: 1px 50%; | |
37 | padding: 1px 0px 1px 20px; |
|
37 | padding: 1px 0px 1px 20px; | |
38 | width:100%; /* IE */ |
|
38 | width:100%; /* IE */ | |
39 | } |
|
39 | } | |
40 | #context-menu li>a { width:auto; } /* others */ |
|
40 | #context-menu li>a { width:auto; } /* others */ | |
41 | #context-menu a.disabled, #context-menu a.disabled:hover {color: #ccc;} |
|
41 | #context-menu a.disabled, #context-menu a.disabled:hover {color: #ccc;} | |
42 | #context-menu li a.submenu { background:url("../images/bullet_arrow_right.png") right no-repeat; } |
|
42 | #context-menu li a.submenu { padding-right:16px; background:url("../images/bullet_arrow_right.png") right no-repeat; } | |
43 | #context-menu li:hover { border:1px solid gray; background-color:#eee; } |
|
43 | #context-menu li:hover { border:1px solid gray; background-color:#eee; } | |
44 | #context-menu a:hover {color:#2A5685;} |
|
44 | #context-menu a:hover {color:#2A5685;} | |
45 | #context-menu li.folder:hover { z-index:40; } |
|
45 | #context-menu li.folder:hover { z-index:40; } | |
46 | #context-menu ul ul, #context-menu li:hover ul ul { display:none; } |
|
46 | #context-menu ul ul, #context-menu li:hover ul ul { display:none; } | |
47 | #context-menu li:hover ul, #context-menu li:hover li:hover ul { display:block; } |
|
47 | #context-menu li:hover ul, #context-menu li:hover li:hover ul { display:block; } | |
48 |
|
48 | |||
49 | /* selected element */ |
|
49 | /* selected element */ | |
50 | .context-menu-selection { background-color:#507AAA !important; color:#f8f8f8 !important; } |
|
50 | .context-menu-selection { background-color:#507AAA !important; color:#f8f8f8 !important; } | |
51 | .context-menu-selection a, .context-menu-selection a:hover { color:#f8f8f8 !important; } |
|
51 | .context-menu-selection a, .context-menu-selection a:hover { color:#f8f8f8 !important; } | |
52 | .context-menu-selection:hover { background-color:#507AAA !important; color:#f8f8f8 !important; } |
|
52 | .context-menu-selection:hover { background-color:#507AAA !important; color:#f8f8f8 !important; } |
@@ -1,156 +1,252 | |||||
1 | require File.expand_path('../../test_helper', __FILE__) |
|
1 | require File.expand_path('../../test_helper', __FILE__) | |
2 |
|
2 | |||
3 | class ContextMenusControllerTest < ActionController::TestCase |
|
3 | class ContextMenusControllerTest < ActionController::TestCase | |
4 | fixtures :projects, |
|
4 | fixtures :projects, | |
5 | :trackers, |
|
5 | :trackers, | |
6 | :projects_trackers, |
|
6 | :projects_trackers, | |
7 | :roles, |
|
7 | :roles, | |
8 | :member_roles, |
|
8 | :member_roles, | |
9 | :members, |
|
9 | :members, | |
10 | :auth_sources, |
|
10 | :auth_sources, | |
11 | :enabled_modules, |
|
11 | :enabled_modules, | |
12 | :workflows, |
|
12 | :workflows, | |
13 | :journals, :journal_details, |
|
13 | :journals, :journal_details, | |
14 | :versions, |
|
14 | :versions, | |
15 | :issues, :issue_statuses, :issue_categories, |
|
15 | :issues, :issue_statuses, :issue_categories, | |
16 | :users, |
|
16 | :users, | |
17 | :enumerations, |
|
17 | :enumerations, | |
18 | :time_entries |
|
18 | :time_entries | |
19 |
|
19 | |||
20 | def test_context_menu_one_issue |
|
20 | def test_context_menu_one_issue | |
21 | @request.session[:user_id] = 2 |
|
21 | @request.session[:user_id] = 2 | |
22 | get :issues, :ids => [1] |
|
22 | get :issues, :ids => [1] | |
23 | assert_response :success |
|
23 | assert_response :success | |
24 | assert_template 'context_menu' |
|
24 | assert_template 'context_menu' | |
25 | assert_tag :tag => 'a', :content => 'Edit', |
|
25 | assert_tag :tag => 'a', :content => 'Edit', | |
26 | :attributes => { :href => '/issues/1/edit', |
|
26 | :attributes => { :href => '/issues/1/edit', | |
27 | :class => 'icon-edit' } |
|
27 | :class => 'icon-edit' } | |
28 | assert_tag :tag => 'a', :content => 'Closed', |
|
28 | assert_tag :tag => 'a', :content => 'Closed', | |
29 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bstatus_id%5D=5', |
|
29 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bstatus_id%5D=5', | |
30 | :class => '' } |
|
30 | :class => '' } | |
31 | assert_tag :tag => 'a', :content => 'Immediate', |
|
31 | assert_tag :tag => 'a', :content => 'Immediate', | |
32 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bpriority_id%5D=8', |
|
32 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bpriority_id%5D=8', | |
33 | :class => '' } |
|
33 | :class => '' } | |
34 | assert_no_tag :tag => 'a', :content => 'Inactive Priority' |
|
34 | assert_no_tag :tag => 'a', :content => 'Inactive Priority' | |
35 | # Versions |
|
35 | # Versions | |
36 | assert_tag :tag => 'a', :content => '2.0', |
|
36 | assert_tag :tag => 'a', :content => '2.0', | |
37 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bfixed_version_id%5D=3', |
|
37 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bfixed_version_id%5D=3', | |
38 | :class => '' } |
|
38 | :class => '' } | |
39 | assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0', |
|
39 | assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0', | |
40 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bfixed_version_id%5D=4', |
|
40 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bfixed_version_id%5D=4', | |
41 | :class => '' } |
|
41 | :class => '' } | |
42 |
|
42 | |||
43 | assert_tag :tag => 'a', :content => 'Dave Lopper', |
|
43 | assert_tag :tag => 'a', :content => 'Dave Lopper', | |
44 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bassigned_to_id%5D=3', |
|
44 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bassigned_to_id%5D=3', | |
45 | :class => '' } |
|
45 | :class => '' } | |
46 | assert_tag :tag => 'a', :content => 'Copy', |
|
46 | assert_tag :tag => 'a', :content => 'Copy', | |
47 | :attributes => { :href => '/projects/ecookbook/issues/1/copy', |
|
47 | :attributes => { :href => '/projects/ecookbook/issues/1/copy', | |
48 | :class => 'icon-copy' } |
|
48 | :class => 'icon-copy' } | |
49 | assert_no_tag :tag => 'a', :content => 'Move' |
|
49 | assert_no_tag :tag => 'a', :content => 'Move' | |
50 | assert_tag :tag => 'a', :content => 'Delete', |
|
50 | assert_tag :tag => 'a', :content => 'Delete', | |
51 | :attributes => { :href => '/issues?ids%5B%5D=1', |
|
51 | :attributes => { :href => '/issues?ids%5B%5D=1', | |
52 | :class => 'icon-del' } |
|
52 | :class => 'icon-del' } | |
53 | end |
|
53 | end | |
54 |
|
54 | |||
55 | def test_context_menu_one_issue_by_anonymous |
|
55 | def test_context_menu_one_issue_by_anonymous | |
56 | get :issues, :ids => [1] |
|
56 | get :issues, :ids => [1] | |
57 | assert_response :success |
|
57 | assert_response :success | |
58 | assert_template 'context_menu' |
|
58 | assert_template 'context_menu' | |
59 | assert_tag :tag => 'a', :content => 'Delete', |
|
59 | assert_tag :tag => 'a', :content => 'Delete', | |
60 | :attributes => { :href => '#', |
|
60 | :attributes => { :href => '#', | |
61 | :class => 'icon-del disabled' } |
|
61 | :class => 'icon-del disabled' } | |
62 | end |
|
62 | end | |
63 |
|
63 | |||
64 | def test_context_menu_multiple_issues_of_same_project |
|
64 | def test_context_menu_multiple_issues_of_same_project | |
65 | @request.session[:user_id] = 2 |
|
65 | @request.session[:user_id] = 2 | |
66 | get :issues, :ids => [1, 2] |
|
66 | get :issues, :ids => [1, 2] | |
67 | assert_response :success |
|
67 | assert_response :success | |
68 | assert_template 'context_menu' |
|
68 | assert_template 'context_menu' | |
69 | assert_not_nil assigns(:issues) |
|
69 | assert_not_nil assigns(:issues) | |
70 | assert_equal [1, 2], assigns(:issues).map(&:id).sort |
|
70 | assert_equal [1, 2], assigns(:issues).map(&:id).sort | |
71 |
|
71 | |||
72 | ids = assigns(:issues).map(&:id).map {|i| "ids%5B%5D=#{i}"}.join('&') |
|
72 | ids = assigns(:issues).map(&:id).map {|i| "ids%5B%5D=#{i}"}.join('&') | |
73 | assert_tag :tag => 'a', :content => 'Edit', |
|
73 | assert_tag :tag => 'a', :content => 'Edit', | |
74 | :attributes => { :href => "/issues/bulk_edit?#{ids}", |
|
74 | :attributes => { :href => "/issues/bulk_edit?#{ids}", | |
75 | :class => 'icon-edit' } |
|
75 | :class => 'icon-edit' } | |
76 | assert_tag :tag => 'a', :content => 'Closed', |
|
76 | assert_tag :tag => 'a', :content => 'Closed', | |
77 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bstatus_id%5D=5", |
|
77 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bstatus_id%5D=5", | |
78 | :class => '' } |
|
78 | :class => '' } | |
79 | assert_tag :tag => 'a', :content => 'Immediate', |
|
79 | assert_tag :tag => 'a', :content => 'Immediate', | |
80 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bpriority_id%5D=8", |
|
80 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bpriority_id%5D=8", | |
81 | :class => '' } |
|
81 | :class => '' } | |
82 | assert_tag :tag => 'a', :content => 'Dave Lopper', |
|
82 | assert_tag :tag => 'a', :content => 'Dave Lopper', | |
83 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=3", |
|
83 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=3", | |
84 | :class => '' } |
|
84 | :class => '' } | |
85 | assert_tag :tag => 'a', :content => 'Copy', |
|
85 | assert_tag :tag => 'a', :content => 'Copy', | |
86 | :attributes => { :href => "/issues/bulk_edit?copy=1&#{ids}", |
|
86 | :attributes => { :href => "/issues/bulk_edit?copy=1&#{ids}", | |
87 | :class => 'icon-copy' } |
|
87 | :class => 'icon-copy' } | |
88 | assert_no_tag :tag => 'a', :content => 'Move' |
|
88 | assert_no_tag :tag => 'a', :content => 'Move' | |
89 | assert_tag :tag => 'a', :content => 'Delete', |
|
89 | assert_tag :tag => 'a', :content => 'Delete', | |
90 | :attributes => { :href => "/issues?#{ids}", |
|
90 | :attributes => { :href => "/issues?#{ids}", | |
91 | :class => 'icon-del' } |
|
91 | :class => 'icon-del' } | |
92 | end |
|
92 | end | |
93 |
|
93 | |||
94 | def test_context_menu_multiple_issues_of_different_projects |
|
94 | def test_context_menu_multiple_issues_of_different_projects | |
95 | @request.session[:user_id] = 2 |
|
95 | @request.session[:user_id] = 2 | |
96 | get :issues, :ids => [1, 2, 6] |
|
96 | get :issues, :ids => [1, 2, 6] | |
97 | assert_response :success |
|
97 | assert_response :success | |
98 | assert_template 'context_menu' |
|
98 | assert_template 'context_menu' | |
99 | assert_not_nil assigns(:issues) |
|
99 | assert_not_nil assigns(:issues) | |
100 | assert_equal [1, 2, 6], assigns(:issues).map(&:id).sort |
|
100 | assert_equal [1, 2, 6], assigns(:issues).map(&:id).sort | |
101 |
|
101 | |||
102 | ids = assigns(:issues).map(&:id).map {|i| "ids%5B%5D=#{i}"}.join('&') |
|
102 | ids = assigns(:issues).map(&:id).map {|i| "ids%5B%5D=#{i}"}.join('&') | |
103 | assert_tag :tag => 'a', :content => 'Edit', |
|
103 | assert_tag :tag => 'a', :content => 'Edit', | |
104 | :attributes => { :href => "/issues/bulk_edit?#{ids}", |
|
104 | :attributes => { :href => "/issues/bulk_edit?#{ids}", | |
105 | :class => 'icon-edit' } |
|
105 | :class => 'icon-edit' } | |
106 | assert_tag :tag => 'a', :content => 'Closed', |
|
106 | assert_tag :tag => 'a', :content => 'Closed', | |
107 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bstatus_id%5D=5", |
|
107 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bstatus_id%5D=5", | |
108 | :class => '' } |
|
108 | :class => '' } | |
109 | assert_tag :tag => 'a', :content => 'Immediate', |
|
109 | assert_tag :tag => 'a', :content => 'Immediate', | |
110 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bpriority_id%5D=8", |
|
110 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bpriority_id%5D=8", | |
111 | :class => '' } |
|
111 | :class => '' } | |
112 | assert_tag :tag => 'a', :content => 'John Smith', |
|
112 | assert_tag :tag => 'a', :content => 'John Smith', | |
113 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=2", |
|
113 | :attributes => { :href => "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=2", | |
114 | :class => '' } |
|
114 | :class => '' } | |
115 | assert_tag :tag => 'a', :content => 'Delete', |
|
115 | assert_tag :tag => 'a', :content => 'Delete', | |
116 | :attributes => { :href => "/issues?#{ids}", |
|
116 | :attributes => { :href => "/issues?#{ids}", | |
117 | :class => 'icon-del' } |
|
117 | :class => 'icon-del' } | |
118 | end |
|
118 | end | |
119 |
|
119 | |||
|
120 | def test_context_menu_should_include_list_custom_fields | |||
|
121 | field = IssueCustomField.create!(:name => 'List', :field_format => 'list', | |||
|
122 | :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3]) | |||
|
123 | @request.session[:user_id] = 2 | |||
|
124 | get :issues, :ids => [1, 2] | |||
|
125 | ||||
|
126 | assert_tag 'a', | |||
|
127 | :content => 'List', | |||
|
128 | :attributes => {:href => '#'}, | |||
|
129 | :sibling => {:tag => 'ul', :children => {:count => 3}} | |||
|
130 | ||||
|
131 | assert_tag 'a', | |||
|
132 | :content => 'Foo', | |||
|
133 | :attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=Foo"} | |||
|
134 | assert_tag 'a', | |||
|
135 | :content => 'none', | |||
|
136 | :attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bcustom_field_values%5D%5B#{field.id}%5D="} | |||
|
137 | end | |||
|
138 | ||||
|
139 | def test_context_menu_should_not_include_null_value_for_required_custom_fields | |||
|
140 | field = IssueCustomField.create!(:name => 'List', :is_required => true, :field_format => 'list', | |||
|
141 | :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3]) | |||
|
142 | @request.session[:user_id] = 2 | |||
|
143 | get :issues, :ids => [1, 2] | |||
|
144 | ||||
|
145 | assert_tag 'a', | |||
|
146 | :content => 'List', | |||
|
147 | :attributes => {:href => '#'}, | |||
|
148 | :sibling => {:tag => 'ul', :children => {:count => 2}} | |||
|
149 | end | |||
|
150 | ||||
|
151 | def test_context_menu_on_single_issue_should_select_current_custom_field_value | |||
|
152 | field = IssueCustomField.create!(:name => 'List', :field_format => 'list', | |||
|
153 | :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3]) | |||
|
154 | issue = Issue.find(1) | |||
|
155 | issue.custom_field_values = {field.id => 'Bar'} | |||
|
156 | issue.save! | |||
|
157 | @request.session[:user_id] = 2 | |||
|
158 | get :issues, :ids => [1] | |||
|
159 | ||||
|
160 | assert_tag 'a', | |||
|
161 | :content => 'List', | |||
|
162 | :attributes => {:href => '#'}, | |||
|
163 | :sibling => {:tag => 'ul', :children => {:count => 3}} | |||
|
164 | assert_tag 'a', | |||
|
165 | :content => 'Bar', | |||
|
166 | :attributes => {:class => /icon-checked/} | |||
|
167 | end | |||
|
168 | ||||
|
169 | def test_context_menu_should_include_bool_custom_fields | |||
|
170 | field = IssueCustomField.create!(:name => 'Bool', :field_format => 'bool', | |||
|
171 | :is_for_all => true, :tracker_ids => [1, 2, 3]) | |||
|
172 | @request.session[:user_id] = 2 | |||
|
173 | get :issues, :ids => [1, 2] | |||
|
174 | ||||
|
175 | assert_tag 'a', | |||
|
176 | :content => 'Bool', | |||
|
177 | :attributes => {:href => '#'}, | |||
|
178 | :sibling => {:tag => 'ul', :children => {:count => 3}} | |||
|
179 | ||||
|
180 | assert_tag 'a', | |||
|
181 | :content => 'Yes', | |||
|
182 | :attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=1"} | |||
|
183 | end | |||
|
184 | ||||
|
185 | def test_context_menu_should_include_user_custom_fields | |||
|
186 | field = IssueCustomField.create!(:name => 'User', :field_format => 'user', | |||
|
187 | :is_for_all => true, :tracker_ids => [1, 2, 3]) | |||
|
188 | @request.session[:user_id] = 2 | |||
|
189 | get :issues, :ids => [1, 2] | |||
|
190 | ||||
|
191 | assert_tag 'a', | |||
|
192 | :content => 'User', | |||
|
193 | :attributes => {:href => '#'}, | |||
|
194 | :sibling => {:tag => 'ul', :children => {:count => Project.find(1).members.count + 1}} | |||
|
195 | ||||
|
196 | assert_tag 'a', | |||
|
197 | :content => 'John Smith', | |||
|
198 | :attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=2"} | |||
|
199 | end | |||
|
200 | ||||
|
201 | def test_context_menu_should_include_version_custom_fields | |||
|
202 | field = IssueCustomField.create!(:name => 'Version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1, 2, 3]) | |||
|
203 | @request.session[:user_id] = 2 | |||
|
204 | get :issues, :ids => [1, 2] | |||
|
205 | ||||
|
206 | assert_tag 'a', | |||
|
207 | :content => 'Version', | |||
|
208 | :attributes => {:href => '#'}, | |||
|
209 | :sibling => {:tag => 'ul', :children => {:count => Project.find(1).shared_versions.count + 1}} | |||
|
210 | ||||
|
211 | assert_tag 'a', | |||
|
212 | :content => '2.0', | |||
|
213 | :attributes => {:href => "/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=3"} | |||
|
214 | end | |||
|
215 | ||||
120 | def test_context_menu_by_assignable_user_should_include_assigned_to_me_link |
|
216 | def test_context_menu_by_assignable_user_should_include_assigned_to_me_link | |
121 | @request.session[:user_id] = 2 |
|
217 | @request.session[:user_id] = 2 | |
122 | get :issues, :ids => [1] |
|
218 | get :issues, :ids => [1] | |
123 | assert_response :success |
|
219 | assert_response :success | |
124 | assert_template 'context_menu' |
|
220 | assert_template 'context_menu' | |
125 |
|
221 | |||
126 | assert_tag :tag => 'a', :content => / me /, |
|
222 | assert_tag :tag => 'a', :content => / me /, | |
127 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bassigned_to_id%5D=2', |
|
223 | :attributes => { :href => '/issues/bulk_update?ids%5B%5D=1&issue%5Bassigned_to_id%5D=2', | |
128 | :class => '' } |
|
224 | :class => '' } | |
129 | end |
|
225 | end | |
130 |
|
226 | |||
131 | def test_context_menu_issue_visibility |
|
227 | def test_context_menu_issue_visibility | |
132 | get :issues, :ids => [1, 4] |
|
228 | get :issues, :ids => [1, 4] | |
133 | assert_response :success |
|
229 | assert_response :success | |
134 | assert_template 'context_menu' |
|
230 | assert_template 'context_menu' | |
135 | assert_equal [1], assigns(:issues).collect(&:id) |
|
231 | assert_equal [1], assigns(:issues).collect(&:id) | |
136 | end |
|
232 | end | |
137 |
|
233 | |||
138 | def test_time_entries_context_menu |
|
234 | def test_time_entries_context_menu | |
139 | @request.session[:user_id] = 2 |
|
235 | @request.session[:user_id] = 2 | |
140 | get :time_entries, :ids => [1, 2] |
|
236 | get :time_entries, :ids => [1, 2] | |
141 | assert_response :success |
|
237 | assert_response :success | |
142 | assert_template 'time_entries' |
|
238 | assert_template 'time_entries' | |
143 | assert_tag 'a', :content => 'Edit' |
|
239 | assert_tag 'a', :content => 'Edit' | |
144 | assert_no_tag 'a', :content => 'Edit', :attributes => {:class => /disabled/} |
|
240 | assert_no_tag 'a', :content => 'Edit', :attributes => {:class => /disabled/} | |
145 | end |
|
241 | end | |
146 |
|
242 | |||
147 | def test_time_entries_context_menu_without_edit_permission |
|
243 | def test_time_entries_context_menu_without_edit_permission | |
148 | @request.session[:user_id] = 2 |
|
244 | @request.session[:user_id] = 2 | |
149 | Role.find_by_name('Manager').remove_permission! :edit_time_entries |
|
245 | Role.find_by_name('Manager').remove_permission! :edit_time_entries | |
150 |
|
246 | |||
151 | get :time_entries, :ids => [1, 2] |
|
247 | get :time_entries, :ids => [1, 2] | |
152 | assert_response :success |
|
248 | assert_response :success | |
153 | assert_template 'time_entries' |
|
249 | assert_template 'time_entries' | |
154 | assert_tag 'a', :content => 'Edit', :attributes => {:class => /disabled/} |
|
250 | assert_tag 'a', :content => 'Edit', :attributes => {:class => /disabled/} | |
155 | end |
|
251 | end | |
156 | end |
|
252 | end |
General Comments 0
You need to be logged in to leave comments.
Login now