##// END OF EJS Templates
Removed User#display_name (replaced by User#name)....
Jean-Philippe Lang -
r575:65be9d3c86f5
parent child
Show More
@@ -1,277 +1,277
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 RedCloth
18 class RedCloth
19 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
19 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
20 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
20 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
21 def hard_break( text )
21 def hard_break( text )
22 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
22 text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks
23 end
23 end
24 end
24 end
25
25
26 module ApplicationHelper
26 module ApplicationHelper
27
27
28 # Return current logged in user or nil
28 # Return current logged in user or nil
29 def loggedin?
29 def loggedin?
30 @logged_in_user
30 @logged_in_user
31 end
31 end
32
32
33 # Return true if user is logged in and is admin, otherwise false
33 # Return true if user is logged in and is admin, otherwise false
34 def admin_loggedin?
34 def admin_loggedin?
35 @logged_in_user and @logged_in_user.admin?
35 @logged_in_user and @logged_in_user.admin?
36 end
36 end
37
37
38 # Return true if user is authorized for controller/action, otherwise false
38 # Return true if user is authorized for controller/action, otherwise false
39 def authorize_for(controller, action)
39 def authorize_for(controller, action)
40 # check if action is allowed on public projects
40 # check if action is allowed on public projects
41 if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
41 if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ]
42 return true
42 return true
43 end
43 end
44 # check if user is authorized
44 # check if user is authorized
45 if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) )
45 if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) )
46 return true
46 return true
47 end
47 end
48 return false
48 return false
49 end
49 end
50
50
51 # Display a link if user is authorized
51 # Display a link if user is authorized
52 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
52 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
53 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
53 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action])
54 end
54 end
55
55
56 # Display a link to user's account page
56 # Display a link to user's account page
57 def link_to_user(user)
57 def link_to_user(user)
58 link_to user.display_name, :controller => 'account', :action => 'show', :id => user
58 link_to user.name, :controller => 'account', :action => 'show', :id => user
59 end
59 end
60
60
61 def link_to_issue(issue)
61 def link_to_issue(issue)
62 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
62 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
63 end
63 end
64
64
65 def toggle_link(name, id, options={})
65 def toggle_link(name, id, options={})
66 onclick = "Element.toggle('#{id}'); "
66 onclick = "Element.toggle('#{id}'); "
67 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
67 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
68 onclick << "return false;"
68 onclick << "return false;"
69 link_to(name, "#", :onclick => onclick)
69 link_to(name, "#", :onclick => onclick)
70 end
70 end
71
71
72 def image_to_function(name, function, html_options = {})
72 def image_to_function(name, function, html_options = {})
73 html_options.symbolize_keys!
73 html_options.symbolize_keys!
74 tag(:input, html_options.merge({
74 tag(:input, html_options.merge({
75 :type => "image", :src => image_path(name),
75 :type => "image", :src => image_path(name),
76 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
76 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
77 }))
77 }))
78 end
78 end
79
79
80 def format_date(date)
80 def format_date(date)
81 l_date(date) if date
81 l_date(date) if date
82 end
82 end
83
83
84 def format_time(time)
84 def format_time(time)
85 l_datetime((time.is_a? String) ? time.to_time : time) if time
85 l_datetime((time.is_a? String) ? time.to_time : time) if time
86 end
86 end
87
87
88 def day_name(day)
88 def day_name(day)
89 l(:general_day_names).split(',')[day-1]
89 l(:general_day_names).split(',')[day-1]
90 end
90 end
91
91
92 def month_name(month)
92 def month_name(month)
93 l(:actionview_datehelper_select_month_names).split(',')[month-1]
93 l(:actionview_datehelper_select_month_names).split(',')[month-1]
94 end
94 end
95
95
96 def pagination_links_full(paginator, options={}, html_options={})
96 def pagination_links_full(paginator, options={}, html_options={})
97 page_param = options.delete(:page_param) || :page
97 page_param = options.delete(:page_param) || :page
98
98
99 html = ''
99 html = ''
100 html << link_to_remote(('&#171; ' + l(:label_previous)),
100 html << link_to_remote(('&#171; ' + l(:label_previous)),
101 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
101 {:update => "content", :url => options.merge(page_param => paginator.current.previous)},
102 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
102 {:href => url_for(:params => options.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
103
103
104 html << (pagination_links_each(paginator, options) do |n|
104 html << (pagination_links_each(paginator, options) do |n|
105 link_to_remote(n.to_s,
105 link_to_remote(n.to_s,
106 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
106 {:url => {:params => options.merge(page_param => n)}, :update => 'content'},
107 {:href => url_for(:params => options.merge(page_param => n))})
107 {:href => url_for(:params => options.merge(page_param => n))})
108 end || '')
108 end || '')
109
109
110 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
110 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
111 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
111 {:update => "content", :url => options.merge(page_param => paginator.current.next)},
112 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
112 {:href => url_for(:params => options.merge(page_param => paginator.current.next))}) if paginator.current.next
113 html
113 html
114 end
114 end
115
115
116 # textilize text according to system settings and RedCloth availability
116 # textilize text according to system settings and RedCloth availability
117 def textilizable(text, options = {})
117 def textilizable(text, options = {})
118 return "" if text.blank?
118 return "" if text.blank?
119
119
120 # different methods for formatting wiki links
120 # different methods for formatting wiki links
121 case options[:wiki_links]
121 case options[:wiki_links]
122 when :local
122 when :local
123 # used for local links to html files
123 # used for local links to html files
124 format_wiki_link = Proc.new {|title| "#{title}.html" }
124 format_wiki_link = Proc.new {|title| "#{title}.html" }
125 when :anchor
125 when :anchor
126 # used for single-file wiki export
126 # used for single-file wiki export
127 format_wiki_link = Proc.new {|title| "##{title}" }
127 format_wiki_link = Proc.new {|title| "##{title}" }
128 else
128 else
129 if @project
129 if @project
130 format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title }
130 format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title }
131 else
131 else
132 format_wiki_link = Proc.new {|title| title }
132 format_wiki_link = Proc.new {|title| title }
133 end
133 end
134 end
134 end
135
135
136 # turn wiki links into textile links:
136 # turn wiki links into textile links:
137 # example:
137 # example:
138 # [[link]] -> "link":link
138 # [[link]] -> "link":link
139 # [[link|title]] -> "title":link
139 # [[link|title]] -> "title":link
140 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| "\"#{$3 || $1}\":" + format_wiki_link.call(Wiki.titleize($1)) }
140 text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| "\"#{$3 || $1}\":" + format_wiki_link.call(Wiki.titleize($1)) }
141
141
142 # turn issue ids into links
142 # turn issue ids into links
143 # example:
143 # example:
144 # #52 -> <a href="/issues/show/52">#52</a>
144 # #52 -> <a href="/issues/show/52">#52</a>
145 text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", :controller => 'issues', :action => 'show', :id => $1}
145 text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", :controller => 'issues', :action => 'show', :id => $1}
146
146
147 # turn revision ids into links (@project needed)
147 # turn revision ids into links (@project needed)
148 # example:
148 # example:
149 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6)
149 # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6)
150 text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", :controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1} if @project
150 text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", :controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1} if @project
151
151
152 # when using an image link, try to use an attachment, if possible
152 # when using an image link, try to use an attachment, if possible
153 attachments = options[:attachments]
153 attachments = options[:attachments]
154 if attachments
154 if attachments
155 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
155 text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
156 align = $1
156 align = $1
157 filename = $2
157 filename = $2
158 rf = Regexp.new(filename, Regexp::IGNORECASE)
158 rf = Regexp.new(filename, Regexp::IGNORECASE)
159 # search for the picture in attachments
159 # search for the picture in attachments
160 if found = attachments.detect { |att| att.filename =~ rf }
160 if found = attachments.detect { |att| att.filename =~ rf }
161 image_url = url_for :controller => 'attachments', :action => 'show', :id => found.id
161 image_url = url_for :controller => 'attachments', :action => 'show', :id => found.id
162 "!#{align}#{image_url}!"
162 "!#{align}#{image_url}!"
163 else
163 else
164 "!#{align}#{filename}!"
164 "!#{align}#{filename}!"
165 end
165 end
166 end
166 end
167 end
167 end
168
168
169 # finally textilize text
169 # finally textilize text
170 @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize")
170 @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize")
171 text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text)))
171 text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text)))
172 end
172 end
173
173
174 def error_messages_for(object_name, options = {})
174 def error_messages_for(object_name, options = {})
175 options = options.symbolize_keys
175 options = options.symbolize_keys
176 object = instance_variable_get("@#{object_name}")
176 object = instance_variable_get("@#{object_name}")
177 if object && !object.errors.empty?
177 if object && !object.errors.empty?
178 # build full_messages here with controller current language
178 # build full_messages here with controller current language
179 full_messages = []
179 full_messages = []
180 object.errors.each do |attr, msg|
180 object.errors.each do |attr, msg|
181 next if msg.nil?
181 next if msg.nil?
182 msg = msg.first if msg.is_a? Array
182 msg = msg.first if msg.is_a? Array
183 if attr == "base"
183 if attr == "base"
184 full_messages << l(msg)
184 full_messages << l(msg)
185 else
185 else
186 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
186 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
187 end
187 end
188 end
188 end
189 # retrieve custom values error messages
189 # retrieve custom values error messages
190 if object.errors[:custom_values]
190 if object.errors[:custom_values]
191 object.custom_values.each do |v|
191 object.custom_values.each do |v|
192 v.errors.each do |attr, msg|
192 v.errors.each do |attr, msg|
193 next if msg.nil?
193 next if msg.nil?
194 msg = msg.first if msg.is_a? Array
194 msg = msg.first if msg.is_a? Array
195 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
195 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
196 end
196 end
197 end
197 end
198 end
198 end
199 content_tag("div",
199 content_tag("div",
200 content_tag(
200 content_tag(
201 options[:header_tag] || "h2", lwr(:gui_validation_error, full_messages.length) + " :"
201 options[:header_tag] || "h2", lwr(:gui_validation_error, full_messages.length) + " :"
202 ) +
202 ) +
203 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
203 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
204 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
204 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
205 )
205 )
206 else
206 else
207 ""
207 ""
208 end
208 end
209 end
209 end
210
210
211 def lang_options_for_select(blank=true)
211 def lang_options_for_select(blank=true)
212 (blank ? [["(auto)", ""]] : []) +
212 (blank ? [["(auto)", ""]] : []) +
213 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first }
213 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first }
214 end
214 end
215
215
216 def label_tag_for(name, option_tags = nil, options = {})
216 def label_tag_for(name, option_tags = nil, options = {})
217 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
217 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
218 content_tag("label", label_text)
218 content_tag("label", label_text)
219 end
219 end
220
220
221 def labelled_tabular_form_for(name, object, options, &proc)
221 def labelled_tabular_form_for(name, object, options, &proc)
222 options[:html] ||= {}
222 options[:html] ||= {}
223 options[:html].store :class, "tabular"
223 options[:html].store :class, "tabular"
224 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
224 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
225 end
225 end
226
226
227 def check_all_links(form_name)
227 def check_all_links(form_name)
228 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
228 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
229 " | " +
229 " | " +
230 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
230 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
231 end
231 end
232
232
233 def calendar_for(field_id)
233 def calendar_for(field_id)
234 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
234 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
235 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
235 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
236 end
236 end
237
237
238 def wikitoolbar_for(field_id)
238 def wikitoolbar_for(field_id)
239 return '' unless Setting.text_formatting == 'textile'
239 return '' unless Setting.text_formatting == 'textile'
240 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
240 javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
241 end
241 end
242 end
242 end
243
243
244 class TabularFormBuilder < ActionView::Helpers::FormBuilder
244 class TabularFormBuilder < ActionView::Helpers::FormBuilder
245 include GLoc
245 include GLoc
246
246
247 def initialize(object_name, object, template, options, proc)
247 def initialize(object_name, object, template, options, proc)
248 set_language_if_valid options.delete(:lang)
248 set_language_if_valid options.delete(:lang)
249 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
249 @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
250 end
250 end
251
251
252 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
252 (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector|
253 src = <<-END_SRC
253 src = <<-END_SRC
254 def #{selector}(field, options = {})
254 def #{selector}(field, options = {})
255 return super if options.delete :no_label
255 return super if options.delete :no_label
256 label_text = l(options[:label]) if options[:label]
256 label_text = l(options[:label]) if options[:label]
257 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
257 label_text ||= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym)
258 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
258 label_text << @template.content_tag("span", " *", :class => "required") if options.delete(:required)
259 label = @template.content_tag("label", label_text,
259 label = @template.content_tag("label", label_text,
260 :class => (@object && @object.errors[field] ? "error" : nil),
260 :class => (@object && @object.errors[field] ? "error" : nil),
261 :for => (@object_name.to_s + "_" + field.to_s))
261 :for => (@object_name.to_s + "_" + field.to_s))
262 label + super
262 label + super
263 end
263 end
264 END_SRC
264 END_SRC
265 class_eval src, __FILE__, __LINE__
265 class_eval src, __FILE__, __LINE__
266 end
266 end
267
267
268 def select(field, choices, options = {}, html_options = {})
268 def select(field, choices, options = {}, html_options = {})
269 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
269 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
270 label = @template.content_tag("label", label_text,
270 label = @template.content_tag("label", label_text,
271 :class => (@object && @object.errors[field] ? "error" : nil),
271 :class => (@object && @object.errors[field] ? "error" : nil),
272 :for => (@object_name.to_s + "_" + field.to_s))
272 :for => (@object_name.to_s + "_" + field.to_s))
273 label + super
273 label + super
274 end
274 end
275
275
276 end
276 end
277
277
@@ -1,34 +1,34
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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 Member < ActiveRecord::Base
18 class Member < ActiveRecord::Base
19 belongs_to :user
19 belongs_to :user
20 belongs_to :role
20 belongs_to :role
21 belongs_to :project
21 belongs_to :project
22
22
23 validates_presence_of :role, :user, :project
23 validates_presence_of :role, :user, :project
24 validates_uniqueness_of :user_id, :scope => :project_id
24 validates_uniqueness_of :user_id, :scope => :project_id
25
25
26 def name
26 def name
27 self.user.display_name
27 self.user.name
28 end
28 end
29
29
30 def before_destroy
30 def before_destroy
31 # remove category based auto assignments for this member
31 # remove category based auto assignments for this member
32 project.issue_categories.update_all "assigned_to_id = NULL", ["assigned_to_id = ?", self.user.id]
32 project.issue_categories.update_all "assigned_to_id = NULL", ["assigned_to_id = ?", self.user.id]
33 end
33 end
34 end
34 end
@@ -1,167 +1,163
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < ActiveRecord::Base
20 class User < ActiveRecord::Base
21 # Account statuses
21 # Account statuses
22 STATUS_ACTIVE = 1
22 STATUS_ACTIVE = 1
23 STATUS_REGISTERED = 2
23 STATUS_REGISTERED = 2
24 STATUS_LOCKED = 3
24 STATUS_LOCKED = 3
25
25
26 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
26 has_many :memberships, :class_name => 'Member', :include => [ :project, :role ], :conditions => "#{Project.table_name}.status=#{Project::STATUS_ACTIVE}", :order => "#{Project.table_name}.name", :dependent => :delete_all
27 has_many :projects, :through => :memberships
27 has_many :projects, :through => :memberships
28 has_many :custom_values, :dependent => :delete_all, :as => :customized
28 has_many :custom_values, :dependent => :delete_all, :as => :customized
29 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
29 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
30 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
30 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
31 has_one :rss_key, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
31 has_one :rss_key, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
32 belongs_to :auth_source
32 belongs_to :auth_source
33
33
34 attr_accessor :password, :password_confirmation
34 attr_accessor :password, :password_confirmation
35 attr_accessor :last_before_login_on
35 attr_accessor :last_before_login_on
36 # Prevents unauthorized assignments
36 # Prevents unauthorized assignments
37 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
37 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
38
38
39 validates_presence_of :login, :firstname, :lastname, :mail
39 validates_presence_of :login, :firstname, :lastname, :mail
40 validates_uniqueness_of :login, :mail
40 validates_uniqueness_of :login, :mail
41 # Login must contain lettres, numbers, underscores only
41 # Login must contain lettres, numbers, underscores only
42 validates_format_of :login, :with => /^[a-z0-9_\-@\.]+$/i
42 validates_format_of :login, :with => /^[a-z0-9_\-@\.]+$/i
43 validates_length_of :login, :maximum => 30
43 validates_length_of :login, :maximum => 30
44 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
44 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-]*$/i
45 validates_length_of :firstname, :lastname, :maximum => 30
45 validates_length_of :firstname, :lastname, :maximum => 30
46 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
46 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
47 validates_length_of :mail, :maximum => 60
47 validates_length_of :mail, :maximum => 60
48 # Password length between 4 and 12
48 # Password length between 4 and 12
49 validates_length_of :password, :in => 4..12, :allow_nil => true
49 validates_length_of :password, :in => 4..12, :allow_nil => true
50 validates_confirmation_of :password, :allow_nil => true
50 validates_confirmation_of :password, :allow_nil => true
51 validates_associated :custom_values, :on => :update
51 validates_associated :custom_values, :on => :update
52
52
53 def before_save
53 def before_save
54 # update hashed_password if password was set
54 # update hashed_password if password was set
55 self.hashed_password = User.hash_password(self.password) if self.password
55 self.hashed_password = User.hash_password(self.password) if self.password
56 end
56 end
57
57
58 def self.active
58 def self.active
59 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
59 with_scope :find => { :conditions => [ "status = ?", STATUS_ACTIVE ] } do
60 yield
60 yield
61 end
61 end
62 end
62 end
63
63
64 def self.find_active(*args)
64 def self.find_active(*args)
65 active do
65 active do
66 find(*args)
66 find(*args)
67 end
67 end
68 end
68 end
69
69
70 # Returns the user that matches provided login and password, or nil
70 # Returns the user that matches provided login and password, or nil
71 def self.try_to_login(login, password)
71 def self.try_to_login(login, password)
72 user = find(:first, :conditions => ["login=?", login])
72 user = find(:first, :conditions => ["login=?", login])
73 if user
73 if user
74 # user is already in local database
74 # user is already in local database
75 return nil if !user.active?
75 return nil if !user.active?
76 if user.auth_source
76 if user.auth_source
77 # user has an external authentication method
77 # user has an external authentication method
78 return nil unless user.auth_source.authenticate(login, password)
78 return nil unless user.auth_source.authenticate(login, password)
79 else
79 else
80 # authentication with local password
80 # authentication with local password
81 return nil unless User.hash_password(password) == user.hashed_password
81 return nil unless User.hash_password(password) == user.hashed_password
82 end
82 end
83 else
83 else
84 # user is not yet registered, try to authenticate with available sources
84 # user is not yet registered, try to authenticate with available sources
85 attrs = AuthSource.authenticate(login, password)
85 attrs = AuthSource.authenticate(login, password)
86 if attrs
86 if attrs
87 onthefly = new(*attrs)
87 onthefly = new(*attrs)
88 onthefly.login = login
88 onthefly.login = login
89 onthefly.language = Setting.default_language
89 onthefly.language = Setting.default_language
90 if onthefly.save
90 if onthefly.save
91 user = find(:first, :conditions => ["login=?", login])
91 user = find(:first, :conditions => ["login=?", login])
92 logger.info("User '#{user.login}' created on the fly.") if logger
92 logger.info("User '#{user.login}' created on the fly.") if logger
93 end
93 end
94 end
94 end
95 end
95 end
96 user.update_attribute(:last_login_on, Time.now) if user
96 user.update_attribute(:last_login_on, Time.now) if user
97 user
97 user
98
98
99 rescue => text
99 rescue => text
100 raise text
100 raise text
101 end
101 end
102
102
103 # Return user's full name for display
103 # Return user's full name for display
104 def display_name
105 firstname + " " + lastname
106 end
107
108 def name
104 def name
109 display_name
105 "#{firstname} #{lastname}"
110 end
106 end
111
107
112 def active?
108 def active?
113 self.status == STATUS_ACTIVE
109 self.status == STATUS_ACTIVE
114 end
110 end
115
111
116 def registered?
112 def registered?
117 self.status == STATUS_REGISTERED
113 self.status == STATUS_REGISTERED
118 end
114 end
119
115
120 def locked?
116 def locked?
121 self.status == STATUS_LOCKED
117 self.status == STATUS_LOCKED
122 end
118 end
123
119
124 def check_password?(clear_password)
120 def check_password?(clear_password)
125 User.hash_password(clear_password) == self.hashed_password
121 User.hash_password(clear_password) == self.hashed_password
126 end
122 end
127
123
128 def role_for_project(project)
124 def role_for_project(project)
129 return nil unless project
125 return nil unless project
130 member = memberships.detect {|m| m.project_id == project.id}
126 member = memberships.detect {|m| m.project_id == project.id}
131 member ? member.role : nil
127 member ? member.role : nil
132 end
128 end
133
129
134 def authorized_to(project, action)
130 def authorized_to(project, action)
135 return true if self.admin?
131 return true if self.admin?
136 role = role_for_project(project)
132 role = role_for_project(project)
137 role && Permission.allowed_to_role(action, role)
133 role && Permission.allowed_to_role(action, role)
138 end
134 end
139
135
140 def pref
136 def pref
141 self.preference ||= UserPreference.new(:user => self)
137 self.preference ||= UserPreference.new(:user => self)
142 end
138 end
143
139
144 def get_or_create_rss_key
140 def get_or_create_rss_key
145 self.rss_key || Token.create(:user => self, :action => 'feeds')
141 self.rss_key || Token.create(:user => self, :action => 'feeds')
146 end
142 end
147
143
148 def self.find_by_rss_key(key)
144 def self.find_by_rss_key(key)
149 token = Token.find_by_value(key)
145 token = Token.find_by_value(key)
150 token && token.user.active? ? token.user : nil
146 token && token.user.active? ? token.user : nil
151 end
147 end
152
148
153 def self.find_by_autologin_key(key)
149 def self.find_by_autologin_key(key)
154 token = Token.find_by_action_and_value('autologin', key)
150 token = Token.find_by_action_and_value('autologin', key)
155 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
151 token && (token.created_on > Setting.autologin.to_i.day.ago) && token.user.active? ? token.user : nil
156 end
152 end
157
153
158 def <=>(user)
154 def <=>(user)
159 lastname == user.lastname ? firstname <=> user.firstname : lastname <=> user.lastname
155 lastname == user.lastname ? firstname <=> user.firstname : lastname <=> user.lastname
160 end
156 end
161
157
162 private
158 private
163 # Return password digest
159 # Return password digest
164 def self.hash_password(clear_password)
160 def self.hash_password(clear_password)
165 Digest::SHA1.hexdigest(clear_password || "")
161 Digest::SHA1.hexdigest(clear_password || "")
166 end
162 end
167 end
163 end
@@ -1,28 +1,28
1 <h2><%= @user.display_name %></h2>
1 <h2><%=h @user.name %></h2>
2
2
3 <p>
3 <p>
4 <%= mail_to @user.mail unless @user.pref.hide_mail %>
4 <%= mail_to @user.mail unless @user.pref.hide_mail %>
5 <ul>
5 <ul>
6 <li><%=l(:label_registered_on)%>: <%= format_date(@user.created_on) %></li>
6 <li><%=l(:label_registered_on)%>: <%= format_date(@user.created_on) %></li>
7 <% for custom_value in @custom_values %>
7 <% for custom_value in @custom_values %>
8 <% if !custom_value.value.empty? %>
8 <% if !custom_value.value.empty? %>
9 <li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
9 <li><%= custom_value.custom_field.name%>: <%=h show_value(custom_value) %></li>
10 <% end %>
10 <% end %>
11 <% end %>
11 <% end %>
12 </ul>
12 </ul>
13 </p>
13 </p>
14
14
15 <% unless @memberships.empty? %>
15 <% unless @memberships.empty? %>
16 <h3><%=l(:label_project_plural)%></h3>
16 <h3><%=l(:label_project_plural)%></h3>
17 <ul>
17 <ul>
18 <% for membership in @memberships %>
18 <% for membership in @memberships %>
19 <li><%= link_to membership.project.name, :controller => 'projects', :action => 'show', :id => membership.project %>
19 <li><%= link_to membership.project.name, :controller => 'projects', :action => 'show', :id => membership.project %>
20 (<%= membership.role.name %>, <%= format_date(membership.created_on) %>)</li>
20 (<%= membership.role.name %>, <%= format_date(membership.created_on) %>)</li>
21 <% end %>
21 <% end %>
22 </ul>
22 </ul>
23 <% end %>
23 <% end %>
24
24
25 <h3><%=l(:label_activity)%></h3>
25 <h3><%=l(:label_activity)%></h3>
26 <p>
26 <p>
27 <%=l(:label_reported_issues)%>: <%= Issue.count(["author_id=?", @user.id]) %>
27 <%=l(:label_reported_issues)%>: <%= Issue.count(["author_id=?", @user.id]) %>
28 </p> No newline at end of file
28 </p>
@@ -1,13 +1,13
1 <div class="attachments">
1 <div class="attachments">
2 <% for attachment in attachments %>
2 <% for attachment in attachments %>
3 <p><%= link_to attachment.filename, {:controller => 'attachments', :action => 'download', :id => attachment }, :class => 'icon icon-attachment' %>
3 <p><%= link_to attachment.filename, {:controller => 'attachments', :action => 'download', :id => attachment }, :class => 'icon icon-attachment' %>
4 (<%= number_to_human_size attachment.filesize %>)
4 (<%= number_to_human_size attachment.filesize %>)
5 <% unless options[:no_author] %>
5 <% unless options[:no_author] %>
6 <em><%= attachment.author.display_name %>, <%= format_date(attachment.created_on) %></em>
6 <em><%= attachment.author.name %>, <%= format_date(attachment.created_on) %></em>
7 <% end %>
7 <% end %>
8 <% if options[:delete_url] %>
8 <% if options[:delete_url] %>
9 <%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}), :confirm => l(:text_are_you_sure), :method => :post %>
9 <%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}), :confirm => l(:text_are_you_sure), :method => :post %>
10 <% end %>
10 <% end %>
11 </p>
11 </p>
12 <% end %>
12 <% end %>
13 </div>
13 </div>
@@ -1,36 +1,36
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to_if_authorized l(:button_edit), {:controller => 'documents', :action => 'edit', :id => @document}, :class => 'icon icon-edit' %>
2 <%= link_to_if_authorized l(:button_edit), {:controller => 'documents', :action => 'edit', :id => @document}, :class => 'icon icon-edit' %>
3 <%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy', :id => @document}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
3 <%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy', :id => @document}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
4 </div>
4 </div>
5
5
6 <h2><%= @document.title %></h2>
6 <h2><%= @document.title %></h2>
7
7
8 <p><em><%= @document.category.name %><br />
8 <p><em><%= @document.category.name %><br />
9 <%= format_date @document.created_on %></em></p>
9 <%= format_date @document.created_on %></em></p>
10 <%= textilizable @document.description, :attachments => @document.attachments %>
10 <%= textilizable @document.description, :attachments => @document.attachments %>
11 <br />
11 <br />
12
12
13 <h3><%= l(:label_attachment_plural) %></h3>
13 <h3><%= l(:label_attachment_plural) %></h3>
14 <ul class="documents">
14 <ul class="documents">
15 <% for attachment in @attachments %>
15 <% for attachment in @attachments %>
16 <li>
16 <li>
17 <div class="contextual">
17 <div class="contextual">
18 <%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy_attachment', :id => @document, :attachment_id => attachment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
18 <%= link_to_if_authorized l(:button_delete), {:controller => 'documents', :action => 'destroy_attachment', :id => @document, :attachment_id => attachment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
19 </div>
19 </div>
20 <%= link_to attachment.filename, :action => 'download', :id => @document, :attachment_id => attachment %>
20 <%= link_to attachment.filename, :action => 'download', :id => @document, :attachment_id => attachment %>
21 (<%= number_to_human_size attachment.filesize %>)<br />
21 (<%= number_to_human_size attachment.filesize %>)<br />
22 <em><%= attachment.author.display_name %>, <%= format_date(attachment.created_on) %></em><br />
22 <em><%= attachment.author.name %>, <%= format_date(attachment.created_on) %></em><br />
23 <%= lwr(:label_download, attachment.downloads) %>
23 <%= lwr(:label_download, attachment.downloads) %>
24 </li>
24 </li>
25 <% end %>
25 <% end %>
26 </ul>
26 </ul>
27 <br />
27 <br />
28
28
29
29
30 <% if authorize_for('documents', 'add_attachment') %>
30 <% if authorize_for('documents', 'add_attachment') %>
31 <p><%= toggle_link l(:label_attachment_new), "add_attachment_form" %></p>
31 <p><%= toggle_link l(:label_attachment_new), "add_attachment_form" %></p>
32 <% form_tag({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true, :class => "tabular", :id => "add_attachment_form", :style => "display:none;") do %>
32 <% form_tag({ :controller => 'documents', :action => 'add_attachment', :id => @document }, :multipart => true, :class => "tabular", :id => "add_attachment_form", :style => "display:none;") do %>
33 <%= render :partial => 'attachments/form' %>
33 <%= render :partial => 'attachments/form' %>
34 <%= submit_tag l(:button_add) %>
34 <%= submit_tag l(:button_add) %>
35 <% end %>
35 <% end %>
36 <% end %>
36 <% end %>
@@ -1,8 +1,8
1 <%=l(:label_issue)%> #<%= issue.id %> - <%= issue.subject %>
1 <%=l(:label_issue)%> #<%= issue.id %> - <%= issue.subject %>
2 <%=l(:field_author)%>: <%= issue.author.display_name %>
2 <%=l(:field_author)%>: <%= issue.author.name %>
3 <%=l(:field_assigned_to)%>: <%= issue.assigned_to ? issue.assigned_to.name : "-" %>
3 <%=l(:field_assigned_to)%>: <%= issue.assigned_to ? issue.assigned_to.name : "-" %>
4 <%=l(:field_status)%>: <%= issue.status.name %>
4 <%=l(:field_status)%>: <%= issue.status.name %>
5
5
6 <%= issue.description %>
6 <%= issue.description %>
7
7
8 http://<%= Setting.host_name %>/issues/show/<%= issue.id %> No newline at end of file
8 http://<%= Setting.host_name %>/issues/show/<%= issue.id %>
@@ -1,32 +1,32
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to_if_authorized l(:button_edit), {:controller => 'news', :action => 'edit', :id => @news}, :class => 'icon icon-edit' %>
2 <%= link_to_if_authorized l(:button_edit), {:controller => 'news', :action => 'edit', :id => @news}, :class => 'icon icon-edit' %>
3 <%= link_to_if_authorized l(:button_delete), {:controller => 'news', :action => 'destroy', :id => @news}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
3 <%= link_to_if_authorized l(:button_delete), {:controller => 'news', :action => 'destroy', :id => @news}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
4 </div>
4 </div>
5
5
6 <h2><%=h @news.title %></h2>
6 <h2><%=h @news.title %></h2>
7
7
8 <p><em><% unless @news.summary.empty? %><%=h @news.summary %><br /><% end %>
8 <p><em><% unless @news.summary.empty? %><%=h @news.summary %><br /><% end %>
9 <%= @news.author.display_name %>, <%= format_time(@news.created_on) %></em></p>
9 <%= @news.author.name %>, <%= format_time(@news.created_on) %></em></p>
10 <br />
10 <br />
11 <%= textilizable(@news.description) %>
11 <%= textilizable(@news.description) %>
12 <br />
12 <br />
13
13
14 <div id="comments" style="margin-bottom:16px;">
14 <div id="comments" style="margin-bottom:16px;">
15 <h3 class="icon22 icon22-comment"><%= l(:label_comment_plural) %></h3>
15 <h3 class="icon22 icon22-comment"><%= l(:label_comment_plural) %></h3>
16 <% @news.comments.each do |comment| %>
16 <% @news.comments.each do |comment| %>
17 <% next if comment.new_record? %>
17 <% next if comment.new_record? %>
18 <h4><%= format_time(comment.created_on) %> - <%= comment.author.name %></h4>
18 <h4><%= format_time(comment.created_on) %> - <%= comment.author.name %></h4>
19 <div class="contextual">
19 <div class="contextual">
20 <%= link_to_if_authorized l(:button_delete), {:controller => 'news', :action => 'destroy_comment', :id => @news, :comment_id => comment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
20 <%= link_to_if_authorized l(:button_delete), {:controller => 'news', :action => 'destroy_comment', :id => @news, :comment_id => comment}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %>
21 </div>
21 </div>
22 <%= simple_format(auto_link(h comment.comments))%>
22 <%= simple_format(auto_link(h comment.comments))%>
23 <% end if @news.comments_count > 0 %>
23 <% end if @news.comments_count > 0 %>
24 </div>
24 </div>
25
25
26 <% if authorize_for 'news', 'add_comment' %>
26 <% if authorize_for 'news', 'add_comment' %>
27 <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
27 <p><%= toggle_link l(:label_comment_add), "add_comment_form", :focus => "comment_comments" %></p>
28 <% form_tag({:action => 'add_comment', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
28 <% form_tag({:action => 'add_comment', :id => @news}, :id => "add_comment_form", :style => "display:none;") do %>
29 <%= text_area 'comment', 'comments', :cols => 60, :rows => 6 %>
29 <%= text_area 'comment', 'comments', :cols => 60, :rows => 6 %>
30 <p><%= submit_tag l(:button_add) %></p>
30 <p><%= submit_tag l(:button_add) %></p>
31 <% end %>
31 <% end %>
32 <% end %> No newline at end of file
32 <% end %>
@@ -1,43 +1,43
1 <%= error_messages_for 'member' %>
1 <%= error_messages_for 'member' %>
2 <% roles = Role.find(:all, :order => 'position') %>
2 <% roles = Role.find(:all, :order => 'position') %>
3 <% users = User.find_active(:all) - @project.users %>
3 <% users = User.find_active(:all) - @project.users %>
4
4
5 <table class="list">
5 <table class="list">
6 <thead>
6 <thead>
7 <th><%= l(:label_user) %></th>
7 <th><%= l(:label_user) %></th>
8 <th><%= l(:label_role) %></th>
8 <th><%= l(:label_role) %></th>
9 <th style="width:15%"></th>
9 <th style="width:15%"></th>
10 </thead>
10 </thead>
11 <tbody>
11 <tbody>
12 <% @project.members.find(:all, :include => [:role, :user]).sort{|x,y| x.role.position <=> y.role.position}.each do |member| %>
12 <% @project.members.find(:all, :include => [:role, :user]).sort{|x,y| x.role.position <=> y.role.position}.each do |member| %>
13 <% next if member.new_record? %>
13 <% next if member.new_record? %>
14 <tr class="<%= cycle 'odd', 'even' %>">
14 <tr class="<%= cycle 'odd', 'even' %>">
15 <td><%= member.user.display_name %></td>
15 <td><%= member.name %></td>
16 <td align="center">
16 <td align="center">
17 <% if authorize_for('members', 'edit') %>
17 <% if authorize_for('members', 'edit') %>
18 <% remote_form_for(:member, member, :url => {:controller => 'members', :action => 'edit', :id => member}, :method => :post) do |f| %>
18 <% remote_form_for(:member, member, :url => {:controller => 'members', :action => 'edit', :id => member}, :method => :post) do |f| %>
19 <%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, {}, :class => "small" %>
19 <%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, {}, :class => "small" %>
20 <%= submit_tag l(:button_change), :class => "small" %>
20 <%= submit_tag l(:button_change), :class => "small" %>
21 <% end %>
21 <% end %>
22 <% end %>
22 <% end %>
23 </td>
23 </td>
24 <td align="center">
24 <td align="center">
25 <small><%= link_to_remote l(:button_delete), { :url => {:controller => 'members', :action => 'destroy', :id => member},
25 <small><%= link_to_remote l(:button_delete), { :url => {:controller => 'members', :action => 'destroy', :id => member},
26 :method => :post
26 :method => :post
27 }, :title => l(:button_delete),
27 }, :title => l(:button_delete),
28 :class => 'icon icon-del' %></small>
28 :class => 'icon icon-del' %></small>
29 </td>
29 </td>
30 </tr>
30 </tr>
31 </tbody>
31 </tbody>
32 <% end; reset_cycle %>
32 <% end; reset_cycle %>
33 </table>
33 </table>
34 &nbsp;
34 &nbsp;
35
35
36 <% if authorize_for('projects', 'add_member') && !users.empty? %>
36 <% if authorize_for('projects', 'add_member') && !users.empty? %>
37 <% remote_form_for(:member, @member, :url => {:controller => 'projects', :action => 'add_member', :tab => 'members', :id => @project}, :method => :post) do |f| %>
37 <% remote_form_for(:member, @member, :url => {:controller => 'projects', :action => 'add_member', :tab => 'members', :id => @project}, :method => :post) do |f| %>
38 <p><label for="member_user_id"><%=l(:label_member_new)%></label><br />
38 <p><label for="member_user_id"><%=l(:label_member_new)%></label><br />
39 <%= f.select :user_id, users.collect{|user| [user.name, user.id]} %>
39 <%= f.select :user_id, users.collect{|user| [user.name, user.id]} %>
40 <%= l(:label_role) %>: <%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, :selected => nil %>
40 <%= l(:label_role) %>: <%= f.select :role_id, roles.collect{|role| [role.name, role.id]}, :selected => nil %>
41 <%= submit_tag l(:button_add) %></p>
41 <%= submit_tag l(:button_add) %></p>
42 <% end %>
42 <% end %>
43 <% end %>
43 <% end %>
@@ -1,13 +1,13
1 <h2><%=l(:label_member_plural)%></h2>
1 <h2><%=l(:label_member_plural)%></h2>
2
2
3 <% if @members.empty? %><p><i><%= l(:label_no_data) %></i></p><% end %>
3 <% if @members.empty? %><p><i><%= l(:label_no_data) %></i></p><% end %>
4
4
5 <% members = @members.group_by {|m| m.role } %>
5 <% members = @members.group_by {|m| m.role } %>
6 <% members.keys.sort{|x,y| x.position <=> y.position}.each do |role| %>
6 <% members.keys.sort{|x,y| x.position <=> y.position}.each do |role| %>
7 <h3><%= role.name %></h3>
7 <h3><%= role.name %></h3>
8 <ul>
8 <ul>
9 <% members[role].each do |m| %>
9 <% members[role].each do |m| %>
10 <li><%= link_to m.user.display_name, :controller => 'account', :action => 'show', :id => m.user %> (<%= format_date m.created_on %>)</li>
10 <li><%= link_to m.name, :controller => 'account', :action => 'show', :id => m.user %> (<%= format_date m.created_on %>)</li>
11 <% end %>
11 <% end %>
12 </ul>
12 </ul>
13 <% end %>
13 <% end %>
General Comments 0
You need to be logged in to leave comments. Login now