@@ -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,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) ? (' ' * level * 2 + '» ') : 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