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