##// END OF EJS Templates
Merged r8210 from trunk (#9792)....
Jean-Philippe Lang -
r8646:1dcdba7cdcb5
parent child
Show More
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module AccountHelper
20 module AccountHelper
19 end
21 end
@@ -1,23 +1,25
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module AdminHelper
20 module AdminHelper
19 def project_status_options_for_select(selected)
21 def project_status_options_for_select(selected)
20 options_for_select([[l(:label_all), ''],
22 options_for_select([[l(:label_all), ''],
21 [l(:status_active), 1]], selected)
23 [l(:status_active), 1]], selected)
22 end
24 end
23 end
25 end
@@ -1,43 +1,45
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module AttachmentsHelper
20 module AttachmentsHelper
19 # Displays view/delete links to the attachments of the given object
21 # Displays view/delete links to the attachments of the given object
20 # Options:
22 # Options:
21 # :author -- author names are not displayed if set to false
23 # :author -- author names are not displayed if set to false
22 def link_to_attachments(container, options = {})
24 def link_to_attachments(container, options = {})
23 options.assert_valid_keys(:author)
25 options.assert_valid_keys(:author)
24
26
25 if container.attachments.any?
27 if container.attachments.any?
26 options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
28 options = {:deletable => container.attachments_deletable?, :author => true}.merge(options)
27 render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
29 render :partial => 'attachments/links', :locals => {:attachments => container.attachments, :options => options}
28 end
30 end
29 end
31 end
30
32
31 def render_api_attachment(attachment, api)
33 def render_api_attachment(attachment, api)
32 api.attachment do
34 api.attachment do
33 api.id attachment.id
35 api.id attachment.id
34 api.filename attachment.filename
36 api.filename attachment.filename
35 api.filesize attachment.filesize
37 api.filesize attachment.filesize
36 api.content_type attachment.content_type
38 api.content_type attachment.content_type
37 api.description attachment.description
39 api.description attachment.description
38 api.content_url url_for(:controller => 'attachments', :action => 'download', :id => attachment, :filename => attachment.filename, :only_path => false)
40 api.content_url url_for(:controller => 'attachments', :action => 'download', :id => attachment, :filename => attachment.filename, :only_path => false)
39 api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
41 api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
40 api.created_on attachment.created_on
42 api.created_on attachment.created_on
41 end
43 end
42 end
44 end
43 end
45 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module AuthSourcesHelper
20 module AuthSourcesHelper
19 end
21 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module BoardsHelper
20 module BoardsHelper
19 end
21 end
@@ -1,39 +1,58
1 # encoding: utf-8
2 #
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
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
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
1 module CalendarsHelper
20 module CalendarsHelper
2 def link_to_previous_month(year, month, options={})
21 def link_to_previous_month(year, month, options={})
3 target_year, target_month = if month == 1
22 target_year, target_month = if month == 1
4 [year - 1, 12]
23 [year - 1, 12]
5 else
24 else
6 [year, month - 1]
25 [year, month - 1]
7 end
26 end
8
27
9 name = if target_month == 12
28 name = if target_month == 12
10 "#{month_name(target_month)} #{target_year}"
29 "#{month_name(target_month)} #{target_year}"
11 else
30 else
12 "#{month_name(target_month)}"
31 "#{month_name(target_month)}"
13 end
32 end
14
33
15 # \xc2\xab(utf-8) = «
34 # \xc2\xab(utf-8) = «
16 link_to_month(("\xc2\xab " + name), target_year, target_month, options)
35 link_to_month(("\xc2\xab " + name), target_year, target_month, options)
17 end
36 end
18
37
19 def link_to_next_month(year, month, options={})
38 def link_to_next_month(year, month, options={})
20 target_year, target_month = if month == 12
39 target_year, target_month = if month == 12
21 [year + 1, 1]
40 [year + 1, 1]
22 else
41 else
23 [year, month + 1]
42 [year, month + 1]
24 end
43 end
25
44
26 name = if target_month == 1
45 name = if target_month == 1
27 "#{month_name(target_month)} #{target_year}"
46 "#{month_name(target_month)} #{target_year}"
28 else
47 else
29 "#{month_name(target_month)}"
48 "#{month_name(target_month)}"
30 end
49 end
31
50
32 # \xc2\xbb(utf-8) = »
51 # \xc2\xbb(utf-8) = »
33 link_to_month((name + " \xc2\xbb"), target_year, target_month, options)
52 link_to_month((name + " \xc2\xbb"), target_year, target_month, options)
34 end
53 end
35
54
36 def link_to_month(link_name, year, month, options={})
55 def link_to_month(link_name, year, month, options={})
37 link_to_content_update(h(link_name), params.merge(:year => year, :month => month))
56 link_to_content_update(h(link_name), params.merge(:year => year, :month => month))
38 end
57 end
39 end
58 end
@@ -1,118 +1,120
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module CustomFieldsHelper
20 module CustomFieldsHelper
19
21
20 def custom_fields_tabs
22 def custom_fields_tabs
21 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
23 tabs = [{:name => 'IssueCustomField', :partial => 'custom_fields/index', :label => :label_issue_plural},
22 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
24 {:name => 'TimeEntryCustomField', :partial => 'custom_fields/index', :label => :label_spent_time},
23 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
25 {:name => 'ProjectCustomField', :partial => 'custom_fields/index', :label => :label_project_plural},
24 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
26 {:name => 'VersionCustomField', :partial => 'custom_fields/index', :label => :label_version_plural},
25 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
27 {:name => 'UserCustomField', :partial => 'custom_fields/index', :label => :label_user_plural},
26 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
28 {:name => 'GroupCustomField', :partial => 'custom_fields/index', :label => :label_group_plural},
27 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
29 {:name => 'TimeEntryActivityCustomField', :partial => 'custom_fields/index', :label => TimeEntryActivity::OptionName},
28 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
30 {:name => 'IssuePriorityCustomField', :partial => 'custom_fields/index', :label => IssuePriority::OptionName},
29 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
31 {:name => 'DocumentCategoryCustomField', :partial => 'custom_fields/index', :label => DocumentCategory::OptionName}
30 ]
32 ]
31 end
33 end
32
34
33 # Return custom field html tag corresponding to its format
35 # Return custom field html tag corresponding to its format
34 def custom_field_tag(name, custom_value)
36 def custom_field_tag(name, custom_value)
35 custom_field = custom_value.custom_field
37 custom_field = custom_value.custom_field
36 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
38 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
37 field_id = "#{name}_custom_field_values_#{custom_field.id}"
39 field_id = "#{name}_custom_field_values_#{custom_field.id}"
38
40
39 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
41 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
40 case field_format.try(:edit_as)
42 case field_format.try(:edit_as)
41 when "date"
43 when "date"
42 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
44 text_field_tag(field_name, custom_value.value, :id => field_id, :size => 10) +
43 calendar_for(field_id)
45 calendar_for(field_id)
44 when "text"
46 when "text"
45 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
47 text_area_tag(field_name, custom_value.value, :id => field_id, :rows => 3, :style => 'width:90%')
46 when "bool"
48 when "bool"
47 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
49 hidden_field_tag(field_name, '0') + check_box_tag(field_name, '1', custom_value.true?, :id => field_id)
48 when "list"
50 when "list"
49 blank_option = custom_field.is_required? ?
51 blank_option = custom_field.is_required? ?
50 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
52 (custom_field.default_value.blank? ? "<option value=\"\">--- #{l(:actionview_instancetag_blank_option)} ---</option>" : '') :
51 '<option></option>'
53 '<option></option>'
52 select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
54 select_tag(field_name, blank_option + options_for_select(custom_field.possible_values_options(custom_value.customized), custom_value.value), :id => field_id)
53 else
55 else
54 text_field_tag(field_name, custom_value.value, :id => field_id)
56 text_field_tag(field_name, custom_value.value, :id => field_id)
55 end
57 end
56 end
58 end
57
59
58 # Return custom field label tag
60 # Return custom field label tag
59 def custom_field_label_tag(name, custom_value)
61 def custom_field_label_tag(name, custom_value)
60 content_tag "label", h(custom_value.custom_field.name) +
62 content_tag "label", h(custom_value.custom_field.name) +
61 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
63 (custom_value.custom_field.is_required? ? " <span class=\"required\">*</span>".html_safe : ""),
62 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
64 :for => "#{name}_custom_field_values_#{custom_value.custom_field.id}",
63 :class => (custom_value.errors.empty? ? nil : "error" )
65 :class => (custom_value.errors.empty? ? nil : "error" )
64 end
66 end
65
67
66 # Return custom field tag with its label tag
68 # Return custom field tag with its label tag
67 def custom_field_tag_with_label(name, custom_value)
69 def custom_field_tag_with_label(name, custom_value)
68 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
70 custom_field_label_tag(name, custom_value) + custom_field_tag(name, custom_value)
69 end
71 end
70
72
71 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
73 def custom_field_tag_for_bulk_edit(name, custom_field, projects=nil)
72 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
74 field_name = "#{name}[custom_field_values][#{custom_field.id}]"
73 field_id = "#{name}_custom_field_values_#{custom_field.id}"
75 field_id = "#{name}_custom_field_values_#{custom_field.id}"
74 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
76 field_format = Redmine::CustomFieldFormat.find_by_name(custom_field.field_format)
75 case field_format.try(:edit_as)
77 case field_format.try(:edit_as)
76 when "date"
78 when "date"
77 text_field_tag(field_name, '', :id => field_id, :size => 10) +
79 text_field_tag(field_name, '', :id => field_id, :size => 10) +
78 calendar_for(field_id)
80 calendar_for(field_id)
79 when "text"
81 when "text"
80 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
82 text_area_tag(field_name, '', :id => field_id, :rows => 3, :style => 'width:90%')
81 when "bool"
83 when "bool"
82 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
84 select_tag(field_name, options_for_select([[l(:label_no_change_option), ''],
83 [l(:general_text_yes), '1'],
85 [l(:general_text_yes), '1'],
84 [l(:general_text_no), '0']]), :id => field_id)
86 [l(:general_text_no), '0']]), :id => field_id)
85 when "list"
87 when "list"
86 select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options(projects)), :id => field_id)
88 select_tag(field_name, options_for_select([[l(:label_no_change_option), '']] + custom_field.possible_values_options(projects)), :id => field_id)
87 else
89 else
88 text_field_tag(field_name, '', :id => field_id)
90 text_field_tag(field_name, '', :id => field_id)
89 end
91 end
90 end
92 end
91
93
92 # Return a string used to display a custom value
94 # Return a string used to display a custom value
93 def show_value(custom_value)
95 def show_value(custom_value)
94 return "" unless custom_value
96 return "" unless custom_value
95 format_value(custom_value.value, custom_value.custom_field.field_format)
97 format_value(custom_value.value, custom_value.custom_field.field_format)
96 end
98 end
97
99
98 # Return a string used to display a custom value
100 # Return a string used to display a custom value
99 def format_value(value, field_format)
101 def format_value(value, field_format)
100 Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
102 Redmine::CustomFieldFormat.format_value(value, field_format) # Proxy
101 end
103 end
102
104
103 # Return an array of custom field formats which can be used in select_tag
105 # Return an array of custom field formats which can be used in select_tag
104 def custom_field_formats_for_select(custom_field)
106 def custom_field_formats_for_select(custom_field)
105 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
107 Redmine::CustomFieldFormat.as_select(custom_field.class.customized_class.name)
106 end
108 end
107
109
108 # Renders the custom_values in api views
110 # Renders the custom_values in api views
109 def render_api_custom_values(custom_values, api)
111 def render_api_custom_values(custom_values, api)
110 api.array :custom_fields do
112 api.array :custom_fields do
111 custom_values.each do |custom_value|
113 custom_values.each do |custom_value|
112 api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
114 api.custom_field :id => custom_value.custom_field_id, :name => custom_value.custom_field.name do
113 api.value custom_value.value
115 api.value custom_value.value
114 end
116 end
115 end
117 end
116 end unless custom_values.empty?
118 end unless custom_values.empty?
117 end
119 end
118 end
120 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module DocumentsHelper
20 module DocumentsHelper
19 end
21 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module EnumerationsHelper
20 module EnumerationsHelper
19 end
21 end
@@ -1,41 +1,43
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module GanttHelper
20 module GanttHelper
19
21
20 def gantt_zoom_link(gantt, in_or_out)
22 def gantt_zoom_link(gantt, in_or_out)
21 case in_or_out
23 case in_or_out
22 when :in
24 when :in
23 if gantt.zoom < 4
25 if gantt.zoom < 4
24 link_to_content_update l(:text_zoom_in),
26 link_to_content_update l(:text_zoom_in),
25 params.merge(gantt.params.merge(:zoom => (gantt.zoom+1))),
27 params.merge(gantt.params.merge(:zoom => (gantt.zoom+1))),
26 :class => 'icon icon-zoom-in'
28 :class => 'icon icon-zoom-in'
27 else
29 else
28 content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe
30 content_tag('span', l(:text_zoom_in), :class => 'icon icon-zoom-in').html_safe
29 end
31 end
30
32
31 when :out
33 when :out
32 if gantt.zoom > 1
34 if gantt.zoom > 1
33 link_to_content_update l(:text_zoom_out),
35 link_to_content_update l(:text_zoom_out),
34 params.merge(gantt.params.merge(:zoom => (gantt.zoom-1))),
36 params.merge(gantt.params.merge(:zoom => (gantt.zoom-1))),
35 :class => 'icon icon-zoom-out'
37 :class => 'icon icon-zoom-out'
36 else
38 else
37 content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe
39 content_tag('span', l(:text_zoom_out), :class => 'icon icon-zoom-out').html_safe
38 end
40 end
39 end
41 end
40 end
42 end
41 end
43 end
@@ -1,34 +1,36
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module GroupsHelper
20 module GroupsHelper
19 # Options for the new membership projects combo-box
21 # Options for the new membership projects combo-box
20 def options_for_membership_project_select(user, projects)
22 def options_for_membership_project_select(user, projects)
21 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
23 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
22 options << project_tree_options_for_select(projects) do |p|
24 options << project_tree_options_for_select(projects) do |p|
23 {:disabled => (user.projects.include?(p))}
25 {:disabled => (user.projects.include?(p))}
24 end
26 end
25 options
27 options
26 end
28 end
27
29
28 def group_settings_tabs
30 def group_settings_tabs
29 tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
31 tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general},
30 {:name => 'users', :partial => 'groups/users', :label => :label_user_plural},
32 {:name => 'users', :partial => 'groups/users', :label => :label_user_plural},
31 {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
33 {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
32 ]
34 ]
33 end
35 end
34 end
36 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module IssueCategoriesHelper
20 module IssueCategoriesHelper
19 end
21 end
@@ -1,2 +1,4
1 # encoding: utf-8
2 #
1 module IssueMovesHelper
3 module IssueMovesHelper
2 end
4 end
@@ -1,23 +1,25
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module IssueRelationsHelper
20 module IssueRelationsHelper
19 def collection_for_relation_type_select
21 def collection_for_relation_type_select
20 values = IssueRelation::TYPES
22 values = IssueRelation::TYPES
21 values.keys.sort{|x,y| values[x][:order] <=> values[y][:order]}.collect{|k| [l(values[k][:name]), k]}
23 values.keys.sort{|x,y| values[x][:order] <=> values[y][:order]}.collect{|k| [l(values[k][:name]), k]}
22 end
24 end
23 end
25 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module IssueStatusesHelper
20 module IssueStatusesHelper
19 end
21 end
@@ -1,302 +1,304
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module IssuesHelper
20 module IssuesHelper
19 include ApplicationHelper
21 include ApplicationHelper
20
22
21 def issue_list(issues, &block)
23 def issue_list(issues, &block)
22 ancestors = []
24 ancestors = []
23 issues.each do |issue|
25 issues.each do |issue|
24 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
26 while (ancestors.any? && !issue.is_descendant_of?(ancestors.last))
25 ancestors.pop
27 ancestors.pop
26 end
28 end
27 yield issue, ancestors.size
29 yield issue, ancestors.size
28 ancestors << issue unless issue.leaf?
30 ancestors << issue unless issue.leaf?
29 end
31 end
30 end
32 end
31
33
32 # Renders a HTML/CSS tooltip
34 # Renders a HTML/CSS tooltip
33 #
35 #
34 # To use, a trigger div is needed. This is a div with the class of "tooltip"
36 # To use, a trigger div is needed. This is a div with the class of "tooltip"
35 # that contains this method wrapped in a span with the class of "tip"
37 # that contains this method wrapped in a span with the class of "tip"
36 #
38 #
37 # <div class="tooltip"><%= link_to_issue(issue) %>
39 # <div class="tooltip"><%= link_to_issue(issue) %>
38 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
40 # <span class="tip"><%= render_issue_tooltip(issue) %></span>
39 # </div>
41 # </div>
40 #
42 #
41 def render_issue_tooltip(issue)
43 def render_issue_tooltip(issue)
42 @cached_label_status ||= l(:field_status)
44 @cached_label_status ||= l(:field_status)
43 @cached_label_start_date ||= l(:field_start_date)
45 @cached_label_start_date ||= l(:field_start_date)
44 @cached_label_due_date ||= l(:field_due_date)
46 @cached_label_due_date ||= l(:field_due_date)
45 @cached_label_assigned_to ||= l(:field_assigned_to)
47 @cached_label_assigned_to ||= l(:field_assigned_to)
46 @cached_label_priority ||= l(:field_priority)
48 @cached_label_priority ||= l(:field_priority)
47 @cached_label_project ||= l(:field_project)
49 @cached_label_project ||= l(:field_project)
48
50
49 (link_to_issue(issue) + "<br /><br />" +
51 (link_to_issue(issue) + "<br /><br />" +
50 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
52 "<strong>#{@cached_label_project}</strong>: #{link_to_project(issue.project)}<br />" +
51 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />" +
53 "<strong>#{@cached_label_status}</strong>: #{h(issue.status.name)}<br />" +
52 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
54 "<strong>#{@cached_label_start_date}</strong>: #{format_date(issue.start_date)}<br />" +
53 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
55 "<strong>#{@cached_label_due_date}</strong>: #{format_date(issue.due_date)}<br />" +
54 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />" +
56 "<strong>#{@cached_label_assigned_to}</strong>: #{h(issue.assigned_to)}<br />" +
55 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}").html_safe
57 "<strong>#{@cached_label_priority}</strong>: #{h(issue.priority.name)}").html_safe
56 end
58 end
57
59
58 def issue_heading(issue)
60 def issue_heading(issue)
59 h("#{issue.tracker} ##{issue.id}")
61 h("#{issue.tracker} ##{issue.id}")
60 end
62 end
61
63
62 def render_issue_subject_with_tree(issue)
64 def render_issue_subject_with_tree(issue)
63 s = ''
65 s = ''
64 ancestors = issue.root? ? [] : issue.ancestors.visible.all
66 ancestors = issue.root? ? [] : issue.ancestors.visible.all
65 ancestors.each do |ancestor|
67 ancestors.each do |ancestor|
66 s << '<div>' + content_tag('p', link_to_issue(ancestor))
68 s << '<div>' + content_tag('p', link_to_issue(ancestor))
67 end
69 end
68 s << '<div>'
70 s << '<div>'
69 subject = h(issue.subject)
71 subject = h(issue.subject)
70 if issue.is_private?
72 if issue.is_private?
71 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
73 subject = content_tag('span', l(:field_is_private), :class => 'private') + ' ' + subject
72 end
74 end
73 s << content_tag('h3', subject)
75 s << content_tag('h3', subject)
74 s << '</div>' * (ancestors.size + 1)
76 s << '</div>' * (ancestors.size + 1)
75 s.html_safe
77 s.html_safe
76 end
78 end
77
79
78 def render_descendants_tree(issue)
80 def render_descendants_tree(issue)
79 s = '<form><table class="list issues">'
81 s = '<form><table class="list issues">'
80 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
82 issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
81 s << content_tag('tr',
83 s << content_tag('tr',
82 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
84 content_tag('td', check_box_tag("ids[]", child.id, false, :id => nil), :class => 'checkbox') +
83 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
85 content_tag('td', link_to_issue(child, :truncate => 60), :class => 'subject') +
84 content_tag('td', h(child.status)) +
86 content_tag('td', h(child.status)) +
85 content_tag('td', link_to_user(child.assigned_to)) +
87 content_tag('td', link_to_user(child.assigned_to)) +
86 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
88 content_tag('td', progress_bar(child.done_ratio, :width => '80px')),
87 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
89 :class => "issue issue-#{child.id} hascontextmenu #{level > 0 ? "idnt idnt-#{level}" : nil}")
88 end
90 end
89 s << '</form></table>'
91 s << '</form></table>'
90 s.html_safe
92 s.html_safe
91 end
93 end
92
94
93 def render_custom_fields_rows(issue)
95 def render_custom_fields_rows(issue)
94 return if issue.custom_field_values.empty?
96 return if issue.custom_field_values.empty?
95 ordered_values = []
97 ordered_values = []
96 half = (issue.custom_field_values.size / 2.0).ceil
98 half = (issue.custom_field_values.size / 2.0).ceil
97 half.times do |i|
99 half.times do |i|
98 ordered_values << issue.custom_field_values[i]
100 ordered_values << issue.custom_field_values[i]
99 ordered_values << issue.custom_field_values[i + half]
101 ordered_values << issue.custom_field_values[i + half]
100 end
102 end
101 s = "<tr>\n"
103 s = "<tr>\n"
102 n = 0
104 n = 0
103 ordered_values.compact.each do |value|
105 ordered_values.compact.each do |value|
104 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
106 s << "</tr>\n<tr>\n" if n > 0 && (n % 2) == 0
105 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
107 s << "\t<th>#{ h(value.custom_field.name) }:</th><td>#{ simple_format_without_paragraph(h(show_value(value))) }</td>\n"
106 n += 1
108 n += 1
107 end
109 end
108 s << "</tr>\n"
110 s << "</tr>\n"
109 s.html_safe
111 s.html_safe
110 end
112 end
111
113
112 def issues_destroy_confirmation_message(issues)
114 def issues_destroy_confirmation_message(issues)
113 issues = [issues] unless issues.is_a?(Array)
115 issues = [issues] unless issues.is_a?(Array)
114 message = l(:text_issues_destroy_confirmation)
116 message = l(:text_issues_destroy_confirmation)
115 descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
117 descendant_count = issues.inject(0) {|memo, i| memo += (i.right - i.left - 1)/2}
116 if descendant_count > 0
118 if descendant_count > 0
117 issues.each do |issue|
119 issues.each do |issue|
118 next if issue.root?
120 next if issue.root?
119 issues.each do |other_issue|
121 issues.each do |other_issue|
120 descendant_count -= 1 if issue.is_descendant_of?(other_issue)
122 descendant_count -= 1 if issue.is_descendant_of?(other_issue)
121 end
123 end
122 end
124 end
123 if descendant_count > 0
125 if descendant_count > 0
124 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
126 message << "\n" + l(:text_issues_destroy_descendants_confirmation, :count => descendant_count)
125 end
127 end
126 end
128 end
127 message
129 message
128 end
130 end
129
131
130 def sidebar_queries
132 def sidebar_queries
131 unless @sidebar_queries
133 unless @sidebar_queries
132 # User can see public queries and his own queries
134 # User can see public queries and his own queries
133 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
135 visible = ARCondition.new(["is_public = ? OR user_id = ?", true, (User.current.logged? ? User.current.id : 0)])
134 # Project specific queries and global queries
136 # Project specific queries and global queries
135 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
137 visible << (@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id])
136 @sidebar_queries = Query.find(:all,
138 @sidebar_queries = Query.find(:all,
137 :select => 'id, name, is_public',
139 :select => 'id, name, is_public',
138 :order => "name ASC",
140 :order => "name ASC",
139 :conditions => visible.conditions)
141 :conditions => visible.conditions)
140 end
142 end
141 @sidebar_queries
143 @sidebar_queries
142 end
144 end
143
145
144 def query_links(title, queries)
146 def query_links(title, queries)
145 # links to #index on issues/show
147 # links to #index on issues/show
146 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
148 url_params = controller_name == 'issues' ? {:controller => 'issues', :action => 'index', :project_id => @project} : params
147
149
148 content_tag('h3', h(title)) +
150 content_tag('h3', h(title)) +
149 queries.collect {|query|
151 queries.collect {|query|
150 link_to(h(query.name), url_params.merge(:query_id => query))
152 link_to(h(query.name), url_params.merge(:query_id => query))
151 }.join('<br />')
153 }.join('<br />')
152 end
154 end
153
155
154 def render_sidebar_queries
156 def render_sidebar_queries
155 out = ''
157 out = ''
156 queries = sidebar_queries.select {|q| !q.is_public?}
158 queries = sidebar_queries.select {|q| !q.is_public?}
157 out << query_links(l(:label_my_queries), queries) if queries.any?
159 out << query_links(l(:label_my_queries), queries) if queries.any?
158 queries = sidebar_queries.select {|q| q.is_public?}
160 queries = sidebar_queries.select {|q| q.is_public?}
159 out << query_links(l(:label_query_plural), queries) if queries.any?
161 out << query_links(l(:label_query_plural), queries) if queries.any?
160 out
162 out
161 end
163 end
162
164
163 def show_detail(detail, no_html=false)
165 def show_detail(detail, no_html=false)
164 case detail.property
166 case detail.property
165 when 'attr'
167 when 'attr'
166 field = detail.prop_key.to_s.gsub(/\_id$/, "")
168 field = detail.prop_key.to_s.gsub(/\_id$/, "")
167 label = l(("field_" + field).to_sym)
169 label = l(("field_" + field).to_sym)
168 case
170 case
169 when ['due_date', 'start_date'].include?(detail.prop_key)
171 when ['due_date', 'start_date'].include?(detail.prop_key)
170 value = format_date(detail.value.to_date) if detail.value
172 value = format_date(detail.value.to_date) if detail.value
171 old_value = format_date(detail.old_value.to_date) if detail.old_value
173 old_value = format_date(detail.old_value.to_date) if detail.old_value
172
174
173 when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
175 when ['project_id', 'status_id', 'tracker_id', 'assigned_to_id', 'priority_id', 'category_id', 'fixed_version_id'].include?(detail.prop_key)
174 value = find_name_by_reflection(field, detail.value)
176 value = find_name_by_reflection(field, detail.value)
175 old_value = find_name_by_reflection(field, detail.old_value)
177 old_value = find_name_by_reflection(field, detail.old_value)
176
178
177 when detail.prop_key == 'estimated_hours'
179 when detail.prop_key == 'estimated_hours'
178 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
180 value = "%0.02f" % detail.value.to_f unless detail.value.blank?
179 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
181 old_value = "%0.02f" % detail.old_value.to_f unless detail.old_value.blank?
180
182
181 when detail.prop_key == 'parent_id'
183 when detail.prop_key == 'parent_id'
182 label = l(:field_parent_issue)
184 label = l(:field_parent_issue)
183 value = "##{detail.value}" unless detail.value.blank?
185 value = "##{detail.value}" unless detail.value.blank?
184 old_value = "##{detail.old_value}" unless detail.old_value.blank?
186 old_value = "##{detail.old_value}" unless detail.old_value.blank?
185
187
186 when detail.prop_key == 'is_private'
188 when detail.prop_key == 'is_private'
187 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
189 value = l(detail.value == "0" ? :general_text_No : :general_text_Yes) unless detail.value.blank?
188 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
190 old_value = l(detail.old_value == "0" ? :general_text_No : :general_text_Yes) unless detail.old_value.blank?
189 end
191 end
190 when 'cf'
192 when 'cf'
191 custom_field = CustomField.find_by_id(detail.prop_key)
193 custom_field = CustomField.find_by_id(detail.prop_key)
192 if custom_field
194 if custom_field
193 label = custom_field.name
195 label = custom_field.name
194 value = format_value(detail.value, custom_field.field_format) if detail.value
196 value = format_value(detail.value, custom_field.field_format) if detail.value
195 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
197 old_value = format_value(detail.old_value, custom_field.field_format) if detail.old_value
196 end
198 end
197 when 'attachment'
199 when 'attachment'
198 label = l(:label_attachment)
200 label = l(:label_attachment)
199 end
201 end
200 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
202 call_hook(:helper_issues_show_detail_after_setting, {:detail => detail, :label => label, :value => value, :old_value => old_value })
201
203
202 label ||= detail.prop_key
204 label ||= detail.prop_key
203 value ||= detail.value
205 value ||= detail.value
204 old_value ||= detail.old_value
206 old_value ||= detail.old_value
205
207
206 unless no_html
208 unless no_html
207 label = content_tag('strong', label)
209 label = content_tag('strong', label)
208 old_value = content_tag("i", h(old_value)) if detail.old_value
210 old_value = content_tag("i", h(old_value)) if detail.old_value
209 old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
211 old_value = content_tag("strike", old_value) if detail.old_value and detail.value.blank?
210 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
212 if detail.property == 'attachment' && !value.blank? && a = Attachment.find_by_id(detail.prop_key)
211 # Link to the attachment if it has not been removed
213 # Link to the attachment if it has not been removed
212 value = link_to_attachment(a)
214 value = link_to_attachment(a)
213 else
215 else
214 value = content_tag("i", h(value)) if value
216 value = content_tag("i", h(value)) if value
215 end
217 end
216 end
218 end
217
219
218 if detail.property == 'attr' && detail.prop_key == 'description'
220 if detail.property == 'attr' && detail.prop_key == 'description'
219 s = l(:text_journal_changed_no_detail, :label => label)
221 s = l(:text_journal_changed_no_detail, :label => label)
220 unless no_html
222 unless no_html
221 diff_link = link_to 'diff',
223 diff_link = link_to 'diff',
222 {:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
224 {:controller => 'journals', :action => 'diff', :id => detail.journal_id, :detail_id => detail.id},
223 :title => l(:label_view_diff)
225 :title => l(:label_view_diff)
224 s << " (#{ diff_link })"
226 s << " (#{ diff_link })"
225 end
227 end
226 s
228 s
227 elsif !detail.value.blank?
229 elsif !detail.value.blank?
228 case detail.property
230 case detail.property
229 when 'attr', 'cf'
231 when 'attr', 'cf'
230 if !detail.old_value.blank?
232 if !detail.old_value.blank?
231 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
233 l(:text_journal_changed, :label => label, :old => old_value, :new => value)
232 else
234 else
233 l(:text_journal_set_to, :label => label, :value => value)
235 l(:text_journal_set_to, :label => label, :value => value)
234 end
236 end
235 when 'attachment'
237 when 'attachment'
236 l(:text_journal_added, :label => label, :value => value)
238 l(:text_journal_added, :label => label, :value => value)
237 end
239 end
238 else
240 else
239 l(:text_journal_deleted, :label => label, :old => old_value)
241 l(:text_journal_deleted, :label => label, :old => old_value)
240 end
242 end
241 end
243 end
242
244
243 # Find the name of an associated record stored in the field attribute
245 # Find the name of an associated record stored in the field attribute
244 def find_name_by_reflection(field, id)
246 def find_name_by_reflection(field, id)
245 association = Issue.reflect_on_association(field.to_sym)
247 association = Issue.reflect_on_association(field.to_sym)
246 if association
248 if association
247 record = association.class_name.constantize.find_by_id(id)
249 record = association.class_name.constantize.find_by_id(id)
248 return record.name if record
250 return record.name if record
249 end
251 end
250 end
252 end
251
253
252 # Renders issue children recursively
254 # Renders issue children recursively
253 def render_api_issue_children(issue, api)
255 def render_api_issue_children(issue, api)
254 return if issue.leaf?
256 return if issue.leaf?
255 api.array :children do
257 api.array :children do
256 issue.children.each do |child|
258 issue.children.each do |child|
257 api.issue(:id => child.id) do
259 api.issue(:id => child.id) do
258 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
260 api.tracker(:id => child.tracker_id, :name => child.tracker.name) unless child.tracker.nil?
259 api.subject child.subject
261 api.subject child.subject
260 render_api_issue_children(child, api)
262 render_api_issue_children(child, api)
261 end
263 end
262 end
264 end
263 end
265 end
264 end
266 end
265
267
266 def issues_to_csv(issues, project, query, options={})
268 def issues_to_csv(issues, project, query, options={})
267 decimal_separator = l(:general_csv_decimal_separator)
269 decimal_separator = l(:general_csv_decimal_separator)
268 encoding = l(:general_csv_encoding)
270 encoding = l(:general_csv_encoding)
269 columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
271 columns = (options[:columns] == 'all' ? query.available_columns : query.columns)
270
272
271 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
273 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
272 # csv header fields
274 # csv header fields
273 csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
275 csv << [ "#" ] + columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) } +
274 (options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
276 (options[:description] ? [Redmine::CodesetUtil.from_utf8(l(:field_description), encoding)] : [])
275
277
276 # csv lines
278 # csv lines
277 issues.each do |issue|
279 issues.each do |issue|
278 col_values = columns.collect do |column|
280 col_values = columns.collect do |column|
279 s = if column.is_a?(QueryCustomFieldColumn)
281 s = if column.is_a?(QueryCustomFieldColumn)
280 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
282 cv = issue.custom_values.detect {|v| v.custom_field_id == column.custom_field.id}
281 show_value(cv)
283 show_value(cv)
282 else
284 else
283 value = issue.send(column.name)
285 value = issue.send(column.name)
284 if value.is_a?(Date)
286 if value.is_a?(Date)
285 format_date(value)
287 format_date(value)
286 elsif value.is_a?(Time)
288 elsif value.is_a?(Time)
287 format_time(value)
289 format_time(value)
288 elsif value.is_a?(Float)
290 elsif value.is_a?(Float)
289 value.to_s.gsub('.', decimal_separator)
291 value.to_s.gsub('.', decimal_separator)
290 else
292 else
291 value
293 value
292 end
294 end
293 end
295 end
294 s.to_s
296 s.to_s
295 end
297 end
296 csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
298 csv << [ issue.id.to_s ] + col_values.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) } +
297 (options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
299 (options[:description] ? [Redmine::CodesetUtil.from_utf8(issue.description, encoding)] : [])
298 end
300 end
299 end
301 end
300 export
302 export
301 end
303 end
302 end
304 end
@@ -1,42 +1,44
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module JournalsHelper
20 module JournalsHelper
19 def render_notes(issue, journal, options={})
21 def render_notes(issue, journal, options={})
20 content = ''
22 content = ''
21 editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
23 editable = User.current.logged? && (User.current.allowed_to?(:edit_issue_notes, issue.project) || (journal.user == User.current && User.current.allowed_to?(:edit_own_issue_notes, issue.project)))
22 links = []
24 links = []
23 if !journal.notes.blank?
25 if !journal.notes.blank?
24 links << link_to_remote(image_tag('comment.png'),
26 links << link_to_remote(image_tag('comment.png'),
25 { :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
27 { :url => {:controller => 'journals', :action => 'new', :id => issue, :journal_id => journal} },
26 :title => l(:button_quote)) if options[:reply_links]
28 :title => l(:button_quote)) if options[:reply_links]
27 links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
29 links << link_to_in_place_notes_editor(image_tag('edit.png'), "journal-#{journal.id}-notes",
28 { :controller => 'journals', :action => 'edit', :id => journal },
30 { :controller => 'journals', :action => 'edit', :id => journal },
29 :title => l(:button_edit)) if editable
31 :title => l(:button_edit)) if editable
30 end
32 end
31 content << content_tag('div', links.join(' '), :class => 'contextual') unless links.empty?
33 content << content_tag('div', links.join(' '), :class => 'contextual') unless links.empty?
32 content << textilizable(journal, :notes)
34 content << textilizable(journal, :notes)
33 css_classes = "wiki"
35 css_classes = "wiki"
34 css_classes << " editable" if editable
36 css_classes << " editable" if editable
35 content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => css_classes)
37 content_tag('div', content, :id => "journal-#{journal.id}-notes", :class => css_classes)
36 end
38 end
37
39
38 def link_to_in_place_notes_editor(text, field_id, url, options={})
40 def link_to_in_place_notes_editor(text, field_id, url, options={})
39 onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
41 onclick = "new Ajax.Request('#{url_for(url)}', {asynchronous:true, evalScripts:true, method:'get'}); return false;"
40 link_to text, '#', options.merge(:onclick => onclick)
42 link_to text, '#', options.merge(:onclick => onclick)
41 end
43 end
42 end
44 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module MailHandlerHelper
20 module MailHandlerHelper
19 end
21 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module MembersHelper
20 module MembersHelper
19 end
21 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module MessagesHelper
20 module MessagesHelper
19 end
21 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module MyHelper
20 module MyHelper
19 end
21 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module NewsHelper
20 module NewsHelper
19 end
21 end
@@ -1,108 +1,110
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module ProjectsHelper
20 module ProjectsHelper
19 def link_to_version(version, options = {})
21 def link_to_version(version, options = {})
20 return '' unless version && version.is_a?(Version)
22 return '' unless version && version.is_a?(Version)
21 link_to_if version.visible?, format_version_name(version), { :controller => 'versions', :action => 'show', :id => version }, options
23 link_to_if version.visible?, format_version_name(version), { :controller => 'versions', :action => 'show', :id => version }, options
22 end
24 end
23
25
24 def project_settings_tabs
26 def project_settings_tabs
25 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
27 tabs = [{:name => 'info', :action => :edit_project, :partial => 'projects/edit', :label => :label_information_plural},
26 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
28 {:name => 'modules', :action => :select_project_modules, :partial => 'projects/settings/modules', :label => :label_module_plural},
27 {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
29 {:name => 'members', :action => :manage_members, :partial => 'projects/settings/members', :label => :label_member_plural},
28 {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
30 {:name => 'versions', :action => :manage_versions, :partial => 'projects/settings/versions', :label => :label_version_plural},
29 {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
31 {:name => 'categories', :action => :manage_categories, :partial => 'projects/settings/issue_categories', :label => :label_issue_category_plural},
30 {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
32 {:name => 'wiki', :action => :manage_wiki, :partial => 'projects/settings/wiki', :label => :label_wiki},
31 {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
33 {:name => 'repository', :action => :manage_repository, :partial => 'projects/settings/repository', :label => :label_repository},
32 {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
34 {:name => 'boards', :action => :manage_boards, :partial => 'projects/settings/boards', :label => :label_board_plural},
33 {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities}
35 {:name => 'activities', :action => :manage_project_activities, :partial => 'projects/settings/activities', :label => :enumeration_activities}
34 ]
36 ]
35 tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
37 tabs.select {|tab| User.current.allowed_to?(tab[:action], @project)}
36 end
38 end
37
39
38 def parent_project_select_tag(project)
40 def parent_project_select_tag(project)
39 selected = project.parent
41 selected = project.parent
40 # retrieve the requested parent project
42 # retrieve the requested parent project
41 parent_id = (params[:project] && params[:project][:parent_id]) || params[:parent_id]
43 parent_id = (params[:project] && params[:project][:parent_id]) || params[:parent_id]
42 if parent_id
44 if parent_id
43 selected = (parent_id.blank? ? nil : Project.find(parent_id))
45 selected = (parent_id.blank? ? nil : Project.find(parent_id))
44 end
46 end
45
47
46 options = ''
48 options = ''
47 options << "<option value=''></option>" if project.allowed_parents.include?(nil)
49 options << "<option value=''></option>" if project.allowed_parents.include?(nil)
48 options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
50 options << project_tree_options_for_select(project.allowed_parents.compact, :selected => selected)
49 content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
51 content_tag('select', options.html_safe, :name => 'project[parent_id]', :id => 'project_parent_id')
50 end
52 end
51
53
52 # Renders a tree of projects as a nested set of unordered lists
54 # Renders a tree of projects as a nested set of unordered lists
53 # The given collection may be a subset of the whole project tree
55 # The given collection may be a subset of the whole project tree
54 # (eg. some intermediate nodes are private and can not be seen)
56 # (eg. some intermediate nodes are private and can not be seen)
55 def render_project_hierarchy(projects)
57 def render_project_hierarchy(projects)
56 s = ''
58 s = ''
57 if projects.any?
59 if projects.any?
58 ancestors = []
60 ancestors = []
59 original_project = @project
61 original_project = @project
60 projects.each do |project|
62 projects.each do |project|
61 # set the project environment to please macros.
63 # set the project environment to please macros.
62 @project = project
64 @project = project
63 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
65 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
64 s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
66 s << "<ul class='projects #{ ancestors.empty? ? 'root' : nil}'>\n"
65 else
67 else
66 ancestors.pop
68 ancestors.pop
67 s << "</li>"
69 s << "</li>"
68 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
70 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
69 ancestors.pop
71 ancestors.pop
70 s << "</ul></li>\n"
72 s << "</ul></li>\n"
71 end
73 end
72 end
74 end
73 classes = (ancestors.empty? ? 'root' : 'child')
75 classes = (ancestors.empty? ? 'root' : 'child')
74 s << "<li class='#{classes}'><div class='#{classes}'>" +
76 s << "<li class='#{classes}'><div class='#{classes}'>" +
75 link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
77 link_to_project(project, {}, :class => "project #{User.current.member_of?(project) ? 'my-project' : nil}")
76 s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
78 s << "<div class='wiki description'>#{textilizable(project.short_description, :project => project)}</div>" unless project.description.blank?
77 s << "</div>\n"
79 s << "</div>\n"
78 ancestors << project
80 ancestors << project
79 end
81 end
80 s << ("</li></ul>\n" * ancestors.size)
82 s << ("</li></ul>\n" * ancestors.size)
81 @project = original_project
83 @project = original_project
82 end
84 end
83 s.html_safe
85 s.html_safe
84 end
86 end
85
87
86 # Returns a set of options for a select field, grouped by project.
88 # Returns a set of options for a select field, grouped by project.
87 def version_options_for_select(versions, selected=nil)
89 def version_options_for_select(versions, selected=nil)
88 grouped = Hash.new {|h,k| h[k] = []}
90 grouped = Hash.new {|h,k| h[k] = []}
89 versions.each do |version|
91 versions.each do |version|
90 grouped[version.project.name] << [version.name, version.id]
92 grouped[version.project.name] << [version.name, version.id]
91 end
93 end
92 # Add in the selected
94 # Add in the selected
93 if selected && !versions.include?(selected)
95 if selected && !versions.include?(selected)
94 grouped[selected.project.name] << [selected.name, selected.id]
96 grouped[selected.project.name] << [selected.name, selected.id]
95 end
97 end
96
98
97 if grouped.keys.size > 1
99 if grouped.keys.size > 1
98 grouped_options_for_select(grouped, selected && selected.id)
100 grouped_options_for_select(grouped, selected && selected.id)
99 else
101 else
100 options_for_select((grouped.values.first || []), selected && selected.id)
102 options_for_select((grouped.values.first || []), selected && selected.id)
101 end
103 end
102 end
104 end
103
105
104 def format_version_sharing(sharing)
106 def format_version_sharing(sharing)
105 sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing)
107 sharing = 'none' unless Version::VERSION_SHARINGS.include?(sharing)
106 l("label_version_sharing_#{sharing}")
108 l("label_version_sharing_#{sharing}")
107 end
109 end
108 end
110 end
@@ -1,103 +1,105
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module QueriesHelper
20 module QueriesHelper
19
21
20 def operators_for_select(filter_type)
22 def operators_for_select(filter_type)
21 Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
23 Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]}
22 end
24 end
23
25
24 def column_header(column)
26 def column_header(column)
25 column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
27 column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
26 :default_order => column.default_order) :
28 :default_order => column.default_order) :
27 content_tag('th', h(column.caption))
29 content_tag('th', h(column.caption))
28 end
30 end
29
31
30 def column_content(column, issue)
32 def column_content(column, issue)
31 value = column.value(issue)
33 value = column.value(issue)
32
34
33 case value.class.name
35 case value.class.name
34 when 'String'
36 when 'String'
35 if column.name == :subject
37 if column.name == :subject
36 link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
38 link_to(h(value), :controller => 'issues', :action => 'show', :id => issue)
37 else
39 else
38 h(value)
40 h(value)
39 end
41 end
40 when 'Time'
42 when 'Time'
41 format_time(value)
43 format_time(value)
42 when 'Date'
44 when 'Date'
43 format_date(value)
45 format_date(value)
44 when 'Fixnum', 'Float'
46 when 'Fixnum', 'Float'
45 if column.name == :done_ratio
47 if column.name == :done_ratio
46 progress_bar(value, :width => '80px')
48 progress_bar(value, :width => '80px')
47 else
49 else
48 h(value.to_s)
50 h(value.to_s)
49 end
51 end
50 when 'User'
52 when 'User'
51 link_to_user value
53 link_to_user value
52 when 'Project'
54 when 'Project'
53 link_to_project value
55 link_to_project value
54 when 'Version'
56 when 'Version'
55 link_to(h(value), :controller => 'versions', :action => 'show', :id => value)
57 link_to(h(value), :controller => 'versions', :action => 'show', :id => value)
56 when 'TrueClass'
58 when 'TrueClass'
57 l(:general_text_Yes)
59 l(:general_text_Yes)
58 when 'FalseClass'
60 when 'FalseClass'
59 l(:general_text_No)
61 l(:general_text_No)
60 when 'Issue'
62 when 'Issue'
61 link_to_issue(value, :subject => false)
63 link_to_issue(value, :subject => false)
62 else
64 else
63 h(value)
65 h(value)
64 end
66 end
65 end
67 end
66
68
67 # Retrieve query from session or build a new query
69 # Retrieve query from session or build a new query
68 def retrieve_query
70 def retrieve_query
69 if !params[:query_id].blank?
71 if !params[:query_id].blank?
70 cond = "project_id IS NULL"
72 cond = "project_id IS NULL"
71 cond << " OR project_id = #{@project.id}" if @project
73 cond << " OR project_id = #{@project.id}" if @project
72 @query = Query.find(params[:query_id], :conditions => cond)
74 @query = Query.find(params[:query_id], :conditions => cond)
73 raise ::Unauthorized unless @query.visible?
75 raise ::Unauthorized unless @query.visible?
74 @query.project = @project
76 @query.project = @project
75 session[:query] = {:id => @query.id, :project_id => @query.project_id}
77 session[:query] = {:id => @query.id, :project_id => @query.project_id}
76 sort_clear
78 sort_clear
77 elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
79 elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
78 # Give it a name, required to be valid
80 # Give it a name, required to be valid
79 @query = Query.new(:name => "_")
81 @query = Query.new(:name => "_")
80 @query.project = @project
82 @query.project = @project
81 build_query_from_params
83 build_query_from_params
82 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
84 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
83 else
85 else
84 # retrieve from session
86 # retrieve from session
85 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
87 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
86 @query ||= Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
88 @query ||= Query.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
87 @query.project = @project
89 @query.project = @project
88 end
90 end
89 end
91 end
90
92
91 def build_query_from_params
93 def build_query_from_params
92 if params[:fields] || params[:f]
94 if params[:fields] || params[:f]
93 @query.filters = {}
95 @query.filters = {}
94 @query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
96 @query.add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
95 else
97 else
96 @query.available_filters.keys.each do |field|
98 @query.available_filters.keys.each do |field|
97 @query.add_short_filter(field, params[field]) if params[field]
99 @query.add_short_filter(field, params[field]) if params[field]
98 end
100 end
99 end
101 end
100 @query.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
102 @query.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
101 @query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
103 @query.column_names = params[:c] || (params[:query] && params[:query][:column_names])
102 end
104 end
103 end
105 end
@@ -1,36 +1,38
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module ReportsHelper
20 module ReportsHelper
19
21
20 def aggregate(data, criteria)
22 def aggregate(data, criteria)
21 a = 0
23 a = 0
22 data.each { |row|
24 data.each { |row|
23 match = 1
25 match = 1
24 criteria.each { |k, v|
26 criteria.each { |k, v|
25 match = 0 unless (row[k].to_s == v.to_s) || (k == 'closed' && row[k] == (v == 0 ? "f" : "t"))
27 match = 0 unless (row[k].to_s == v.to_s) || (k == 'closed' && row[k] == (v == 0 ? "f" : "t"))
26 } unless criteria.nil?
28 } unless criteria.nil?
27 a = a + row["total"].to_i if match == 1
29 a = a + row["total"].to_i if match == 1
28 } unless data.nil?
30 } unless data.nil?
29 a
31 a
30 end
32 end
31
33
32 def aggregate_link(data, criteria, *args)
34 def aggregate_link(data, criteria, *args)
33 a = aggregate data, criteria
35 a = aggregate data, criteria
34 a > 0 ? link_to(h(a), *args) : '-'
36 a > 0 ? link_to(h(a), *args) : '-'
35 end
37 end
36 end
38 end
@@ -1,311 +1,313
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 require 'iconv'
20 require 'iconv'
19 require 'redmine/codeset_util'
21 require 'redmine/codeset_util'
20
22
21 module RepositoriesHelper
23 module RepositoriesHelper
22 def format_revision(revision)
24 def format_revision(revision)
23 if revision.respond_to? :format_identifier
25 if revision.respond_to? :format_identifier
24 revision.format_identifier
26 revision.format_identifier
25 else
27 else
26 revision.to_s
28 revision.to_s
27 end
29 end
28 end
30 end
29
31
30 def truncate_at_line_break(text, length = 255)
32 def truncate_at_line_break(text, length = 255)
31 if text
33 if text
32 text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...')
34 text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...')
33 end
35 end
34 end
36 end
35
37
36 def render_properties(properties)
38 def render_properties(properties)
37 unless properties.nil? || properties.empty?
39 unless properties.nil? || properties.empty?
38 content = ''
40 content = ''
39 properties.keys.sort.each do |property|
41 properties.keys.sort.each do |property|
40 content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>".html_safe)
42 content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>".html_safe)
41 end
43 end
42 content_tag('ul', content.html_safe, :class => 'properties')
44 content_tag('ul', content.html_safe, :class => 'properties')
43 end
45 end
44 end
46 end
45
47
46 def render_changeset_changes
48 def render_changeset_changes
47 changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
49 changes = @changeset.changes.find(:all, :limit => 1000, :order => 'path').collect do |change|
48 case change.action
50 case change.action
49 when 'A'
51 when 'A'
50 # Detects moved/copied files
52 # Detects moved/copied files
51 if !change.from_path.blank?
53 if !change.from_path.blank?
52 change.action =
54 change.action =
53 @changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
55 @changeset.changes.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
54 end
56 end
55 change
57 change
56 when 'D'
58 when 'D'
57 @changeset.changes.detect {|c| c.from_path == change.path} ? nil : change
59 @changeset.changes.detect {|c| c.from_path == change.path} ? nil : change
58 else
60 else
59 change
61 change
60 end
62 end
61 end.compact
63 end.compact
62
64
63 tree = { }
65 tree = { }
64 changes.each do |change|
66 changes.each do |change|
65 p = tree
67 p = tree
66 dirs = change.path.to_s.split('/').select {|d| !d.blank?}
68 dirs = change.path.to_s.split('/').select {|d| !d.blank?}
67 path = ''
69 path = ''
68 dirs.each do |dir|
70 dirs.each do |dir|
69 path += '/' + dir
71 path += '/' + dir
70 p[:s] ||= {}
72 p[:s] ||= {}
71 p = p[:s]
73 p = p[:s]
72 p[path] ||= {}
74 p[path] ||= {}
73 p = p[path]
75 p = p[path]
74 end
76 end
75 p[:c] = change
77 p[:c] = change
76 end
78 end
77 render_changes_tree(tree[:s])
79 render_changes_tree(tree[:s])
78 end
80 end
79
81
80 def render_changes_tree(tree)
82 def render_changes_tree(tree)
81 return '' if tree.nil?
83 return '' if tree.nil?
82 output = ''
84 output = ''
83 output << '<ul>'
85 output << '<ul>'
84 tree.keys.sort.each do |file|
86 tree.keys.sort.each do |file|
85 style = 'change'
87 style = 'change'
86 text = File.basename(h(file))
88 text = File.basename(h(file))
87 if s = tree[file][:s]
89 if s = tree[file][:s]
88 style << ' folder'
90 style << ' folder'
89 path_param = to_path_param(@repository.relative_path(file))
91 path_param = to_path_param(@repository.relative_path(file))
90 text = link_to(h(text), :controller => 'repositories',
92 text = link_to(h(text), :controller => 'repositories',
91 :action => 'show',
93 :action => 'show',
92 :id => @project,
94 :id => @project,
93 :path => path_param,
95 :path => path_param,
94 :rev => @changeset.identifier)
96 :rev => @changeset.identifier)
95 output << "<li class='#{style}'>#{text}</li>"
97 output << "<li class='#{style}'>#{text}</li>"
96 output << render_changes_tree(s)
98 output << render_changes_tree(s)
97 elsif c = tree[file][:c]
99 elsif c = tree[file][:c]
98 style << " change-#{c.action}"
100 style << " change-#{c.action}"
99 path_param = to_path_param(@repository.relative_path(c.path))
101 path_param = to_path_param(@repository.relative_path(c.path))
100 text = link_to(h(text), :controller => 'repositories',
102 text = link_to(h(text), :controller => 'repositories',
101 :action => 'entry',
103 :action => 'entry',
102 :id => @project,
104 :id => @project,
103 :path => path_param,
105 :path => path_param,
104 :rev => @changeset.identifier) unless c.action == 'D'
106 :rev => @changeset.identifier) unless c.action == 'D'
105 text << " - #{h(c.revision)}" unless c.revision.blank?
107 text << " - #{h(c.revision)}" unless c.revision.blank?
106 text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories',
108 text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories',
107 :action => 'diff',
109 :action => 'diff',
108 :id => @project,
110 :id => @project,
109 :path => path_param,
111 :path => path_param,
110 :rev => @changeset.identifier) + ') '.html_safe if c.action == 'M'
112 :rev => @changeset.identifier) + ') '.html_safe if c.action == 'M'
111 text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank?
113 text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank?
112 output << "<li class='#{style}'>#{text}</li>"
114 output << "<li class='#{style}'>#{text}</li>"
113 end
115 end
114 end
116 end
115 output << '</ul>'
117 output << '</ul>'
116 output.html_safe
118 output.html_safe
117 end
119 end
118
120
119 def repository_field_tags(form, repository)
121 def repository_field_tags(form, repository)
120 method = repository.class.name.demodulize.underscore + "_field_tags"
122 method = repository.class.name.demodulize.underscore + "_field_tags"
121 if repository.is_a?(Repository) &&
123 if repository.is_a?(Repository) &&
122 respond_to?(method) && method != 'repository_field_tags'
124 respond_to?(method) && method != 'repository_field_tags'
123 send(method, form, repository)
125 send(method, form, repository)
124 end
126 end
125 end
127 end
126
128
127 def scm_select_tag(repository)
129 def scm_select_tag(repository)
128 scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
130 scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
129 Redmine::Scm::Base.all.each do |scm|
131 Redmine::Scm::Base.all.each do |scm|
130 if Setting.enabled_scm.include?(scm) ||
132 if Setting.enabled_scm.include?(scm) ||
131 (repository && repository.class.name.demodulize == scm)
133 (repository && repository.class.name.demodulize == scm)
132 scm_options << ["Repository::#{scm}".constantize.scm_name, scm]
134 scm_options << ["Repository::#{scm}".constantize.scm_name, scm]
133 end
135 end
134 end
136 end
135 select_tag('repository_scm',
137 select_tag('repository_scm',
136 options_for_select(scm_options, repository.class.name.demodulize),
138 options_for_select(scm_options, repository.class.name.demodulize),
137 :disabled => (repository && !repository.new_record?),
139 :disabled => (repository && !repository.new_record?),
138 :onchange => remote_function(
140 :onchange => remote_function(
139 :url => {
141 :url => {
140 :controller => 'repositories',
142 :controller => 'repositories',
141 :action => 'edit',
143 :action => 'edit',
142 :id => @project
144 :id => @project
143 },
145 },
144 :method => :get,
146 :method => :get,
145 :with => "Form.serialize(this.form)")
147 :with => "Form.serialize(this.form)")
146 )
148 )
147 end
149 end
148
150
149 def with_leading_slash(path)
151 def with_leading_slash(path)
150 path.to_s.starts_with?('/') ? path : "/#{path}"
152 path.to_s.starts_with?('/') ? path : "/#{path}"
151 end
153 end
152
154
153 def without_leading_slash(path)
155 def without_leading_slash(path)
154 path.gsub(%r{^/+}, '')
156 path.gsub(%r{^/+}, '')
155 end
157 end
156
158
157 def subversion_field_tags(form, repository)
159 def subversion_field_tags(form, repository)
158 content_tag('p', form.text_field(:url, :size => 60, :required => true,
160 content_tag('p', form.text_field(:url, :size => 60, :required => true,
159 :disabled => (repository && !repository.root_url.blank?)) +
161 :disabled => (repository && !repository.root_url.blank?)) +
160 '<br />'.html_safe +
162 '<br />'.html_safe +
161 '(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
163 '(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
162 content_tag('p', form.text_field(:login, :size => 30)) +
164 content_tag('p', form.text_field(:login, :size => 30)) +
163 content_tag('p', form.password_field(
165 content_tag('p', form.password_field(
164 :password, :size => 30, :name => 'ignore',
166 :password, :size => 30, :name => 'ignore',
165 :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
167 :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
166 :onfocus => "this.value=''; this.name='repository[password]';",
168 :onfocus => "this.value=''; this.name='repository[password]';",
167 :onchange => "this.name='repository[password]';"))
169 :onchange => "this.name='repository[password]';"))
168 end
170 end
169
171
170 def darcs_field_tags(form, repository)
172 def darcs_field_tags(form, repository)
171 content_tag('p', form.text_field(
173 content_tag('p', form.text_field(
172 :url, :label => l(:field_path_to_repository),
174 :url, :label => l(:field_path_to_repository),
173 :size => 60, :required => true,
175 :size => 60, :required => true,
174 :disabled => (repository && !repository.new_record?))) +
176 :disabled => (repository && !repository.new_record?))) +
175 content_tag('p', form.select(
177 content_tag('p', form.select(
176 :log_encoding, [nil] + Setting::ENCODINGS,
178 :log_encoding, [nil] + Setting::ENCODINGS,
177 :label => l(:field_commit_logs_encoding), :required => true))
179 :label => l(:field_commit_logs_encoding), :required => true))
178 end
180 end
179
181
180 def mercurial_field_tags(form, repository)
182 def mercurial_field_tags(form, repository)
181 content_tag('p', form.text_field(
183 content_tag('p', form.text_field(
182 :url, :label => l(:field_path_to_repository),
184 :url, :label => l(:field_path_to_repository),
183 :size => 60, :required => true,
185 :size => 60, :required => true,
184 :disabled => (repository && !repository.root_url.blank?)
186 :disabled => (repository && !repository.root_url.blank?)
185 ) +
187 ) +
186 '<br />'.html_safe + l(:text_mercurial_repository_note)) +
188 '<br />'.html_safe + l(:text_mercurial_repository_note)) +
187 content_tag('p', form.select(
189 content_tag('p', form.select(
188 :path_encoding, [nil] + Setting::ENCODINGS,
190 :path_encoding, [nil] + Setting::ENCODINGS,
189 :label => l(:field_scm_path_encoding)
191 :label => l(:field_scm_path_encoding)
190 ) +
192 ) +
191 '<br />'.html_safe + l(:text_scm_path_encoding_note))
193 '<br />'.html_safe + l(:text_scm_path_encoding_note))
192 end
194 end
193
195
194 def git_field_tags(form, repository)
196 def git_field_tags(form, repository)
195 content_tag('p', form.text_field(
197 content_tag('p', form.text_field(
196 :url, :label => l(:field_path_to_repository),
198 :url, :label => l(:field_path_to_repository),
197 :size => 60, :required => true,
199 :size => 60, :required => true,
198 :disabled => (repository && !repository.root_url.blank?)
200 :disabled => (repository && !repository.root_url.blank?)
199 ) +
201 ) +
200 '<br />'.html_safe +
202 '<br />'.html_safe +
201 l(:text_git_repository_note)) +
203 l(:text_git_repository_note)) +
202 content_tag('p', form.select(
204 content_tag('p', form.select(
203 :path_encoding, [nil] + Setting::ENCODINGS,
205 :path_encoding, [nil] + Setting::ENCODINGS,
204 :label => l(:field_scm_path_encoding)
206 :label => l(:field_scm_path_encoding)
205 ) +
207 ) +
206 '<br />'.html_safe + l(:text_scm_path_encoding_note)) +
208 '<br />'.html_safe + l(:text_scm_path_encoding_note)) +
207 content_tag('p', form.check_box(
209 content_tag('p', form.check_box(
208 :extra_report_last_commit,
210 :extra_report_last_commit,
209 :label => l(:label_git_report_last_commit)
211 :label => l(:label_git_report_last_commit)
210 ))
212 ))
211 end
213 end
212
214
213 def cvs_field_tags(form, repository)
215 def cvs_field_tags(form, repository)
214 content_tag('p', form.text_field(
216 content_tag('p', form.text_field(
215 :root_url,
217 :root_url,
216 :label => l(:field_cvsroot),
218 :label => l(:field_cvsroot),
217 :size => 60, :required => true,
219 :size => 60, :required => true,
218 :disabled => !repository.new_record?)) +
220 :disabled => !repository.new_record?)) +
219 content_tag('p', form.text_field(
221 content_tag('p', form.text_field(
220 :url,
222 :url,
221 :label => l(:field_cvs_module),
223 :label => l(:field_cvs_module),
222 :size => 30, :required => true,
224 :size => 30, :required => true,
223 :disabled => !repository.new_record?)) +
225 :disabled => !repository.new_record?)) +
224 content_tag('p', form.select(
226 content_tag('p', form.select(
225 :log_encoding, [nil] + Setting::ENCODINGS,
227 :log_encoding, [nil] + Setting::ENCODINGS,
226 :label => l(:field_commit_logs_encoding), :required => true)) +
228 :label => l(:field_commit_logs_encoding), :required => true)) +
227 content_tag('p', form.select(
229 content_tag('p', form.select(
228 :path_encoding, [nil] + Setting::ENCODINGS,
230 :path_encoding, [nil] + Setting::ENCODINGS,
229 :label => l(:field_scm_path_encoding)
231 :label => l(:field_scm_path_encoding)
230 ) +
232 ) +
231 '<br />'.html_safe + l(:text_scm_path_encoding_note))
233 '<br />'.html_safe + l(:text_scm_path_encoding_note))
232 end
234 end
233
235
234 def bazaar_field_tags(form, repository)
236 def bazaar_field_tags(form, repository)
235 content_tag('p', form.text_field(
237 content_tag('p', form.text_field(
236 :url, :label => l(:field_path_to_repository),
238 :url, :label => l(:field_path_to_repository),
237 :size => 60, :required => true,
239 :size => 60, :required => true,
238 :disabled => (repository && !repository.new_record?))) +
240 :disabled => (repository && !repository.new_record?))) +
239 content_tag('p', form.select(
241 content_tag('p', form.select(
240 :log_encoding, [nil] + Setting::ENCODINGS,
242 :log_encoding, [nil] + Setting::ENCODINGS,
241 :label => l(:field_commit_logs_encoding), :required => true))
243 :label => l(:field_commit_logs_encoding), :required => true))
242 end
244 end
243
245
244 def filesystem_field_tags(form, repository)
246 def filesystem_field_tags(form, repository)
245 content_tag('p', form.text_field(
247 content_tag('p', form.text_field(
246 :url, :label => l(:field_root_directory),
248 :url, :label => l(:field_root_directory),
247 :size => 60, :required => true,
249 :size => 60, :required => true,
248 :disabled => (repository && !repository.root_url.blank?))) +
250 :disabled => (repository && !repository.root_url.blank?))) +
249 content_tag('p', form.select(
251 content_tag('p', form.select(
250 :path_encoding, [nil] + Setting::ENCODINGS,
252 :path_encoding, [nil] + Setting::ENCODINGS,
251 :label => l(:field_scm_path_encoding)
253 :label => l(:field_scm_path_encoding)
252 ) +
254 ) +
253 '<br />'.html_safe + l(:text_scm_path_encoding_note))
255 '<br />'.html_safe + l(:text_scm_path_encoding_note))
254 end
256 end
255
257
256 def index_commits(commits, heads, href_proc = nil)
258 def index_commits(commits, heads, href_proc = nil)
257 return nil if commits.nil? or commits.first.parents.nil?
259 return nil if commits.nil? or commits.first.parents.nil?
258 map = {}
260 map = {}
259 commit_hashes = []
261 commit_hashes = []
260 refs_map = {}
262 refs_map = {}
261 href_proc ||= Proc.new {|x|x}
263 href_proc ||= Proc.new {|x|x}
262 heads.each{|r| refs_map[r.scmid] ||= []; refs_map[r.scmid] << r}
264 heads.each{|r| refs_map[r.scmid] ||= []; refs_map[r.scmid] << r}
263 commits.reverse.each_with_index do |c, i|
265 commits.reverse.each_with_index do |c, i|
264 h = {}
266 h = {}
265 h[:parents] = c.parents.collect do |p|
267 h[:parents] = c.parents.collect do |p|
266 [p.scmid, 0, 0]
268 [p.scmid, 0, 0]
267 end
269 end
268 h[:rdmid] = i
270 h[:rdmid] = i
269 h[:space] = 0
271 h[:space] = 0
270 h[:refs] = refs_map[c.scmid].join(" ") if refs_map.include? c.scmid
272 h[:refs] = refs_map[c.scmid].join(" ") if refs_map.include? c.scmid
271 h[:scmid] = c.scmid
273 h[:scmid] = c.scmid
272 h[:href] = href_proc.call(c.scmid)
274 h[:href] = href_proc.call(c.scmid)
273 commit_hashes << h
275 commit_hashes << h
274 map[c.scmid] = h
276 map[c.scmid] = h
275 end
277 end
276 heads.sort! do |a,b|
278 heads.sort! do |a,b|
277 a.to_s <=> b.to_s
279 a.to_s <=> b.to_s
278 end
280 end
279 j = 0
281 j = 0
280 heads.each do |h|
282 heads.each do |h|
281 if map.include? h.scmid then
283 if map.include? h.scmid then
282 j = mark_chain(j += 1, map[h.scmid], map)
284 j = mark_chain(j += 1, map[h.scmid], map)
283 end
285 end
284 end
286 end
285 # when no head matched anything use first commit
287 # when no head matched anything use first commit
286 if j == 0 then
288 if j == 0 then
287 mark_chain(j += 1, map.values.first, map)
289 mark_chain(j += 1, map.values.first, map)
288 end
290 end
289 map
291 map
290 end
292 end
291
293
292 def mark_chain(mark, commit, map)
294 def mark_chain(mark, commit, map)
293 stack = [[mark, commit]]
295 stack = [[mark, commit]]
294 markmax = mark
296 markmax = mark
295 until stack.empty?
297 until stack.empty?
296 current = stack.pop
298 current = stack.pop
297 m, commit = current
299 m, commit = current
298 commit[:space] = m if commit[:space] == 0
300 commit[:space] = m if commit[:space] == 0
299 m1 = m - 1
301 m1 = m - 1
300 commit[:parents].each_with_index do |p, i|
302 commit[:parents].each_with_index do |p, i|
301 psha = p[0]
303 psha = p[0]
302 if map.include? psha and map[psha][:space] == 0 then
304 if map.include? psha and map[psha][:space] == 0 then
303 stack << [m1 += 1, map[psha]] if i == 0
305 stack << [m1 += 1, map[psha]] if i == 0
304 stack = [[m1 += 1, map[psha]]] + stack if i > 0
306 stack = [[m1 += 1, map[psha]]] + stack if i > 0
305 end
307 end
306 end
308 end
307 markmax = m1 if markmax < m1
309 markmax = m1 if markmax < m1
308 end
310 end
309 markmax
311 markmax
310 end
312 end
311 end
313 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module RolesHelper
20 module RolesHelper
19 end
21 end
@@ -1,66 +1,68
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module SearchHelper
20 module SearchHelper
19 def highlight_tokens(text, tokens)
21 def highlight_tokens(text, tokens)
20 return text unless text && tokens && !tokens.empty?
22 return text unless text && tokens && !tokens.empty?
21 re_tokens = tokens.collect {|t| Regexp.escape(t)}
23 re_tokens = tokens.collect {|t| Regexp.escape(t)}
22 regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
24 regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
23 result = ''
25 result = ''
24 text.split(regexp).each_with_index do |words, i|
26 text.split(regexp).each_with_index do |words, i|
25 if result.length > 1200
27 if result.length > 1200
26 # maximum length of the preview reached
28 # maximum length of the preview reached
27 result << '...'
29 result << '...'
28 break
30 break
29 end
31 end
30 words = words.mb_chars
32 words = words.mb_chars
31 if i.even?
33 if i.even?
32 result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
34 result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
33 else
35 else
34 t = (tokens.index(words.downcase) || 0) % 4
36 t = (tokens.index(words.downcase) || 0) % 4
35 result << content_tag('span', h(words), :class => "highlight token-#{t}")
37 result << content_tag('span', h(words), :class => "highlight token-#{t}")
36 end
38 end
37 end
39 end
38 result
40 result
39 end
41 end
40
42
41 def type_label(t)
43 def type_label(t)
42 l("label_#{t.singularize}_plural", :default => t.to_s.humanize)
44 l("label_#{t.singularize}_plural", :default => t.to_s.humanize)
43 end
45 end
44
46
45 def project_select_tag
47 def project_select_tag
46 options = [[l(:label_project_all), 'all']]
48 options = [[l(:label_project_all), 'all']]
47 options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
49 options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
48 options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
50 options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
49 options << [@project.name, ''] unless @project.nil?
51 options << [@project.name, ''] unless @project.nil?
50 label_tag("scope", l(:description_project_scope), :class => "hidden-for-sighted") +
52 label_tag("scope", l(:description_project_scope), :class => "hidden-for-sighted") +
51 select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
53 select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
52 end
54 end
53
55
54 def render_results_by_type(results_by_type)
56 def render_results_by_type(results_by_type)
55 links = []
57 links = []
56 # Sorts types by results count
58 # Sorts types by results count
57 results_by_type.keys.sort {|a, b| results_by_type[b] <=> results_by_type[a]}.each do |t|
59 results_by_type.keys.sort {|a, b| results_by_type[b] <=> results_by_type[a]}.each do |t|
58 c = results_by_type[t]
60 c = results_by_type[t]
59 next if c == 0
61 next if c == 0
60 text = "#{type_label(t)} (#{c})"
62 text = "#{type_label(t)} (#{c})"
61 links << link_to(h(text), :q => params[:q], :titles_only => params[:titles_only],
63 links << link_to(h(text), :q => params[:q], :titles_only => params[:titles_only],
62 :all_words => params[:all_words], :scope => params[:scope], t => 1)
64 :all_words => params[:all_words], :scope => params[:scope], t => 1)
63 end
65 end
64 ('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
66 ('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
65 end
67 end
66 end
68 end
@@ -1,91 +1,93
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module SettingsHelper
20 module SettingsHelper
19 def administration_settings_tabs
21 def administration_settings_tabs
20 tabs = [{:name => 'general', :partial => 'settings/general', :label => :label_general},
22 tabs = [{:name => 'general', :partial => 'settings/general', :label => :label_general},
21 {:name => 'display', :partial => 'settings/display', :label => :label_display},
23 {:name => 'display', :partial => 'settings/display', :label => :label_display},
22 {:name => 'authentication', :partial => 'settings/authentication', :label => :label_authentication},
24 {:name => 'authentication', :partial => 'settings/authentication', :label => :label_authentication},
23 {:name => 'projects', :partial => 'settings/projects', :label => :label_project_plural},
25 {:name => 'projects', :partial => 'settings/projects', :label => :label_project_plural},
24 {:name => 'issues', :partial => 'settings/issues', :label => :label_issue_tracking},
26 {:name => 'issues', :partial => 'settings/issues', :label => :label_issue_tracking},
25 {:name => 'notifications', :partial => 'settings/notifications', :label => :field_mail_notification},
27 {:name => 'notifications', :partial => 'settings/notifications', :label => :field_mail_notification},
26 {:name => 'mail_handler', :partial => 'settings/mail_handler', :label => :label_incoming_emails},
28 {:name => 'mail_handler', :partial => 'settings/mail_handler', :label => :label_incoming_emails},
27 {:name => 'repositories', :partial => 'settings/repositories', :label => :label_repository_plural}
29 {:name => 'repositories', :partial => 'settings/repositories', :label => :label_repository_plural}
28 ]
30 ]
29 end
31 end
30
32
31 def setting_select(setting, choices, options={})
33 def setting_select(setting, choices, options={})
32 if blank_text = options.delete(:blank)
34 if blank_text = options.delete(:blank)
33 choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices
35 choices = [[blank_text.is_a?(Symbol) ? l(blank_text) : blank_text, '']] + choices
34 end
36 end
35 setting_label(setting, options).html_safe +
37 setting_label(setting, options).html_safe +
36 select_tag("settings[#{setting}]",
38 select_tag("settings[#{setting}]",
37 options_for_select(choices, Setting.send(setting).to_s),
39 options_for_select(choices, Setting.send(setting).to_s),
38 options).html_safe
40 options).html_safe
39 end
41 end
40
42
41 def setting_multiselect(setting, choices, options={})
43 def setting_multiselect(setting, choices, options={})
42 setting_values = Setting.send(setting)
44 setting_values = Setting.send(setting)
43 setting_values = [] unless setting_values.is_a?(Array)
45 setting_values = [] unless setting_values.is_a?(Array)
44
46
45 setting_label(setting, options).html_safe +
47 setting_label(setting, options).html_safe +
46 hidden_field_tag("settings[#{setting}][]", '').html_safe +
48 hidden_field_tag("settings[#{setting}][]", '').html_safe +
47 choices.collect do |choice|
49 choices.collect do |choice|
48 text, value = (choice.is_a?(Array) ? choice : [choice, choice])
50 text, value = (choice.is_a?(Array) ? choice : [choice, choice])
49 content_tag(
51 content_tag(
50 'label',
52 'label',
51 check_box_tag(
53 check_box_tag(
52 "settings[#{setting}][]",
54 "settings[#{setting}][]",
53 value,
55 value,
54 Setting.send(setting).include?(value)
56 Setting.send(setting).include?(value)
55 ) + text.to_s,
57 ) + text.to_s,
56 :class => 'block'
58 :class => 'block'
57 )
59 )
58 end.join.html_safe
60 end.join.html_safe
59 end
61 end
60
62
61 def setting_text_field(setting, options={})
63 def setting_text_field(setting, options={})
62 setting_label(setting, options).html_safe +
64 setting_label(setting, options).html_safe +
63 text_field_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
65 text_field_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
64 end
66 end
65
67
66 def setting_text_area(setting, options={})
68 def setting_text_area(setting, options={})
67 setting_label(setting, options).html_safe +
69 setting_label(setting, options).html_safe +
68 text_area_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
70 text_area_tag("settings[#{setting}]", Setting.send(setting), options).html_safe
69 end
71 end
70
72
71 def setting_check_box(setting, options={})
73 def setting_check_box(setting, options={})
72 setting_label(setting, options).html_safe +
74 setting_label(setting, options).html_safe +
73 hidden_field_tag("settings[#{setting}]", 0).html_safe +
75 hidden_field_tag("settings[#{setting}]", 0).html_safe +
74 check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
76 check_box_tag("settings[#{setting}]", 1, Setting.send("#{setting}?"), options).html_safe
75 end
77 end
76
78
77 def setting_label(setting, options={})
79 def setting_label(setting, options={})
78 label = options.delete(:label)
80 label = options.delete(:label)
79 label != false ? content_tag("label", l(label || "setting_#{setting}")).html_safe : ''
81 label != false ? content_tag("label", l(label || "setting_#{setting}")).html_safe : ''
80 end
82 end
81
83
82 # Renders a notification field for a Redmine::Notifiable option
84 # Renders a notification field for a Redmine::Notifiable option
83 def notification_field(notifiable)
85 def notification_field(notifiable)
84 return content_tag(:label,
86 return content_tag(:label,
85 check_box_tag('settings[notified_events][]',
87 check_box_tag('settings[notified_events][]',
86 notifiable.name,
88 notifiable.name,
87 Setting.notified_events.include?(notifiable.name)).html_safe +
89 Setting.notified_events.include?(notifiable.name)).html_safe +
88 l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
90 l_or_humanize(notifiable.name, :prefix => 'label_').html_safe,
89 :class => notifiable.parent.present? ? "parent" : '').html_safe
91 :class => notifiable.parent.present? ? "parent" : '').html_safe
90 end
92 end
91 end
93 end
@@ -1,231 +1,233
1 # encoding: utf-8
2 #
1 # Helpers to sort tables using clickable column headers.
3 # Helpers to sort tables using clickable column headers.
2 #
4 #
3 # Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
5 # Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
4 # Jean-Philippe Lang, 2009
6 # Jean-Philippe Lang, 2009
5 # License: This source code is released under the MIT license.
7 # License: This source code is released under the MIT license.
6 #
8 #
7 # - Consecutive clicks toggle the column's sort order.
9 # - Consecutive clicks toggle the column's sort order.
8 # - Sort state is maintained by a session hash entry.
10 # - Sort state is maintained by a session hash entry.
9 # - CSS classes identify sort column and state.
11 # - CSS classes identify sort column and state.
10 # - Typically used in conjunction with the Pagination module.
12 # - Typically used in conjunction with the Pagination module.
11 #
13 #
12 # Example code snippets:
14 # Example code snippets:
13 #
15 #
14 # Controller:
16 # Controller:
15 #
17 #
16 # helper :sort
18 # helper :sort
17 # include SortHelper
19 # include SortHelper
18 #
20 #
19 # def list
21 # def list
20 # sort_init 'last_name'
22 # sort_init 'last_name'
21 # sort_update %w(first_name last_name)
23 # sort_update %w(first_name last_name)
22 # @items = Contact.find_all nil, sort_clause
24 # @items = Contact.find_all nil, sort_clause
23 # end
25 # end
24 #
26 #
25 # Controller (using Pagination module):
27 # Controller (using Pagination module):
26 #
28 #
27 # helper :sort
29 # helper :sort
28 # include SortHelper
30 # include SortHelper
29 #
31 #
30 # def list
32 # def list
31 # sort_init 'last_name'
33 # sort_init 'last_name'
32 # sort_update %w(first_name last_name)
34 # sort_update %w(first_name last_name)
33 # @contact_pages, @items = paginate :contacts,
35 # @contact_pages, @items = paginate :contacts,
34 # :order_by => sort_clause,
36 # :order_by => sort_clause,
35 # :per_page => 10
37 # :per_page => 10
36 # end
38 # end
37 #
39 #
38 # View (table header in list.rhtml):
40 # View (table header in list.rhtml):
39 #
41 #
40 # <thead>
42 # <thead>
41 # <tr>
43 # <tr>
42 # <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
44 # <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
43 # <%= sort_header_tag('last_name', :caption => 'Name') %>
45 # <%= sort_header_tag('last_name', :caption => 'Name') %>
44 # <%= sort_header_tag('phone') %>
46 # <%= sort_header_tag('phone') %>
45 # <%= sort_header_tag('address', :width => 200) %>
47 # <%= sort_header_tag('address', :width => 200) %>
46 # </tr>
48 # </tr>
47 # </thead>
49 # </thead>
48 #
50 #
49 # - Introduces instance variables: @sort_default, @sort_criteria
51 # - Introduces instance variables: @sort_default, @sort_criteria
50 # - Introduces param :sort
52 # - Introduces param :sort
51 #
53 #
52
54
53 module SortHelper
55 module SortHelper
54 class SortCriteria
56 class SortCriteria
55
57
56 def initialize
58 def initialize
57 @criteria = []
59 @criteria = []
58 end
60 end
59
61
60 def available_criteria=(criteria)
62 def available_criteria=(criteria)
61 unless criteria.is_a?(Hash)
63 unless criteria.is_a?(Hash)
62 criteria = criteria.inject({}) {|h,k| h[k] = k; h}
64 criteria = criteria.inject({}) {|h,k| h[k] = k; h}
63 end
65 end
64 @available_criteria = criteria
66 @available_criteria = criteria
65 end
67 end
66
68
67 def from_param(param)
69 def from_param(param)
68 @criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
70 @criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
69 normalize!
71 normalize!
70 end
72 end
71
73
72 def criteria=(arg)
74 def criteria=(arg)
73 @criteria = arg
75 @criteria = arg
74 normalize!
76 normalize!
75 end
77 end
76
78
77 def to_param
79 def to_param
78 @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
80 @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
79 end
81 end
80
82
81 def to_sql
83 def to_sql
82 sql = @criteria.collect do |k,o|
84 sql = @criteria.collect do |k,o|
83 if s = @available_criteria[k]
85 if s = @available_criteria[k]
84 (o ? s.to_a : s.to_a.collect {|c| append_desc(c)}).join(', ')
86 (o ? s.to_a : s.to_a.collect {|c| append_desc(c)}).join(', ')
85 end
87 end
86 end.compact.join(', ')
88 end.compact.join(', ')
87 sql.blank? ? nil : sql
89 sql.blank? ? nil : sql
88 end
90 end
89
91
90 def add!(key, asc)
92 def add!(key, asc)
91 @criteria.delete_if {|k,o| k == key}
93 @criteria.delete_if {|k,o| k == key}
92 @criteria = [[key, asc]] + @criteria
94 @criteria = [[key, asc]] + @criteria
93 normalize!
95 normalize!
94 end
96 end
95
97
96 def add(*args)
98 def add(*args)
97 r = self.class.new.from_param(to_param)
99 r = self.class.new.from_param(to_param)
98 r.add!(*args)
100 r.add!(*args)
99 r
101 r
100 end
102 end
101
103
102 def first_key
104 def first_key
103 @criteria.first && @criteria.first.first
105 @criteria.first && @criteria.first.first
104 end
106 end
105
107
106 def first_asc?
108 def first_asc?
107 @criteria.first && @criteria.first.last
109 @criteria.first && @criteria.first.last
108 end
110 end
109
111
110 def empty?
112 def empty?
111 @criteria.empty?
113 @criteria.empty?
112 end
114 end
113
115
114 private
116 private
115
117
116 def normalize!
118 def normalize!
117 @criteria ||= []
119 @criteria ||= []
118 @criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
120 @criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
119 @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
121 @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
120 @criteria.slice!(3)
122 @criteria.slice!(3)
121 self
123 self
122 end
124 end
123
125
124 # Appends DESC to the sort criterion unless it has a fixed order
126 # Appends DESC to the sort criterion unless it has a fixed order
125 def append_desc(criterion)
127 def append_desc(criterion)
126 if criterion =~ / (asc|desc)$/i
128 if criterion =~ / (asc|desc)$/i
127 criterion
129 criterion
128 else
130 else
129 "#{criterion} DESC"
131 "#{criterion} DESC"
130 end
132 end
131 end
133 end
132 end
134 end
133
135
134 def sort_name
136 def sort_name
135 controller_name + '_' + action_name + '_sort'
137 controller_name + '_' + action_name + '_sort'
136 end
138 end
137
139
138 # Initializes the default sort.
140 # Initializes the default sort.
139 # Examples:
141 # Examples:
140 #
142 #
141 # sort_init 'name'
143 # sort_init 'name'
142 # sort_init 'id', 'desc'
144 # sort_init 'id', 'desc'
143 # sort_init ['name', ['id', 'desc']]
145 # sort_init ['name', ['id', 'desc']]
144 # sort_init [['name', 'desc'], ['id', 'desc']]
146 # sort_init [['name', 'desc'], ['id', 'desc']]
145 #
147 #
146 def sort_init(*args)
148 def sort_init(*args)
147 case args.size
149 case args.size
148 when 1
150 when 1
149 @sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
151 @sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
150 when 2
152 when 2
151 @sort_default = [[args.first, args.last]]
153 @sort_default = [[args.first, args.last]]
152 else
154 else
153 raise ArgumentError
155 raise ArgumentError
154 end
156 end
155 end
157 end
156
158
157 # Updates the sort state. Call this in the controller prior to calling
159 # Updates the sort state. Call this in the controller prior to calling
158 # sort_clause.
160 # sort_clause.
159 # - criteria can be either an array or a hash of allowed keys
161 # - criteria can be either an array or a hash of allowed keys
160 #
162 #
161 def sort_update(criteria)
163 def sort_update(criteria)
162 @sort_criteria = SortCriteria.new
164 @sort_criteria = SortCriteria.new
163 @sort_criteria.available_criteria = criteria
165 @sort_criteria.available_criteria = criteria
164 @sort_criteria.from_param(params[:sort] || session[sort_name])
166 @sort_criteria.from_param(params[:sort] || session[sort_name])
165 @sort_criteria.criteria = @sort_default if @sort_criteria.empty?
167 @sort_criteria.criteria = @sort_default if @sort_criteria.empty?
166 session[sort_name] = @sort_criteria.to_param
168 session[sort_name] = @sort_criteria.to_param
167 end
169 end
168
170
169 # Clears the sort criteria session data
171 # Clears the sort criteria session data
170 #
172 #
171 def sort_clear
173 def sort_clear
172 session[sort_name] = nil
174 session[sort_name] = nil
173 end
175 end
174
176
175 # Returns an SQL sort clause corresponding to the current sort state.
177 # Returns an SQL sort clause corresponding to the current sort state.
176 # Use this to sort the controller's table items collection.
178 # Use this to sort the controller's table items collection.
177 #
179 #
178 def sort_clause()
180 def sort_clause()
179 @sort_criteria.to_sql
181 @sort_criteria.to_sql
180 end
182 end
181
183
182 # Returns a link which sorts by the named column.
184 # Returns a link which sorts by the named column.
183 #
185 #
184 # - column is the name of an attribute in the sorted record collection.
186 # - column is the name of an attribute in the sorted record collection.
185 # - the optional caption explicitly specifies the displayed link text.
187 # - the optional caption explicitly specifies the displayed link text.
186 # - 2 CSS classes reflect the state of the link: sort and asc or desc
188 # - 2 CSS classes reflect the state of the link: sort and asc or desc
187 #
189 #
188 def sort_link(column, caption, default_order)
190 def sort_link(column, caption, default_order)
189 css, order = nil, default_order
191 css, order = nil, default_order
190
192
191 if column.to_s == @sort_criteria.first_key
193 if column.to_s == @sort_criteria.first_key
192 if @sort_criteria.first_asc?
194 if @sort_criteria.first_asc?
193 css = 'sort asc'
195 css = 'sort asc'
194 order = 'desc'
196 order = 'desc'
195 else
197 else
196 css = 'sort desc'
198 css = 'sort desc'
197 order = 'asc'
199 order = 'asc'
198 end
200 end
199 end
201 end
200 caption = column.to_s.humanize unless caption
202 caption = column.to_s.humanize unless caption
201
203
202 sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
204 sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
203 url_options = params.merge(sort_options)
205 url_options = params.merge(sort_options)
204
206
205 # Add project_id to url_options
207 # Add project_id to url_options
206 url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
208 url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
207
209
208 link_to_content_update(h(caption), url_options, :class => css)
210 link_to_content_update(h(caption), url_options, :class => css)
209 end
211 end
210
212
211 # Returns a table header <th> tag with a sort link for the named column
213 # Returns a table header <th> tag with a sort link for the named column
212 # attribute.
214 # attribute.
213 #
215 #
214 # Options:
216 # Options:
215 # :caption The displayed link name (defaults to titleized column name).
217 # :caption The displayed link name (defaults to titleized column name).
216 # :title The tag's 'title' attribute (defaults to 'Sort by :caption').
218 # :title The tag's 'title' attribute (defaults to 'Sort by :caption').
217 #
219 #
218 # Other options hash entries generate additional table header tag attributes.
220 # Other options hash entries generate additional table header tag attributes.
219 #
221 #
220 # Example:
222 # Example:
221 #
223 #
222 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
224 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
223 #
225 #
224 def sort_header_tag(column, options = {})
226 def sort_header_tag(column, options = {})
225 caption = options.delete(:caption) || column.to_s.humanize
227 caption = options.delete(:caption) || column.to_s.humanize
226 default_order = options.delete(:default_order) || 'asc'
228 default_order = options.delete(:default_order) || 'asc'
227 options[:title] = l(:label_sort_by, "\"#{caption}\"") unless options[:title]
229 options[:title] = l(:label_sort_by, "\"#{caption}\"") unless options[:title]
228 content_tag('th', sort_link(column, caption, default_order), options)
230 content_tag('th', sort_link(column, caption, default_order), options)
229 end
231 end
230 end
232 end
231
233
@@ -1,194 +1,196
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module TimelogHelper
20 module TimelogHelper
19 include ApplicationHelper
21 include ApplicationHelper
20
22
21 def render_timelog_breadcrumb
23 def render_timelog_breadcrumb
22 links = []
24 links = []
23 links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
25 links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
24 links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
26 links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
25 if @issue
27 if @issue
26 if @issue.visible?
28 if @issue.visible?
27 links << link_to_issue(@issue, :subject => false)
29 links << link_to_issue(@issue, :subject => false)
28 else
30 else
29 links << "##{@issue.id}"
31 links << "##{@issue.id}"
30 end
32 end
31 end
33 end
32 breadcrumb links
34 breadcrumb links
33 end
35 end
34
36
35 # Returns a collection of activities for a select field. time_entry
37 # Returns a collection of activities for a select field. time_entry
36 # is optional and will be used to check if the selected TimeEntryActivity
38 # is optional and will be used to check if the selected TimeEntryActivity
37 # is active.
39 # is active.
38 def activity_collection_for_select_options(time_entry=nil, project=nil)
40 def activity_collection_for_select_options(time_entry=nil, project=nil)
39 project ||= @project
41 project ||= @project
40 if project.nil?
42 if project.nil?
41 activities = TimeEntryActivity.shared.active
43 activities = TimeEntryActivity.shared.active
42 else
44 else
43 activities = project.activities
45 activities = project.activities
44 end
46 end
45
47
46 collection = []
48 collection = []
47 if time_entry && time_entry.activity && !time_entry.activity.active?
49 if time_entry && time_entry.activity && !time_entry.activity.active?
48 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ]
50 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ]
49 else
51 else
50 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default)
52 collection << [ "--- #{l(:actionview_instancetag_blank_option)} ---", '' ] unless activities.detect(&:is_default)
51 end
53 end
52 activities.each { |a| collection << [a.name, a.id] }
54 activities.each { |a| collection << [a.name, a.id] }
53 collection
55 collection
54 end
56 end
55
57
56 def select_hours(data, criteria, value)
58 def select_hours(data, criteria, value)
57 if value.to_s.empty?
59 if value.to_s.empty?
58 data.select {|row| row[criteria].blank? }
60 data.select {|row| row[criteria].blank? }
59 else
61 else
60 data.select {|row| row[criteria].to_s == value.to_s}
62 data.select {|row| row[criteria].to_s == value.to_s}
61 end
63 end
62 end
64 end
63
65
64 def sum_hours(data)
66 def sum_hours(data)
65 sum = 0
67 sum = 0
66 data.each do |row|
68 data.each do |row|
67 sum += row['hours'].to_f
69 sum += row['hours'].to_f
68 end
70 end
69 sum
71 sum
70 end
72 end
71
73
72 def options_for_period_select(value)
74 def options_for_period_select(value)
73 options_for_select([[l(:label_all_time), 'all'],
75 options_for_select([[l(:label_all_time), 'all'],
74 [l(:label_today), 'today'],
76 [l(:label_today), 'today'],
75 [l(:label_yesterday), 'yesterday'],
77 [l(:label_yesterday), 'yesterday'],
76 [l(:label_this_week), 'current_week'],
78 [l(:label_this_week), 'current_week'],
77 [l(:label_last_week), 'last_week'],
79 [l(:label_last_week), 'last_week'],
78 [l(:label_last_n_days, 7), '7_days'],
80 [l(:label_last_n_days, 7), '7_days'],
79 [l(:label_this_month), 'current_month'],
81 [l(:label_this_month), 'current_month'],
80 [l(:label_last_month), 'last_month'],
82 [l(:label_last_month), 'last_month'],
81 [l(:label_last_n_days, 30), '30_days'],
83 [l(:label_last_n_days, 30), '30_days'],
82 [l(:label_this_year), 'current_year']],
84 [l(:label_this_year), 'current_year']],
83 value)
85 value)
84 end
86 end
85
87
86 def entries_to_csv(entries)
88 def entries_to_csv(entries)
87 decimal_separator = l(:general_csv_decimal_separator)
89 decimal_separator = l(:general_csv_decimal_separator)
88 custom_fields = TimeEntryCustomField.find(:all)
90 custom_fields = TimeEntryCustomField.find(:all)
89 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
91 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
90 # csv header fields
92 # csv header fields
91 headers = [l(:field_spent_on),
93 headers = [l(:field_spent_on),
92 l(:field_user),
94 l(:field_user),
93 l(:field_activity),
95 l(:field_activity),
94 l(:field_project),
96 l(:field_project),
95 l(:field_issue),
97 l(:field_issue),
96 l(:field_tracker),
98 l(:field_tracker),
97 l(:field_subject),
99 l(:field_subject),
98 l(:field_hours),
100 l(:field_hours),
99 l(:field_comments)
101 l(:field_comments)
100 ]
102 ]
101 # Export custom fields
103 # Export custom fields
102 headers += custom_fields.collect(&:name)
104 headers += custom_fields.collect(&:name)
103
105
104 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
106 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
105 c.to_s,
107 c.to_s,
106 l(:general_csv_encoding) ) }
108 l(:general_csv_encoding) ) }
107 # csv lines
109 # csv lines
108 entries.each do |entry|
110 entries.each do |entry|
109 fields = [format_date(entry.spent_on),
111 fields = [format_date(entry.spent_on),
110 entry.user,
112 entry.user,
111 entry.activity,
113 entry.activity,
112 entry.project,
114 entry.project,
113 (entry.issue ? entry.issue.id : nil),
115 (entry.issue ? entry.issue.id : nil),
114 (entry.issue ? entry.issue.tracker : nil),
116 (entry.issue ? entry.issue.tracker : nil),
115 (entry.issue ? entry.issue.subject : nil),
117 (entry.issue ? entry.issue.subject : nil),
116 entry.hours.to_s.gsub('.', decimal_separator),
118 entry.hours.to_s.gsub('.', decimal_separator),
117 entry.comments
119 entry.comments
118 ]
120 ]
119 fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
121 fields += custom_fields.collect {|f| show_value(entry.custom_value_for(f)) }
120
122
121 csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
123 csv << fields.collect {|c| Redmine::CodesetUtil.from_utf8(
122 c.to_s,
124 c.to_s,
123 l(:general_csv_encoding) ) }
125 l(:general_csv_encoding) ) }
124 end
126 end
125 end
127 end
126 export
128 export
127 end
129 end
128
130
129 def format_criteria_value(criteria, value)
131 def format_criteria_value(criteria, value)
130 if value.blank?
132 if value.blank?
131 l(:label_none)
133 l(:label_none)
132 elsif k = @available_criterias[criteria][:klass]
134 elsif k = @available_criterias[criteria][:klass]
133 obj = k.find_by_id(value.to_i)
135 obj = k.find_by_id(value.to_i)
134 if obj.is_a?(Issue)
136 if obj.is_a?(Issue)
135 obj.visible? ? "#{obj.tracker} ##{obj.id}: #{obj.subject}" : "##{obj.id}"
137 obj.visible? ? "#{obj.tracker} ##{obj.id}: #{obj.subject}" : "##{obj.id}"
136 else
138 else
137 obj
139 obj
138 end
140 end
139 else
141 else
140 format_value(value, @available_criterias[criteria][:format])
142 format_value(value, @available_criterias[criteria][:format])
141 end
143 end
142 end
144 end
143
145
144 def report_to_csv(criterias, periods, hours)
146 def report_to_csv(criterias, periods, hours)
145 decimal_separator = l(:general_csv_decimal_separator)
147 decimal_separator = l(:general_csv_decimal_separator)
146 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
148 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
147 # Column headers
149 # Column headers
148 headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
150 headers = criterias.collect {|criteria| l(@available_criterias[criteria][:label]) }
149 headers += periods
151 headers += periods
150 headers << l(:label_total)
152 headers << l(:label_total)
151 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
153 csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(
152 c.to_s,
154 c.to_s,
153 l(:general_csv_encoding) ) }
155 l(:general_csv_encoding) ) }
154 # Content
156 # Content
155 report_criteria_to_csv(csv, criterias, periods, hours)
157 report_criteria_to_csv(csv, criterias, periods, hours)
156 # Total row
158 # Total row
157 str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
159 str_total = Redmine::CodesetUtil.from_utf8(l(:label_total), l(:general_csv_encoding))
158 row = [ str_total ] + [''] * (criterias.size - 1)
160 row = [ str_total ] + [''] * (criterias.size - 1)
159 total = 0
161 total = 0
160 periods.each do |period|
162 periods.each do |period|
161 sum = sum_hours(select_hours(hours, @columns, period.to_s))
163 sum = sum_hours(select_hours(hours, @columns, period.to_s))
162 total += sum
164 total += sum
163 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
165 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
164 end
166 end
165 row << ("%.2f" % total).gsub('.',decimal_separator)
167 row << ("%.2f" % total).gsub('.',decimal_separator)
166 csv << row
168 csv << row
167 end
169 end
168 export
170 export
169 end
171 end
170
172
171 def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
173 def report_criteria_to_csv(csv, criterias, periods, hours, level=0)
172 decimal_separator = l(:general_csv_decimal_separator)
174 decimal_separator = l(:general_csv_decimal_separator)
173 hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
175 hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value|
174 hours_for_value = select_hours(hours, criterias[level], value)
176 hours_for_value = select_hours(hours, criterias[level], value)
175 next if hours_for_value.empty?
177 next if hours_for_value.empty?
176 row = [''] * level
178 row = [''] * level
177 row << Redmine::CodesetUtil.from_utf8(
179 row << Redmine::CodesetUtil.from_utf8(
178 format_criteria_value(criterias[level], value).to_s,
180 format_criteria_value(criterias[level], value).to_s,
179 l(:general_csv_encoding) )
181 l(:general_csv_encoding) )
180 row += [''] * (criterias.length - level - 1)
182 row += [''] * (criterias.length - level - 1)
181 total = 0
183 total = 0
182 periods.each do |period|
184 periods.each do |period|
183 sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
185 sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s))
184 total += sum
186 total += sum
185 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
187 row << (sum > 0 ? ("%.2f" % sum).gsub('.',decimal_separator) : '')
186 end
188 end
187 row << ("%.2f" % total).gsub('.',decimal_separator)
189 row << ("%.2f" % total).gsub('.',decimal_separator)
188 csv << row
190 csv << row
189 if criterias.length > level + 1
191 if criterias.length > level + 1
190 report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
192 report_criteria_to_csv(csv, criterias, periods, hours_for_value, level + 1)
191 end
193 end
192 end
194 end
193 end
195 end
194 end
196 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module TrackersHelper
20 module TrackersHelper
19 end
21 end
@@ -1,61 +1,63
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module UsersHelper
20 module UsersHelper
19 def users_status_options_for_select(selected)
21 def users_status_options_for_select(selected)
20 user_count_by_status = User.count(:group => 'status').to_hash
22 user_count_by_status = User.count(:group => 'status').to_hash
21 options_for_select([[l(:label_all), ''],
23 options_for_select([[l(:label_all), ''],
22 ["#{l(:status_active)} (#{user_count_by_status[1].to_i})", 1],
24 ["#{l(:status_active)} (#{user_count_by_status[1].to_i})", 1],
23 ["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", 2],
25 ["#{l(:status_registered)} (#{user_count_by_status[2].to_i})", 2],
24 ["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", 3]], selected)
26 ["#{l(:status_locked)} (#{user_count_by_status[3].to_i})", 3]], selected)
25 end
27 end
26
28
27 # Options for the new membership projects combo-box
29 # Options for the new membership projects combo-box
28 def options_for_membership_project_select(user, projects)
30 def options_for_membership_project_select(user, projects)
29 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
31 options = content_tag('option', "--- #{l(:actionview_instancetag_blank_option)} ---")
30 options << project_tree_options_for_select(projects) do |p|
32 options << project_tree_options_for_select(projects) do |p|
31 {:disabled => (user.projects.include?(p))}
33 {:disabled => (user.projects.include?(p))}
32 end
34 end
33 options
35 options
34 end
36 end
35
37
36 def user_mail_notification_options(user)
38 def user_mail_notification_options(user)
37 user.valid_notification_options.collect {|o| [l(o.last), o.first]}
39 user.valid_notification_options.collect {|o| [l(o.last), o.first]}
38 end
40 end
39
41
40 def change_status_link(user)
42 def change_status_link(user)
41 url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
43 url = {:controller => 'users', :action => 'update', :id => user, :page => params[:page], :status => params[:status], :tab => nil}
42
44
43 if user.locked?
45 if user.locked?
44 link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
46 link_to l(:button_unlock), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
45 elsif user.registered?
47 elsif user.registered?
46 link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
48 link_to l(:button_activate), url.merge(:user => {:status => User::STATUS_ACTIVE}), :method => :put, :class => 'icon icon-unlock'
47 elsif user != User.current
49 elsif user != User.current
48 link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
50 link_to l(:button_lock), url.merge(:user => {:status => User::STATUS_LOCKED}), :method => :put, :class => 'icon icon-lock'
49 end
51 end
50 end
52 end
51
53
52 def user_settings_tabs
54 def user_settings_tabs
53 tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
55 tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general},
54 {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}
56 {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural}
55 ]
57 ]
56 if Group.all.any?
58 if Group.all.any?
57 tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
59 tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
58 end
60 end
59 tabs
61 tabs
60 end
62 end
61 end
63 end
@@ -1,46 +1,48
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module VersionsHelper
20 module VersionsHelper
19
21
20 STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
22 STATUS_BY_CRITERIAS = %w(category tracker status priority author assigned_to)
21
23
22 def render_issue_status_by(version, criteria)
24 def render_issue_status_by(version, criteria)
23 criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
25 criteria = 'category' unless STATUS_BY_CRITERIAS.include?(criteria)
24
26
25 h = Hash.new {|k,v| k[v] = [0, 0]}
27 h = Hash.new {|k,v| k[v] = [0, 0]}
26 begin
28 begin
27 # Total issue count
29 # Total issue count
28 Issue.count(:group => criteria,
30 Issue.count(:group => criteria,
29 :conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s}
31 :conditions => ["#{Issue.table_name}.fixed_version_id = ?", version.id]).each {|c,s| h[c][0] = s}
30 # Open issues count
32 # Open issues count
31 Issue.count(:group => criteria,
33 Issue.count(:group => criteria,
32 :include => :status,
34 :include => :status,
33 :conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s}
35 :conditions => ["#{Issue.table_name}.fixed_version_id = ? AND #{IssueStatus.table_name}.is_closed = ?", version.id, false]).each {|c,s| h[c][1] = s}
34 rescue ActiveRecord::RecordNotFound
36 rescue ActiveRecord::RecordNotFound
35 # When grouping by an association, Rails throws this exception if there's no result (bug)
37 # When grouping by an association, Rails throws this exception if there's no result (bug)
36 end
38 end
37 counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
39 counts = h.keys.compact.sort.collect {|k| {:group => k, :total => h[k][0], :open => h[k][1], :closed => (h[k][0] - h[k][1])}}
38 max = counts.collect {|c| c[:total]}.max
40 max = counts.collect {|c| c[:total]}.max
39
41
40 render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
42 render :partial => 'issue_counts', :locals => {:version => version, :criteria => criteria, :counts => counts, :max => max}
41 end
43 end
42
44
43 def status_by_options_for_select(value)
45 def status_by_options_for_select(value)
44 options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
46 options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
45 end
47 end
46 end
48 end
@@ -1,64 +1,66
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module WatchersHelper
20 module WatchersHelper
19
21
20 def watcher_tag(object, user, options={})
22 def watcher_tag(object, user, options={})
21 content_tag("span", watcher_link(object, user), :class => watcher_css(object))
23 content_tag("span", watcher_link(object, user), :class => watcher_css(object))
22 end
24 end
23
25
24 def watcher_link(object, user)
26 def watcher_link(object, user)
25 return '' unless user && user.logged? && object.respond_to?('watched_by?')
27 return '' unless user && user.logged? && object.respond_to?('watched_by?')
26 watched = object.watched_by?(user)
28 watched = object.watched_by?(user)
27 url = {:controller => 'watchers',
29 url = {:controller => 'watchers',
28 :action => (watched ? 'unwatch' : 'watch'),
30 :action => (watched ? 'unwatch' : 'watch'),
29 :object_type => object.class.to_s.underscore,
31 :object_type => object.class.to_s.underscore,
30 :object_id => object.id}
32 :object_id => object.id}
31 link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
33 link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
32 {:url => url},
34 {:url => url},
33 :href => url_for(url),
35 :href => url_for(url),
34 :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
36 :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
35
37
36 end
38 end
37
39
38 # Returns the css class used to identify watch links for a given +object+
40 # Returns the css class used to identify watch links for a given +object+
39 def watcher_css(object)
41 def watcher_css(object)
40 "#{object.class.to_s.underscore}-#{object.id}-watcher"
42 "#{object.class.to_s.underscore}-#{object.id}-watcher"
41 end
43 end
42
44
43 # Returns a comma separated list of users watching the given object
45 # Returns a comma separated list of users watching the given object
44 def watchers_list(object)
46 def watchers_list(object)
45 remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
47 remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
46 lis = object.watcher_users.collect do |user|
48 lis = object.watcher_users.collect do |user|
47 s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
49 s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
48 if remove_allowed
50 if remove_allowed
49 url = {:controller => 'watchers',
51 url = {:controller => 'watchers',
50 :action => 'destroy',
52 :action => 'destroy',
51 :object_type => object.class.to_s.underscore,
53 :object_type => object.class.to_s.underscore,
52 :object_id => object.id,
54 :object_id => object.id,
53 :user_id => user}
55 :user_id => user}
54 s += ' ' + link_to_remote(image_tag('delete.png'),
56 s += ' ' + link_to_remote(image_tag('delete.png'),
55 {:url => url},
57 {:url => url},
56 :href => url_for(url),
58 :href => url_for(url),
57 :style => "vertical-align: middle",
59 :style => "vertical-align: middle",
58 :class => "delete")
60 :class => "delete")
59 end
61 end
60 "<li>#{ s }</li>"
62 "<li>#{ s }</li>"
61 end
63 end
62 lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>"
64 lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>"
63 end
65 end
64 end
66 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module WelcomeHelper
20 module WelcomeHelper
19 end
21 end
@@ -1,41 +1,43
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module WikiHelper
20 module WikiHelper
19
21
20 def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
22 def wiki_page_options_for_select(pages, selected = nil, parent = nil, level = 0)
21 pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
23 pages = pages.group_by(&:parent) unless pages.is_a?(Hash)
22 s = ''
24 s = ''
23 if pages.has_key?(parent)
25 if pages.has_key?(parent)
24 pages[parent].each do |page|
26 pages[parent].each do |page|
25 attrs = "value='#{page.id}'"
27 attrs = "value='#{page.id}'"
26 attrs << " selected='selected'" if selected == page
28 attrs << " selected='selected'" if selected == page
27 indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
29 indent = (level > 0) ? ('&nbsp;' * level * 2 + '&#187; ') : nil
28
30
29 s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
31 s << "<option #{attrs}>#{indent}#{h page.pretty_title}</option>\n" +
30 wiki_page_options_for_select(pages, selected, page, level + 1)
32 wiki_page_options_for_select(pages, selected, page, level + 1)
31 end
33 end
32 end
34 end
33 s
35 s
34 end
36 end
35
37
36 def wiki_page_breadcrumb(page)
38 def wiki_page_breadcrumb(page)
37 breadcrumb(page.ancestors.reverse.collect {|parent|
39 breadcrumb(page.ancestors.reverse.collect {|parent|
38 link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project})
40 link_to(h(parent.pretty_title), {:controller => 'wiki', :action => 'show', :id => parent.title, :project_id => parent.project})
39 })
41 })
40 end
42 end
41 end
43 end
@@ -1,19 +1,21
1 # encoding: utf-8
2 #
1 # Redmine - project management software
3 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
5 #
4 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
8 #
10 #
9 # 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,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # GNU General Public License for more details.
13 #
15 #
14 # 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
15 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
16 # 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.
17
19
18 module WorkflowsHelper
20 module WorkflowsHelper
19 end
21 end
General Comments 0
You need to be logged in to leave comments. Login now