@@ -0,0 +1,39 | |||||
|
1 | # redMine - project management software | |||
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |||
|
3 | # | |||
|
4 | # This program is free software; you can redistribute it and/or | |||
|
5 | # modify it under the terms of the GNU General Public License | |||
|
6 | # as published by the Free Software Foundation; either version 2 | |||
|
7 | # of the License, or (at your option) any later version. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU General Public License | |||
|
15 | # along with this program; if not, write to the Free Software | |||
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
|
17 | ||||
|
18 | class AttachmentsController < ApplicationController | |||
|
19 | before_filter :find_project, :check_project_privacy | |||
|
20 | ||||
|
21 | # sends an attachment | |||
|
22 | def download | |||
|
23 | send_file @attachment.diskfile, :filename => @attachment.filename | |||
|
24 | end | |||
|
25 | ||||
|
26 | # sends an image to be displayed inline | |||
|
27 | def show | |||
|
28 | render(:nothing => true, :status => 404) and return unless @attachment.diskfile =~ /\.(jpeg|jpg|gif|png)$/i | |||
|
29 | send_file @attachment.diskfile, :type => "image/#{$1}", :disposition => 'inline' | |||
|
30 | end | |||
|
31 | ||||
|
32 | private | |||
|
33 | def find_project | |||
|
34 | @attachment = Attachment.find(params[:id]) | |||
|
35 | @project = @attachment.project | |||
|
36 | rescue | |||
|
37 | render_404 | |||
|
38 | end | |||
|
39 | end |
@@ -0,0 +1,25 | |||||
|
1 | # redMine - project management software | |||
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |||
|
3 | # | |||
|
4 | # This program is free software; you can redistribute it and/or | |||
|
5 | # modify it under the terms of the GNU General Public License | |||
|
6 | # as published by the Free Software Foundation; either version 2 | |||
|
7 | # of the License, or (at your option) any later version. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU General Public License | |||
|
15 | # along with this program; if not, write to the Free Software | |||
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
|
17 | ||||
|
18 | module AttachmentsHelper | |||
|
19 | # displays the links to a collection of attachments | |||
|
20 | def link_to_attachments(attachments, options = {}) | |||
|
21 | if attachments.any? | |||
|
22 | render :partial => 'attachments/links', :locals => {:attachments => attachments, :options => options} | |||
|
23 | end | |||
|
24 | end | |||
|
25 | end |
@@ -0,0 +1,4 | |||||
|
1 | <p id="attachments_p"><label for="attachment_file"><%=l(:label_attachment)%> | |||
|
2 | <%= image_to_function "add.png", "addFileField();return false" %></label> | |||
|
3 | ||||
|
4 | <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p> |
@@ -0,0 +1,13 | |||||
|
1 | <div class="attachments"> | |||
|
2 | <% for attachment in attachments %> | |||
|
3 | <p><%= link_to attachment.filename, {:controller => 'attachments', :action => 'download', :id => attachment }, :class => 'icon icon-attachment' %> | |||
|
4 | (<%= number_to_human_size attachment.filesize %>) | |||
|
5 | <% unless options[:no_author] %> | |||
|
6 | <em><%= attachment.author.display_name %>, <%= format_date(attachment.created_on) %></em> | |||
|
7 | <% end %> | |||
|
8 | <% if options[:delete_url] %> | |||
|
9 | <%= link_to image_tag('delete.png'), options[:delete_url].update({:attachment_id => attachment}), :confirm => l(:text_are_you_sure), :method => :post %> | |||
|
10 | <% end %> | |||
|
11 | </p> | |||
|
12 | <% end %> | |||
|
13 | </div> |
@@ -0,0 +1,11 | |||||
|
1 | class AddWikiAttachmentsPermissions < ActiveRecord::Migration | |||
|
2 | def self.up | |||
|
3 | Permission.create :controller => 'wiki', :action => 'add_attachment', :description => 'label_attachment_new', :sort => 1750, :is_public => false, :mail_option => 0, :mail_enabled => 0 | |||
|
4 | Permission.create :controller => 'wiki', :action => 'destroy_attachment', :description => 'label_attachment_delete', :sort => 1755, :is_public => false, :mail_option => 0, :mail_enabled => 0 | |||
|
5 | end | |||
|
6 | ||||
|
7 | def self.down | |||
|
8 | Permission.find_by_controller_and_action('wiki', 'add_attachment').destroy | |||
|
9 | Permission.find_by_controller_and_action('wiki', 'destroy_attachment').destroy | |||
|
10 | end | |||
|
11 | end |
@@ -1,165 +1,159 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class IssuesController < ApplicationController |
|
18 | class IssuesController < ApplicationController | |
19 | layout 'base', :except => :export_pdf |
|
19 | layout 'base', :except => :export_pdf | |
20 | before_filter :find_project, :authorize |
|
20 | before_filter :find_project, :authorize | |
21 |
|
21 | |||
22 | cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ] |
|
22 | cache_sweeper :issue_sweeper, :only => [ :edit, :change_status, :destroy ] | |
23 |
|
23 | |||
24 | helper :custom_fields |
|
24 | helper :custom_fields | |
25 | include CustomFieldsHelper |
|
25 | include CustomFieldsHelper | |
26 | helper :ifpdf |
|
26 | helper :ifpdf | |
27 | include IfpdfHelper |
|
27 | include IfpdfHelper | |
28 | helper :issue_relations |
|
28 | helper :issue_relations | |
29 | include IssueRelationsHelper |
|
29 | include IssueRelationsHelper | |
30 | helper :watchers |
|
30 | helper :watchers | |
31 | include WatchersHelper |
|
31 | include WatchersHelper | |
|
32 | helper :attachments | |||
|
33 | include AttachmentsHelper | |||
32 |
|
34 | |||
33 | def show |
|
35 | def show | |
34 | @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user |
|
36 | @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user | |
35 | @custom_values = @issue.custom_values.find(:all, :include => :custom_field) |
|
37 | @custom_values = @issue.custom_values.find(:all, :include => :custom_field) | |
36 | @journals_count = @issue.journals.count |
|
38 | @journals_count = @issue.journals.count | |
37 | @journals = @issue.journals.find(:all, :include => [:user, :details], :limit => 15, :order => "#{Journal.table_name}.created_on desc") |
|
39 | @journals = @issue.journals.find(:all, :include => [:user, :details], :limit => 15, :order => "#{Journal.table_name}.created_on desc") | |
38 | end |
|
40 | end | |
39 |
|
41 | |||
40 | def history |
|
42 | def history | |
41 | @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on desc") |
|
43 | @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on desc") | |
42 | @journals_count = @journals.length |
|
44 | @journals_count = @journals.length | |
43 | end |
|
45 | end | |
44 |
|
46 | |||
45 | def export_pdf |
|
47 | def export_pdf | |
46 | @custom_values = @issue.custom_values.find(:all, :include => :custom_field) |
|
48 | @custom_values = @issue.custom_values.find(:all, :include => :custom_field) | |
47 | @options_for_rfpdf ||= {} |
|
49 | @options_for_rfpdf ||= {} | |
48 | @options_for_rfpdf[:file_name] = "#{@project.name}_#{@issue.id}.pdf" |
|
50 | @options_for_rfpdf[:file_name] = "#{@project.name}_#{@issue.id}.pdf" | |
49 | end |
|
51 | end | |
50 |
|
52 | |||
51 | def edit |
|
53 | def edit | |
52 | @priorities = Enumeration::get_values('IPRI') |
|
54 | @priorities = Enumeration::get_values('IPRI') | |
53 | if request.get? |
|
55 | if request.get? | |
54 | @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) } |
|
56 | @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| @issue.custom_values.find_by_custom_field_id(x.id) || CustomValue.new(:custom_field => x, :customized => @issue) } | |
55 | else |
|
57 | else | |
56 | begin |
|
58 | begin | |
57 | @issue.init_journal(self.logged_in_user) |
|
59 | @issue.init_journal(self.logged_in_user) | |
58 | # Retrieve custom fields and values |
|
60 | # Retrieve custom fields and values | |
59 | @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) } |
|
61 | @custom_values = @project.custom_fields_for_issues(@issue.tracker).collect { |x| CustomValue.new(:custom_field => x, :customized => @issue, :value => params["custom_fields"][x.id.to_s]) } | |
60 | @issue.custom_values = @custom_values |
|
62 | @issue.custom_values = @custom_values | |
61 | @issue.attributes = params[:issue] |
|
63 | @issue.attributes = params[:issue] | |
62 | if @issue.save |
|
64 | if @issue.save | |
63 | flash[:notice] = l(:notice_successful_update) |
|
65 | flash[:notice] = l(:notice_successful_update) | |
64 | redirect_to :action => 'show', :id => @issue |
|
66 | redirect_to :action => 'show', :id => @issue | |
65 | end |
|
67 | end | |
66 | rescue ActiveRecord::StaleObjectError |
|
68 | rescue ActiveRecord::StaleObjectError | |
67 | # Optimistic locking exception |
|
69 | # Optimistic locking exception | |
68 | flash[:notice] = l(:notice_locking_conflict) |
|
70 | flash[:notice] = l(:notice_locking_conflict) | |
69 | end |
|
71 | end | |
70 | end |
|
72 | end | |
71 | end |
|
73 | end | |
72 |
|
74 | |||
73 | def add_note |
|
75 | def add_note | |
74 | unless params[:notes].empty? |
|
76 | unless params[:notes].empty? | |
75 | journal = @issue.init_journal(self.logged_in_user, params[:notes]) |
|
77 | journal = @issue.init_journal(self.logged_in_user, params[:notes]) | |
76 | if @issue.save |
|
78 | if @issue.save | |
77 | flash[:notice] = l(:notice_successful_update) |
|
79 | flash[:notice] = l(:notice_successful_update) | |
78 | Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? |
|
80 | Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? | |
79 | redirect_to :action => 'show', :id => @issue |
|
81 | redirect_to :action => 'show', :id => @issue | |
80 | return |
|
82 | return | |
81 | end |
|
83 | end | |
82 | end |
|
84 | end | |
83 | show |
|
85 | show | |
84 | render :action => 'show' |
|
86 | render :action => 'show' | |
85 | end |
|
87 | end | |
86 |
|
88 | |||
87 | def change_status |
|
89 | def change_status | |
88 | @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user |
|
90 | @status_options = @issue.status.find_new_statuses_allowed_to(logged_in_user.role_for_project(@project), @issue.tracker) if logged_in_user | |
89 | @new_status = IssueStatus.find(params[:new_status_id]) |
|
91 | @new_status = IssueStatus.find(params[:new_status_id]) | |
90 | if params[:confirm] |
|
92 | if params[:confirm] | |
91 | begin |
|
93 | begin | |
92 | journal = @issue.init_journal(self.logged_in_user, params[:notes]) |
|
94 | journal = @issue.init_journal(self.logged_in_user, params[:notes]) | |
93 | @issue.status = @new_status |
|
95 | @issue.status = @new_status | |
94 | if @issue.update_attributes(params[:issue]) |
|
96 | if @issue.update_attributes(params[:issue]) | |
95 | # Save attachments |
|
97 | # Save attachments | |
96 | params[:attachments].each { |file| |
|
98 | params[:attachments].each { |file| | |
97 | next unless file.size > 0 |
|
99 | next unless file.size > 0 | |
98 | a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user) |
|
100 | a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user) | |
99 | journal.details << JournalDetail.new(:property => 'attachment', |
|
101 | journal.details << JournalDetail.new(:property => 'attachment', | |
100 | :prop_key => a.id, |
|
102 | :prop_key => a.id, | |
101 | :value => a.filename) unless a.new_record? |
|
103 | :value => a.filename) unless a.new_record? | |
102 | } if params[:attachments] and params[:attachments].is_a? Array |
|
104 | } if params[:attachments] and params[:attachments].is_a? Array | |
103 |
|
105 | |||
104 | flash[:notice] = l(:notice_successful_update) |
|
106 | flash[:notice] = l(:notice_successful_update) | |
105 | Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? |
|
107 | Mailer.deliver_issue_edit(journal) if Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? | |
106 | redirect_to :action => 'show', :id => @issue |
|
108 | redirect_to :action => 'show', :id => @issue | |
107 | end |
|
109 | end | |
108 | rescue ActiveRecord::StaleObjectError |
|
110 | rescue ActiveRecord::StaleObjectError | |
109 | # Optimistic locking exception |
|
111 | # Optimistic locking exception | |
110 | flash[:notice] = l(:notice_locking_conflict) |
|
112 | flash[:notice] = l(:notice_locking_conflict) | |
111 | end |
|
113 | end | |
112 | end |
|
114 | end | |
113 | @assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user } |
|
115 | @assignable_to = @project.members.find(:all, :include => :user).collect{ |m| m.user } | |
114 | end |
|
116 | end | |
115 |
|
117 | |||
116 | def destroy |
|
118 | def destroy | |
117 | @issue.destroy |
|
119 | @issue.destroy | |
118 | redirect_to :controller => 'projects', :action => 'list_issues', :id => @project |
|
120 | redirect_to :controller => 'projects', :action => 'list_issues', :id => @project | |
119 | end |
|
121 | end | |
120 |
|
122 | |||
121 | def add_attachment |
|
123 | def add_attachment | |
122 | # Save the attachments |
|
124 | # Save the attachments | |
123 | @attachments = [] |
|
125 | @attachments = [] | |
124 | journal = @issue.init_journal(self.logged_in_user) |
|
126 | journal = @issue.init_journal(self.logged_in_user) | |
125 | params[:attachments].each { |file| |
|
127 | params[:attachments].each { |file| | |
126 | next unless file.size > 0 |
|
128 | next unless file.size > 0 | |
127 | a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user) |
|
129 | a = Attachment.create(:container => @issue, :file => file, :author => logged_in_user) | |
128 | @attachments << a unless a.new_record? |
|
130 | @attachments << a unless a.new_record? | |
129 | journal.details << JournalDetail.new(:property => 'attachment', |
|
131 | journal.details << JournalDetail.new(:property => 'attachment', | |
130 | :prop_key => a.id, |
|
132 | :prop_key => a.id, | |
131 | :value => a.filename) unless a.new_record? |
|
133 | :value => a.filename) unless a.new_record? | |
132 | } if params[:attachments] and params[:attachments].is_a? Array |
|
134 | } if params[:attachments] and params[:attachments].is_a? Array | |
133 | journal.save if journal.details.any? |
|
135 | journal.save if journal.details.any? | |
134 | Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? |
|
136 | Mailer.deliver_attachments_add(@attachments) if !@attachments.empty? and Permission.find_by_controller_and_action(params[:controller], params[:action]).mail_enabled? | |
135 | redirect_to :action => 'show', :id => @issue |
|
137 | redirect_to :action => 'show', :id => @issue | |
136 | end |
|
138 | end | |
137 |
|
139 | |||
138 | def destroy_attachment |
|
140 | def destroy_attachment | |
139 | a = @issue.attachments.find(params[:attachment_id]) |
|
141 | a = @issue.attachments.find(params[:attachment_id]) | |
140 | a.destroy |
|
142 | a.destroy | |
141 | journal = @issue.init_journal(self.logged_in_user) |
|
143 | journal = @issue.init_journal(self.logged_in_user) | |
142 | journal.details << JournalDetail.new(:property => 'attachment', |
|
144 | journal.details << JournalDetail.new(:property => 'attachment', | |
143 | :prop_key => a.id, |
|
145 | :prop_key => a.id, | |
144 | :old_value => a.filename) |
|
146 | :old_value => a.filename) | |
145 | journal.save |
|
147 | journal.save | |
146 | redirect_to :action => 'show', :id => @issue |
|
148 | redirect_to :action => 'show', :id => @issue | |
147 | end |
|
149 | end | |
148 |
|
150 | |||
149 | # Send the file in stream mode |
|
|||
150 | def download |
|
|||
151 | @attachment = @issue.attachments.find(params[:attachment_id]) |
|
|||
152 | send_file @attachment.diskfile, :filename => @attachment.filename |
|
|||
153 | rescue |
|
|||
154 | render_404 |
|
|||
155 | end |
|
|||
156 |
|
||||
157 | private |
|
151 | private | |
158 | def find_project |
|
152 | def find_project | |
159 | @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) |
|
153 | @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category]) | |
160 | @project = @issue.project |
|
154 | @project = @issue.project | |
161 | @html_title = "#{@project.name} - #{@issue.tracker.name} ##{@issue.id}" |
|
155 | @html_title = "#{@project.name} - #{@issue.tracker.name} ##{@issue.id}" | |
162 | rescue ActiveRecord::RecordNotFound |
|
156 | rescue ActiveRecord::RecordNotFound | |
163 | render_404 |
|
157 | render_404 | |
164 | end |
|
158 | end | |
165 | end |
|
159 | end |
@@ -1,66 +1,62 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class MessagesController < ApplicationController |
|
18 | class MessagesController < ApplicationController | |
19 | layout 'base' |
|
19 | layout 'base' | |
20 | before_filter :find_project, :check_project_privacy |
|
20 | before_filter :find_project, :check_project_privacy | |
21 | before_filter :require_login, :only => [:new, :reply] |
|
21 | before_filter :require_login, :only => [:new, :reply] | |
22 |
|
22 | |||
23 | verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show } |
|
23 | verify :method => :post, :only => [ :reply, :destroy ], :redirect_to => { :action => :show } | |
24 |
|
24 | |||
|
25 | helper :attachments | |||
|
26 | include AttachmentsHelper | |||
|
27 | ||||
25 | def show |
|
28 | def show | |
26 | @reply = Message.new(:subject => "RE: #{@message.subject}") |
|
29 | @reply = Message.new(:subject => "RE: #{@message.subject}") | |
27 | render :action => "show", :layout => false if request.xhr? |
|
30 | render :action => "show", :layout => false if request.xhr? | |
28 | end |
|
31 | end | |
29 |
|
32 | |||
30 | def new |
|
33 | def new | |
31 | @message = Message.new(params[:message]) |
|
34 | @message = Message.new(params[:message]) | |
32 | @message.author = logged_in_user |
|
35 | @message.author = logged_in_user | |
33 | @message.board = @board |
|
36 | @message.board = @board | |
34 | if request.post? && @message.save |
|
37 | if request.post? && @message.save | |
35 | params[:attachments].each { |file| |
|
38 | params[:attachments].each { |file| | |
36 | next unless file.size > 0 |
|
39 | next unless file.size > 0 | |
37 | Attachment.create(:container => @message, :file => file, :author => logged_in_user) |
|
40 | Attachment.create(:container => @message, :file => file, :author => logged_in_user) | |
38 | } if params[:attachments] and params[:attachments].is_a? Array |
|
41 | } if params[:attachments] and params[:attachments].is_a? Array | |
39 | redirect_to :action => 'show', :id => @message |
|
42 | redirect_to :action => 'show', :id => @message | |
40 | end |
|
43 | end | |
41 | end |
|
44 | end | |
42 |
|
45 | |||
43 | def reply |
|
46 | def reply | |
44 | @reply = Message.new(params[:reply]) |
|
47 | @reply = Message.new(params[:reply]) | |
45 | @reply.author = logged_in_user |
|
48 | @reply.author = logged_in_user | |
46 | @reply.board = @board |
|
49 | @reply.board = @board | |
47 | @message.children << @reply |
|
50 | @message.children << @reply | |
48 | redirect_to :action => 'show', :id => @message |
|
51 | redirect_to :action => 'show', :id => @message | |
49 | end |
|
52 | end | |
50 |
|
53 | |||
51 | def download |
|
|||
52 | @attachment = @message.attachments.find(params[:attachment_id]) |
|
|||
53 | send_file @attachment.diskfile, :filename => @attachment.filename |
|
|||
54 | rescue |
|
|||
55 | render_404 |
|
|||
56 | end |
|
|||
57 |
|
||||
58 | private |
|
54 | private | |
59 | def find_project |
|
55 | def find_project | |
60 | @board = Board.find(params[:board_id], :include => :project) |
|
56 | @board = Board.find(params[:board_id], :include => :project) | |
61 | @project = @board.project |
|
57 | @project = @board.project | |
62 | @message = @board.topics.find(params[:id]) if params[:id] |
|
58 | @message = @board.topics.find(params[:id]) if params[:id] | |
63 | rescue ActiveRecord::RecordNotFound |
|
59 | rescue ActiveRecord::RecordNotFound | |
64 | render_404 |
|
60 | render_404 | |
65 | end |
|
61 | end | |
66 | end |
|
62 | end |
@@ -1,122 +1,143 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class WikiController < ApplicationController |
|
18 | class WikiController < ApplicationController | |
19 | layout 'base' |
|
19 | layout 'base' | |
20 |
before_filter :find_wiki, :check_project_privacy |
|
20 | before_filter :find_wiki, :check_project_privacy | |
21 | before_filter :authorize, :only => :destroy |
|
21 | before_filter :authorize, :only => [:destroy, :add_attachment, :destroy_attachment] | |
22 |
|
22 | |||
23 |
verify :method => :post, :only => [ :destroy |
|
23 | verify :method => :post, :only => [:destroy, :destroy_attachment], :redirect_to => { :action => :index } | |
|
24 | ||||
|
25 | helper :attachments | |||
|
26 | include AttachmentsHelper | |||
24 |
|
27 | |||
25 | # display a page (in editing mode if it doesn't exist) |
|
28 | # display a page (in editing mode if it doesn't exist) | |
26 | def index |
|
29 | def index | |
27 | page_title = params[:page] |
|
30 | page_title = params[:page] | |
28 | @page = @wiki.find_or_new_page(page_title) |
|
31 | @page = @wiki.find_or_new_page(page_title) | |
29 | if @page.new_record? |
|
32 | if @page.new_record? | |
30 | edit |
|
33 | edit | |
31 | render :action => 'edit' and return |
|
34 | render :action => 'edit' and return | |
32 | end |
|
35 | end | |
33 | @content = @page.content_for_version(params[:version]) |
|
36 | @content = @page.content_for_version(params[:version]) | |
34 | if params[:export] == 'html' |
|
37 | if params[:export] == 'html' | |
35 | export = render_to_string :action => 'export', :layout => false |
|
38 | export = render_to_string :action => 'export', :layout => false | |
36 | send_data(export, :type => 'text/html', :filename => "#{@page.title}.html") |
|
39 | send_data(export, :type => 'text/html', :filename => "#{@page.title}.html") | |
37 | return |
|
40 | return | |
38 | elsif params[:export] == 'txt' |
|
41 | elsif params[:export] == 'txt' | |
39 | send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt") |
|
42 | send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt") | |
40 | return |
|
43 | return | |
41 | end |
|
44 | end | |
42 | render :action => 'show' |
|
45 | render :action => 'show' | |
43 | end |
|
46 | end | |
44 |
|
47 | |||
45 | # edit an existing page or a new one |
|
48 | # edit an existing page or a new one | |
46 | def edit |
|
49 | def edit | |
47 | @page = @wiki.find_or_new_page(params[:page]) |
|
50 | @page = @wiki.find_or_new_page(params[:page]) | |
48 | @page.content = WikiContent.new(:page => @page) if @page.new_record? |
|
51 | @page.content = WikiContent.new(:page => @page) if @page.new_record? | |
49 |
|
52 | |||
50 | @content = @page.content_for_version(params[:version]) |
|
53 | @content = @page.content_for_version(params[:version]) | |
51 | @content.text = "h1. #{@page.pretty_title}" if @content.text.blank? |
|
54 | @content.text = "h1. #{@page.pretty_title}" if @content.text.blank? | |
52 | # don't keep previous comment |
|
55 | # don't keep previous comment | |
53 | @content.comments = nil |
|
56 | @content.comments = nil | |
54 | if request.post? |
|
57 | if request.post? | |
55 | if @content.text == params[:content][:text] |
|
58 | if @content.text == params[:content][:text] | |
56 | # don't save if text wasn't changed |
|
59 | # don't save if text wasn't changed | |
57 | redirect_to :action => 'index', :id => @project, :page => @page.title |
|
60 | redirect_to :action => 'index', :id => @project, :page => @page.title | |
58 | return |
|
61 | return | |
59 | end |
|
62 | end | |
60 | @content.text = params[:content][:text] |
|
63 | @content.text = params[:content][:text] | |
61 | @content.comments = params[:content][:comments] |
|
64 | @content.comments = params[:content][:comments] | |
62 | @content.author = logged_in_user |
|
65 | @content.author = logged_in_user | |
63 | # if page is new @page.save will also save content, but not if page isn't a new record |
|
66 | # if page is new @page.save will also save content, but not if page isn't a new record | |
64 | if (@page.new_record? ? @page.save : @content.save) |
|
67 | if (@page.new_record? ? @page.save : @content.save) | |
65 | redirect_to :action => 'index', :id => @project, :page => @page.title |
|
68 | redirect_to :action => 'index', :id => @project, :page => @page.title | |
66 | end |
|
69 | end | |
67 | end |
|
70 | end | |
68 | end |
|
71 | end | |
69 |
|
72 | |||
70 | # show page history |
|
73 | # show page history | |
71 | def history |
|
74 | def history | |
72 | @page = @wiki.find_page(params[:page]) |
|
75 | @page = @wiki.find_page(params[:page]) | |
73 | # don't load text |
|
76 | # don't load text | |
74 | @versions = @page.content.versions.find :all, |
|
77 | @versions = @page.content.versions.find :all, | |
75 | :select => "id, author_id, comments, updated_on, version", |
|
78 | :select => "id, author_id, comments, updated_on, version", | |
76 | :order => 'version DESC' |
|
79 | :order => 'version DESC' | |
77 | end |
|
80 | end | |
78 |
|
81 | |||
79 | # remove a wiki page and its history |
|
82 | # remove a wiki page and its history | |
80 | def destroy |
|
83 | def destroy | |
81 | @page = @wiki.find_page(params[:page]) |
|
84 | @page = @wiki.find_page(params[:page]) | |
82 | @page.destroy if @page |
|
85 | @page.destroy if @page | |
83 | redirect_to :action => 'special', :id => @project, :page => 'Page_index' |
|
86 | redirect_to :action => 'special', :id => @project, :page => 'Page_index' | |
84 | end |
|
87 | end | |
85 |
|
88 | |||
86 | # display special pages |
|
89 | # display special pages | |
87 | def special |
|
90 | def special | |
88 | page_title = params[:page].downcase |
|
91 | page_title = params[:page].downcase | |
89 | case page_title |
|
92 | case page_title | |
90 | # show pages index, sorted by title |
|
93 | # show pages index, sorted by title | |
91 | when 'page_index' |
|
94 | when 'page_index' | |
92 | # eager load information about last updates, without loading text |
|
95 | # eager load information about last updates, without loading text | |
93 | @pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on", |
|
96 | @pages = @wiki.pages.find :all, :select => "#{WikiPage.table_name}.*, #{WikiContent.table_name}.updated_on", | |
94 | :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id", |
|
97 | :joins => "LEFT JOIN #{WikiContent.table_name} ON #{WikiContent.table_name}.page_id = #{WikiPage.table_name}.id", | |
95 | :order => 'title' |
|
98 | :order => 'title' | |
96 | # export wiki to a single html file |
|
99 | # export wiki to a single html file | |
97 | when 'export' |
|
100 | when 'export' | |
98 | @pages = @wiki.pages.find :all, :order => 'title' |
|
101 | @pages = @wiki.pages.find :all, :order => 'title' | |
99 | export = render_to_string :action => 'export_multiple', :layout => false |
|
102 | export = render_to_string :action => 'export_multiple', :layout => false | |
100 | send_data(export, :type => 'text/html', :filename => "wiki.html") |
|
103 | send_data(export, :type => 'text/html', :filename => "wiki.html") | |
101 | return |
|
104 | return | |
102 | else |
|
105 | else | |
103 | # requested special page doesn't exist, redirect to default page |
|
106 | # requested special page doesn't exist, redirect to default page | |
104 | redirect_to :action => 'index', :id => @project, :page => nil and return |
|
107 | redirect_to :action => 'index', :id => @project, :page => nil and return | |
105 | end |
|
108 | end | |
106 | render :action => "special_#{page_title}" |
|
109 | render :action => "special_#{page_title}" | |
107 | end |
|
110 | end | |
108 |
|
111 | |||
109 | def preview |
|
112 | def preview | |
|
113 | page = @wiki.find_page(params[:page]) | |||
|
114 | @attachements = page.attachments if page | |||
110 | @text = params[:content][:text] |
|
115 | @text = params[:content][:text] | |
111 | render :partial => 'preview' |
|
116 | render :partial => 'preview' | |
112 | end |
|
117 | end | |
113 |
|
118 | |||
|
119 | def add_attachment | |||
|
120 | @page = @wiki.find_page(params[:page]) | |||
|
121 | # Save the attachments | |||
|
122 | params[:attachments].each { |file| | |||
|
123 | next unless file.size > 0 | |||
|
124 | a = Attachment.create(:container => @page, :file => file, :author => logged_in_user) | |||
|
125 | } if params[:attachments] and params[:attachments].is_a? Array | |||
|
126 | redirect_to :action => 'index', :page => @page.title | |||
|
127 | end | |||
|
128 | ||||
|
129 | def destroy_attachment | |||
|
130 | @page = @wiki.find_page(params[:page]) | |||
|
131 | @page.attachments.find(params[:attachment_id]).destroy | |||
|
132 | redirect_to :action => 'index', :page => @page.title | |||
|
133 | end | |||
|
134 | ||||
114 | private |
|
135 | private | |
115 |
|
136 | |||
116 | def find_wiki |
|
137 | def find_wiki | |
117 | @project = Project.find(params[:id]) |
|
138 | @project = Project.find(params[:id]) | |
118 | @wiki = @project.wiki |
|
139 | @wiki = @project.wiki | |
119 | rescue ActiveRecord::RecordNotFound |
|
140 | rescue ActiveRecord::RecordNotFound | |
120 | render_404 |
|
141 | render_404 | |
121 | end |
|
142 | end | |
122 | end |
|
143 | end |
@@ -1,256 +1,273 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class RedCloth |
|
18 | class RedCloth | |
19 | # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet. |
|
19 | # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet. | |
20 | # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> |
|
20 | # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> | |
21 | def hard_break( text ) |
|
21 | def hard_break( text ) | |
22 | text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks |
|
22 | text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks | |
23 | end |
|
23 | end | |
24 | end |
|
24 | end | |
25 |
|
25 | |||
26 | module ApplicationHelper |
|
26 | module ApplicationHelper | |
27 |
|
27 | |||
28 | # Return current logged in user or nil |
|
28 | # Return current logged in user or nil | |
29 | def loggedin? |
|
29 | def loggedin? | |
30 | @logged_in_user |
|
30 | @logged_in_user | |
31 | end |
|
31 | end | |
32 |
|
32 | |||
33 | # Return true if user is logged in and is admin, otherwise false |
|
33 | # Return true if user is logged in and is admin, otherwise false | |
34 | def admin_loggedin? |
|
34 | def admin_loggedin? | |
35 | @logged_in_user and @logged_in_user.admin? |
|
35 | @logged_in_user and @logged_in_user.admin? | |
36 | end |
|
36 | end | |
37 |
|
37 | |||
38 | # Return true if user is authorized for controller/action, otherwise false |
|
38 | # Return true if user is authorized for controller/action, otherwise false | |
39 | def authorize_for(controller, action) |
|
39 | def authorize_for(controller, action) | |
40 | # check if action is allowed on public projects |
|
40 | # check if action is allowed on public projects | |
41 | if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ] |
|
41 | if @project.is_public? and Permission.allowed_to_public "%s/%s" % [ controller, action ] | |
42 | return true |
|
42 | return true | |
43 | end |
|
43 | end | |
44 | # check if user is authorized |
|
44 | # check if user is authorized | |
45 | if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) ) |
|
45 | if @logged_in_user and (@logged_in_user.admin? or Permission.allowed_to_role( "%s/%s" % [ controller, action ], @logged_in_user.role_for_project(@project) ) ) | |
46 | return true |
|
46 | return true | |
47 | end |
|
47 | end | |
48 | return false |
|
48 | return false | |
49 | end |
|
49 | end | |
50 |
|
50 | |||
51 | # Display a link if user is authorized |
|
51 | # Display a link if user is authorized | |
52 | def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) |
|
52 | def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference) | |
53 | link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action]) |
|
53 | link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller], options[:action]) | |
54 | end |
|
54 | end | |
55 |
|
55 | |||
56 | # Display a link to user's account page |
|
56 | # Display a link to user's account page | |
57 | def link_to_user(user) |
|
57 | def link_to_user(user) | |
58 | link_to user.display_name, :controller => 'account', :action => 'show', :id => user |
|
58 | link_to user.display_name, :controller => 'account', :action => 'show', :id => user | |
59 | end |
|
59 | end | |
60 |
|
60 | |||
61 | def link_to_issue(issue) |
|
61 | def link_to_issue(issue) | |
62 | link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue |
|
62 | link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
65 | def toggle_link(name, id, options={}) |
|
65 | def toggle_link(name, id, options={}) | |
66 | onclick = "Element.toggle('#{id}'); " |
|
66 | onclick = "Element.toggle('#{id}'); " | |
67 | onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ") |
|
67 | onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ") | |
68 | onclick << "return false;" |
|
68 | onclick << "return false;" | |
69 | link_to(name, "#", :onclick => onclick) |
|
69 | link_to(name, "#", :onclick => onclick) | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
72 | def image_to_function(name, function, html_options = {}) |
|
72 | def image_to_function(name, function, html_options = {}) | |
73 | html_options.symbolize_keys! |
|
73 | html_options.symbolize_keys! | |
74 | tag(:input, html_options.merge({ |
|
74 | tag(:input, html_options.merge({ | |
75 | :type => "image", :src => image_path(name), |
|
75 | :type => "image", :src => image_path(name), | |
76 | :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};" |
|
76 | :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};" | |
77 | })) |
|
77 | })) | |
78 | end |
|
78 | end | |
79 |
|
79 | |||
80 | def format_date(date) |
|
80 | def format_date(date) | |
81 | l_date(date) if date |
|
81 | l_date(date) if date | |
82 | end |
|
82 | end | |
83 |
|
83 | |||
84 | def format_time(time) |
|
84 | def format_time(time) | |
85 | l_datetime((time.is_a? String) ? time.to_time : time) if time |
|
85 | l_datetime((time.is_a? String) ? time.to_time : time) if time | |
86 | end |
|
86 | end | |
87 |
|
87 | |||
88 | def day_name(day) |
|
88 | def day_name(day) | |
89 | l(:general_day_names).split(',')[day-1] |
|
89 | l(:general_day_names).split(',')[day-1] | |
90 | end |
|
90 | end | |
91 |
|
91 | |||
92 | def month_name(month) |
|
92 | def month_name(month) | |
93 | l(:actionview_datehelper_select_month_names).split(',')[month-1] |
|
93 | l(:actionview_datehelper_select_month_names).split(',')[month-1] | |
94 | end |
|
94 | end | |
95 |
|
95 | |||
96 | def pagination_links_full(paginator, options={}, html_options={}) |
|
96 | def pagination_links_full(paginator, options={}, html_options={}) | |
97 | html = '' |
|
97 | html = '' | |
98 | html << link_to_remote(('« ' + l(:label_previous)), |
|
98 | html << link_to_remote(('« ' + l(:label_previous)), | |
99 | {:update => "content", :url => options.merge(:page => paginator.current.previous)}, |
|
99 | {:update => "content", :url => options.merge(:page => paginator.current.previous)}, | |
100 | {:href => url_for(:params => options.merge(:page => paginator.current.previous))}) + ' ' if paginator.current.previous |
|
100 | {:href => url_for(:params => options.merge(:page => paginator.current.previous))}) + ' ' if paginator.current.previous | |
101 |
|
101 | |||
102 | html << (pagination_links_each(paginator, options) do |n| |
|
102 | html << (pagination_links_each(paginator, options) do |n| | |
103 | link_to_remote(n.to_s, |
|
103 | link_to_remote(n.to_s, | |
104 | {:url => {:params => options.merge(:page => n)}, :update => 'content'}, |
|
104 | {:url => {:params => options.merge(:page => n)}, :update => 'content'}, | |
105 | {:href => url_for(:params => options.merge(:page => n))}) |
|
105 | {:href => url_for(:params => options.merge(:page => n))}) | |
106 | end || '') |
|
106 | end || '') | |
107 |
|
107 | |||
108 | html << ' ' + link_to_remote((l(:label_next) + ' »'), |
|
108 | html << ' ' + link_to_remote((l(:label_next) + ' »'), | |
109 | {:update => "content", :url => options.merge(:page => paginator.current.next)}, |
|
109 | {:update => "content", :url => options.merge(:page => paginator.current.next)}, | |
110 | {:href => url_for(:params => options.merge(:page => paginator.current.next))}) if paginator.current.next |
|
110 | {:href => url_for(:params => options.merge(:page => paginator.current.next))}) if paginator.current.next | |
111 | html |
|
111 | html | |
112 | end |
|
112 | end | |
113 |
|
113 | |||
114 | # textilize text according to system settings and RedCloth availability |
|
114 | # textilize text according to system settings and RedCloth availability | |
115 | def textilizable(text, options = {}) |
|
115 | def textilizable(text, options = {}) | |
116 | return "" if text.blank? |
|
116 | return "" if text.blank? | |
117 |
|
117 | |||
118 | # different methods for formatting wiki links |
|
118 | # different methods for formatting wiki links | |
119 | case options[:wiki_links] |
|
119 | case options[:wiki_links] | |
120 | when :local |
|
120 | when :local | |
121 | # used for local links to html files |
|
121 | # used for local links to html files | |
122 | format_wiki_link = Proc.new {|title| "#{title}.html" } |
|
122 | format_wiki_link = Proc.new {|title| "#{title}.html" } | |
123 | when :anchor |
|
123 | when :anchor | |
124 | # used for single-file wiki export |
|
124 | # used for single-file wiki export | |
125 | format_wiki_link = Proc.new {|title| "##{title}" } |
|
125 | format_wiki_link = Proc.new {|title| "##{title}" } | |
126 | else |
|
126 | else | |
127 | if @project |
|
127 | if @project | |
128 | format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title } |
|
128 | format_wiki_link = Proc.new {|title| url_for :controller => 'wiki', :action => 'index', :id => @project, :page => title } | |
129 | else |
|
129 | else | |
130 | format_wiki_link = Proc.new {|title| title } |
|
130 | format_wiki_link = Proc.new {|title| title } | |
131 | end |
|
131 | end | |
132 | end |
|
132 | end | |
133 |
|
133 | |||
134 | # turn wiki links into textile links: |
|
134 | # turn wiki links into textile links: | |
135 | # example: |
|
135 | # example: | |
136 | # [[link]] -> "link":link |
|
136 | # [[link]] -> "link":link | |
137 | # [[link|title]] -> "title":link |
|
137 | # [[link|title]] -> "title":link | |
138 | text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| "\"#{$3 || $1}\":" + format_wiki_link.call(Wiki.titleize($1)) } |
|
138 | text = text.gsub(/\[\[([^\]\|]+)(\|([^\]\|]+))?\]\]/) {|m| "\"#{$3 || $1}\":" + format_wiki_link.call(Wiki.titleize($1)) } | |
139 |
|
139 | |||
140 | # turn issue ids into links |
|
140 | # turn issue ids into links | |
141 | # example: |
|
141 | # example: | |
142 | # #52 -> <a href="/issues/show/52">#52</a> |
|
142 | # #52 -> <a href="/issues/show/52">#52</a> | |
143 | text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", :controller => 'issues', :action => 'show', :id => $1} |
|
143 | text = text.gsub(/#(\d+)(?=\b)/) {|m| link_to "##{$1}", :controller => 'issues', :action => 'show', :id => $1} | |
144 |
|
144 | |||
145 | # turn revision ids into links (@project needed) |
|
145 | # turn revision ids into links (@project needed) | |
146 | # example: |
|
146 | # example: | |
147 | # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6) |
|
147 | # r52 -> <a href="/repositories/revision/6?rev=52">r52</a> (@project.id is 6) | |
148 | text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", :controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1} if @project |
|
148 | text = text.gsub(/(?=\b)r(\d+)(?=\b)/) {|m| link_to "r#{$1}", :controller => 'repositories', :action => 'revision', :id => @project.id, :rev => $1} if @project | |
149 |
|
149 | |||
|
150 | # when using an image link, try to use an attachment, if possible | |||
|
151 | attachments = options[:attachments] | |||
|
152 | if attachments | |||
|
153 | text = text.gsub(/!([<>=]*)(\S+\.(gif|jpg|jpeg|png))!/) do |m| | |||
|
154 | align = $1 | |||
|
155 | filename = $2 | |||
|
156 | rf = Regexp.new(filename, Regexp::IGNORECASE) | |||
|
157 | # search for the picture in attachments | |||
|
158 | if found = attachments.detect { |att| att.filename =~ rf } | |||
|
159 | image_url = url_for :controller => 'attachments', :action => 'show', :id => found.id | |||
|
160 | "!#{align}#{image_url}!" | |||
|
161 | else | |||
|
162 | "!#{align}#{filename}!" | |||
|
163 | end | |||
|
164 | end | |||
|
165 | end | |||
|
166 | ||||
150 | # finally textilize text |
|
167 | # finally textilize text | |
151 | @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize") |
|
168 | @do_textilize ||= (Setting.text_formatting == 'textile') && (ActionView::Helpers::TextHelper.method_defined? "textilize") | |
152 | text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text))) |
|
169 | text = @do_textilize ? auto_link(RedCloth.new(text, [:hard_breaks]).to_html) : simple_format(auto_link(h(text))) | |
153 | end |
|
170 | end | |
154 |
|
171 | |||
155 | def error_messages_for(object_name, options = {}) |
|
172 | def error_messages_for(object_name, options = {}) | |
156 | options = options.symbolize_keys |
|
173 | options = options.symbolize_keys | |
157 | object = instance_variable_get("@#{object_name}") |
|
174 | object = instance_variable_get("@#{object_name}") | |
158 | if object && !object.errors.empty? |
|
175 | if object && !object.errors.empty? | |
159 | # build full_messages here with controller current language |
|
176 | # build full_messages here with controller current language | |
160 | full_messages = [] |
|
177 | full_messages = [] | |
161 | object.errors.each do |attr, msg| |
|
178 | object.errors.each do |attr, msg| | |
162 | next if msg.nil? |
|
179 | next if msg.nil? | |
163 | msg = msg.first if msg.is_a? Array |
|
180 | msg = msg.first if msg.is_a? Array | |
164 | if attr == "base" |
|
181 | if attr == "base" | |
165 | full_messages << l(msg) |
|
182 | full_messages << l(msg) | |
166 | else |
|
183 | else | |
167 | full_messages << "« " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " » " + l(msg) unless attr == "custom_values" |
|
184 | full_messages << "« " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " » " + l(msg) unless attr == "custom_values" | |
168 | end |
|
185 | end | |
169 | end |
|
186 | end | |
170 | # retrieve custom values error messages |
|
187 | # retrieve custom values error messages | |
171 | if object.errors[:custom_values] |
|
188 | if object.errors[:custom_values] | |
172 | object.custom_values.each do |v| |
|
189 | object.custom_values.each do |v| | |
173 | v.errors.each do |attr, msg| |
|
190 | v.errors.each do |attr, msg| | |
174 | next if msg.nil? |
|
191 | next if msg.nil? | |
175 | msg = msg.first if msg.is_a? Array |
|
192 | msg = msg.first if msg.is_a? Array | |
176 | full_messages << "« " + v.custom_field.name + " » " + l(msg) |
|
193 | full_messages << "« " + v.custom_field.name + " » " + l(msg) | |
177 | end |
|
194 | end | |
178 | end |
|
195 | end | |
179 | end |
|
196 | end | |
180 | content_tag("div", |
|
197 | content_tag("div", | |
181 | content_tag( |
|
198 | content_tag( | |
182 | options[:header_tag] || "h2", lwr(:gui_validation_error, full_messages.length) + " :" |
|
199 | options[:header_tag] || "h2", lwr(:gui_validation_error, full_messages.length) + " :" | |
183 | ) + |
|
200 | ) + | |
184 | content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }), |
|
201 | content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }), | |
185 | "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation" |
|
202 | "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation" | |
186 | ) |
|
203 | ) | |
187 | else |
|
204 | else | |
188 | "" |
|
205 | "" | |
189 | end |
|
206 | end | |
190 | end |
|
207 | end | |
191 |
|
208 | |||
192 | def lang_options_for_select(blank=true) |
|
209 | def lang_options_for_select(blank=true) | |
193 | (blank ? [["(auto)", ""]] : []) + |
|
210 | (blank ? [["(auto)", ""]] : []) + | |
194 | GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first } |
|
211 | GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.first <=> y.first } | |
195 | end |
|
212 | end | |
196 |
|
213 | |||
197 | def label_tag_for(name, option_tags = nil, options = {}) |
|
214 | def label_tag_for(name, option_tags = nil, options = {}) | |
198 | label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") |
|
215 | label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") | |
199 | content_tag("label", label_text) |
|
216 | content_tag("label", label_text) | |
200 | end |
|
217 | end | |
201 |
|
218 | |||
202 | def labelled_tabular_form_for(name, object, options, &proc) |
|
219 | def labelled_tabular_form_for(name, object, options, &proc) | |
203 | options[:html] ||= {} |
|
220 | options[:html] ||= {} | |
204 | options[:html].store :class, "tabular" |
|
221 | options[:html].store :class, "tabular" | |
205 | form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc) |
|
222 | form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc) | |
206 | end |
|
223 | end | |
207 |
|
224 | |||
208 | def check_all_links(form_name) |
|
225 | def check_all_links(form_name) | |
209 | link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") + |
|
226 | link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") + | |
210 | " | " + |
|
227 | " | " + | |
211 | link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") |
|
228 | link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)") | |
212 | end |
|
229 | end | |
213 |
|
230 | |||
214 | def calendar_for(field_id) |
|
231 | def calendar_for(field_id) | |
215 | image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) + |
|
232 | image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) + | |
216 | javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });") |
|
233 | javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });") | |
217 | end |
|
234 | end | |
218 |
|
235 | |||
219 | def wikitoolbar_for(field_id) |
|
236 | def wikitoolbar_for(field_id) | |
220 | return '' unless Setting.text_formatting == 'textile' |
|
237 | return '' unless Setting.text_formatting == 'textile' | |
221 | javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();") |
|
238 | javascript_include_tag('jstoolbar') + javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();") | |
222 | end |
|
239 | end | |
223 | end |
|
240 | end | |
224 |
|
241 | |||
225 | class TabularFormBuilder < ActionView::Helpers::FormBuilder |
|
242 | class TabularFormBuilder < ActionView::Helpers::FormBuilder | |
226 | include GLoc |
|
243 | include GLoc | |
227 |
|
244 | |||
228 | def initialize(object_name, object, template, options, proc) |
|
245 | def initialize(object_name, object, template, options, proc) | |
229 | set_language_if_valid options.delete(:lang) |
|
246 | set_language_if_valid options.delete(:lang) | |
230 | @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc |
|
247 | @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc | |
231 | end |
|
248 | end | |
232 |
|
249 | |||
233 | (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector| |
|
250 | (field_helpers - %w(radio_button hidden_field) + %w(date_select)).each do |selector| | |
234 | src = <<-END_SRC |
|
251 | src = <<-END_SRC | |
235 | def #{selector}(field, options = {}) |
|
252 | def #{selector}(field, options = {}) | |
236 | return super if options.delete :no_label |
|
253 | return super if options.delete :no_label | |
237 | label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") |
|
254 | label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") | |
238 | label = @template.content_tag("label", label_text, |
|
255 | label = @template.content_tag("label", label_text, | |
239 | :class => (@object && @object.errors[field] ? "error" : nil), |
|
256 | :class => (@object && @object.errors[field] ? "error" : nil), | |
240 | :for => (@object_name.to_s + "_" + field.to_s)) |
|
257 | :for => (@object_name.to_s + "_" + field.to_s)) | |
241 | label + super |
|
258 | label + super | |
242 | end |
|
259 | end | |
243 | END_SRC |
|
260 | END_SRC | |
244 | class_eval src, __FILE__, __LINE__ |
|
261 | class_eval src, __FILE__, __LINE__ | |
245 | end |
|
262 | end | |
246 |
|
263 | |||
247 | def select(field, choices, options = {}, html_options = {}) |
|
264 | def select(field, choices, options = {}, html_options = {}) | |
248 | label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") |
|
265 | label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "") | |
249 | label = @template.content_tag("label", label_text, |
|
266 | label = @template.content_tag("label", label_text, | |
250 | :class => (@object && @object.errors[field] ? "error" : nil), |
|
267 | :class => (@object && @object.errors[field] ? "error" : nil), | |
251 | :for => (@object_name.to_s + "_" + field.to_s)) |
|
268 | :for => (@object_name.to_s + "_" + field.to_s)) | |
252 | label + super |
|
269 | label + super | |
253 | end |
|
270 | end | |
254 |
|
271 | |||
255 | end |
|
272 | end | |
256 |
|
273 |
@@ -1,92 +1,96 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require "digest/md5" |
|
18 | require "digest/md5" | |
19 |
|
19 | |||
20 | class Attachment < ActiveRecord::Base |
|
20 | class Attachment < ActiveRecord::Base | |
21 | belongs_to :container, :polymorphic => true |
|
21 | belongs_to :container, :polymorphic => true | |
22 | belongs_to :author, :class_name => "User", :foreign_key => "author_id" |
|
22 | belongs_to :author, :class_name => "User", :foreign_key => "author_id" | |
23 |
|
23 | |||
24 | validates_presence_of :container, :filename |
|
24 | validates_presence_of :container, :filename | |
25 |
|
25 | |||
26 | cattr_accessor :storage_path |
|
26 | cattr_accessor :storage_path | |
27 | @@storage_path = "#{RAILS_ROOT}/files" |
|
27 | @@storage_path = "#{RAILS_ROOT}/files" | |
28 |
|
28 | |||
29 | def validate |
|
29 | def validate | |
30 | errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes |
|
30 | errors.add_to_base :too_long if self.filesize > Setting.attachment_max_size.to_i.kilobytes | |
31 | end |
|
31 | end | |
32 |
|
32 | |||
33 | def file=(incomming_file) |
|
33 | def file=(incomming_file) | |
34 | unless incomming_file.nil? |
|
34 | unless incomming_file.nil? | |
35 | @temp_file = incomming_file |
|
35 | @temp_file = incomming_file | |
36 | if @temp_file.size > 0 |
|
36 | if @temp_file.size > 0 | |
37 | self.filename = sanitize_filename(@temp_file.original_filename) |
|
37 | self.filename = sanitize_filename(@temp_file.original_filename) | |
38 | self.disk_filename = DateTime.now.strftime("%y%m%d%H%M%S") + "_" + self.filename |
|
38 | self.disk_filename = DateTime.now.strftime("%y%m%d%H%M%S") + "_" + self.filename | |
39 | self.content_type = @temp_file.content_type |
|
39 | self.content_type = @temp_file.content_type | |
40 | self.filesize = @temp_file.size |
|
40 | self.filesize = @temp_file.size | |
41 | end |
|
41 | end | |
42 | end |
|
42 | end | |
43 | end |
|
43 | end | |
44 |
|
44 | |||
45 | def file |
|
45 | def file | |
46 | nil |
|
46 | nil | |
47 | end |
|
47 | end | |
48 |
|
48 | |||
49 | # Copy temp file to its final location |
|
49 | # Copy temp file to its final location | |
50 | def before_save |
|
50 | def before_save | |
51 | if @temp_file && (@temp_file.size > 0) |
|
51 | if @temp_file && (@temp_file.size > 0) | |
52 | logger.debug("saving '#{self.diskfile}'") |
|
52 | logger.debug("saving '#{self.diskfile}'") | |
53 | File.open(diskfile, "wb") do |f| |
|
53 | File.open(diskfile, "wb") do |f| | |
54 | f.write(@temp_file.read) |
|
54 | f.write(@temp_file.read) | |
55 | end |
|
55 | end | |
56 | self.digest = Digest::MD5.hexdigest(File.read(diskfile)) |
|
56 | self.digest = Digest::MD5.hexdigest(File.read(diskfile)) | |
57 | end |
|
57 | end | |
58 | end |
|
58 | end | |
59 |
|
59 | |||
60 | # Deletes file on the disk |
|
60 | # Deletes file on the disk | |
61 | def after_destroy |
|
61 | def after_destroy | |
62 | if self.filename? |
|
62 | if self.filename? | |
63 | File.delete(diskfile) if File.exist?(diskfile) |
|
63 | File.delete(diskfile) if File.exist?(diskfile) | |
64 | end |
|
64 | end | |
65 | end |
|
65 | end | |
66 |
|
66 | |||
67 | # Returns file's location on disk |
|
67 | # Returns file's location on disk | |
68 | def diskfile |
|
68 | def diskfile | |
69 | "#{@@storage_path}/#{self.disk_filename}" |
|
69 | "#{@@storage_path}/#{self.disk_filename}" | |
70 | end |
|
70 | end | |
71 |
|
71 | |||
72 | def increment_download |
|
72 | def increment_download | |
73 | increment!(:downloads) |
|
73 | increment!(:downloads) | |
74 | end |
|
74 | end | |
75 |
|
75 | |||
76 | # returns last created projects |
|
76 | # returns last created projects | |
77 | def self.most_downloaded |
|
77 | def self.most_downloaded | |
78 | find(:all, :limit => 5, :order => "downloads DESC") |
|
78 | find(:all, :limit => 5, :order => "downloads DESC") | |
79 | end |
|
79 | end | |
80 |
|
80 | |||
|
81 | def project | |||
|
82 | container.is_a?(Project) ? container : container.project | |||
|
83 | end | |||
|
84 | ||||
81 | private |
|
85 | private | |
82 | def sanitize_filename(value) |
|
86 | def sanitize_filename(value) | |
83 | # get only the filename, not the whole path |
|
87 | # get only the filename, not the whole path | |
84 | just_filename = value.gsub(/^.*(\\|\/)/, '') |
|
88 | just_filename = value.gsub(/^.*(\\|\/)/, '') | |
85 | # NOTE: File.basename doesn't work right with Windows paths on Unix |
|
89 | # NOTE: File.basename doesn't work right with Windows paths on Unix | |
86 | # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/')) |
|
90 | # INCORRECT: just_filename = File.basename(value.gsub('\\\\', '/')) | |
87 |
|
91 | |||
88 | # Finally, replace all non alphanumeric, underscore or periods with underscore |
|
92 | # Finally, replace all non alphanumeric, underscore or periods with underscore | |
89 | @filename = just_filename.gsub(/[^\w\.\-]/,'_') |
|
93 | @filename = just_filename.gsub(/[^\w\.\-]/,'_') | |
90 | end |
|
94 | end | |
91 |
|
95 | |||
92 | end |
|
96 | end |
@@ -1,37 +1,41 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class Message < ActiveRecord::Base |
|
18 | class Message < ActiveRecord::Base | |
19 | belongs_to :board |
|
19 | belongs_to :board | |
20 | belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' |
|
20 | belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' | |
21 | acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC" |
|
21 | acts_as_tree :counter_cache => :replies_count, :order => "#{Message.table_name}.created_on ASC" | |
22 | has_many :attachments, :as => :container, :dependent => :destroy |
|
22 | has_many :attachments, :as => :container, :dependent => :destroy | |
23 | belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id' |
|
23 | belongs_to :last_reply, :class_name => 'Message', :foreign_key => 'last_reply_id' | |
24 |
|
24 | |||
25 | validates_presence_of :subject, :content |
|
25 | validates_presence_of :subject, :content | |
26 | validates_length_of :subject, :maximum => 255 |
|
26 | validates_length_of :subject, :maximum => 255 | |
27 |
|
27 | |||
28 | def after_create |
|
28 | def after_create | |
29 | board.update_attribute(:last_message_id, self.id) |
|
29 | board.update_attribute(:last_message_id, self.id) | |
30 | board.increment! :messages_count |
|
30 | board.increment! :messages_count | |
31 | if parent |
|
31 | if parent | |
32 | parent.reload.update_attribute(:last_reply_id, self.id) |
|
32 | parent.reload.update_attribute(:last_reply_id, self.id) | |
33 | else |
|
33 | else | |
34 | board.increment! :topics_count |
|
34 | board.increment! :topics_count | |
35 | end |
|
35 | end | |
36 | end |
|
36 | end | |
|
37 | ||||
|
38 | def project | |||
|
39 | board.project | |||
|
40 | end | |||
37 | end |
|
41 | end |
@@ -1,45 +1,46 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class Wiki < ActiveRecord::Base |
|
18 | class Wiki < ActiveRecord::Base | |
19 | belongs_to :project |
|
19 | belongs_to :project | |
20 | has_many :pages, :class_name => 'WikiPage', :dependent => :destroy |
|
20 | has_many :pages, :class_name => 'WikiPage', :dependent => :destroy | |
21 |
|
21 | |||
22 | validates_presence_of :start_page |
|
22 | validates_presence_of :start_page | |
23 | validates_format_of :start_page, :with => /^[^,\.\/\?\;\|]*$/ |
|
23 | validates_format_of :start_page, :with => /^[^,\.\/\?\;\|]*$/ | |
24 |
|
24 | |||
25 | # find the page with the given title |
|
25 | # find the page with the given title | |
26 | # if page doesn't exist, return a new page |
|
26 | # if page doesn't exist, return a new page | |
27 | def find_or_new_page(title) |
|
27 | def find_or_new_page(title) | |
28 | title = Wiki.titleize(title || start_page) |
|
28 | title = Wiki.titleize(title || start_page) | |
29 | find_page(title) || WikiPage.new(:wiki => self, :title => title) |
|
29 | find_page(title) || WikiPage.new(:wiki => self, :title => title) | |
30 | end |
|
30 | end | |
31 |
|
31 | |||
32 | # find the page with the given title |
|
32 | # find the page with the given title | |
33 | def find_page(title) |
|
33 | def find_page(title) | |
34 | pages.find_by_title(Wiki.titleize(title || start_page)) |
|
34 | title = start_page if title.blank? | |
|
35 | pages.find_by_title(Wiki.titleize(title)) | |||
35 | end |
|
36 | end | |
36 |
|
37 | |||
37 | # turn a string into a valid page title |
|
38 | # turn a string into a valid page title | |
38 | def self.titleize(title) |
|
39 | def self.titleize(title) | |
39 | # replace spaces with _ and remove unwanted caracters |
|
40 | # replace spaces with _ and remove unwanted caracters | |
40 | title = title.gsub(/\s+/, '_').delete(',./?;|') if title |
|
41 | title = title.gsub(/\s+/, '_').delete(',./?;|') if title | |
41 | # upcase the first letter |
|
42 | # upcase the first letter | |
42 | title = title[0..0].upcase + title[1..-1] if title |
|
43 | title = title[0..0].upcase + title[1..-1] if title | |
43 | title |
|
44 | title | |
44 | end |
|
45 | end | |
45 | end |
|
46 | end |
@@ -1,44 +1,49 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program; if not, write to the Free Software |
|
15 | # along with this program; if not, write to the Free Software | |
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class WikiPage < ActiveRecord::Base |
|
18 | class WikiPage < ActiveRecord::Base | |
19 | belongs_to :wiki |
|
19 | belongs_to :wiki | |
20 | has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy |
|
20 | has_one :content, :class_name => 'WikiContent', :foreign_key => 'page_id', :dependent => :destroy | |
|
21 | has_many :attachments, :as => :container, :dependent => :destroy | |||
21 |
|
22 | |||
22 | validates_presence_of :title |
|
23 | validates_presence_of :title | |
23 | validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/ |
|
24 | validates_format_of :title, :with => /^[^,\.\/\?\;\|\s]*$/ | |
24 | validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false |
|
25 | validates_uniqueness_of :title, :scope => :wiki_id, :case_sensitive => false | |
25 | validates_associated :content |
|
26 | validates_associated :content | |
26 |
|
27 | |||
27 | def before_save |
|
28 | def before_save | |
28 | self.title = Wiki.titleize(title) |
|
29 | self.title = Wiki.titleize(title) | |
29 | end |
|
30 | end | |
30 |
|
31 | |||
31 | def pretty_title |
|
32 | def pretty_title | |
32 | WikiPage.pretty_title(title) |
|
33 | WikiPage.pretty_title(title) | |
33 | end |
|
34 | end | |
34 |
|
35 | |||
35 | def content_for_version(version=nil) |
|
36 | def content_for_version(version=nil) | |
36 | result = content.versions.find_by_version(version.to_i) if version |
|
37 | result = content.versions.find_by_version(version.to_i) if version | |
37 | result ||= content |
|
38 | result ||= content | |
38 | result |
|
39 | result | |
39 | end |
|
40 | end | |
40 |
|
41 | |||
41 | def self.pretty_title(str) |
|
42 | def self.pretty_title(str) | |
42 | (str && str.is_a?(String)) ? str.tr('_', ' ') : str |
|
43 | (str && str.is_a?(String)) ? str.tr('_', ' ') : str | |
43 | end |
|
44 | end | |
|
45 | ||||
|
46 | def project | |||
|
47 | wiki.project | |||
|
48 | end | |||
44 | end |
|
49 | end |
@@ -1,125 +1,115 | |||||
1 | <div class="contextual"> |
|
1 | <div class="contextual"> | |
2 | <%= l(:label_export_to) %><%= link_to 'PDF', {:action => 'export_pdf', :id => @issue}, :class => 'icon icon-pdf' %> |
|
2 | <%= l(:label_export_to) %><%= link_to 'PDF', {:action => 'export_pdf', :id => @issue}, :class => 'icon icon-pdf' %> | |
3 | </div> |
|
3 | </div> | |
4 |
|
4 | |||
5 | <h2><%= @issue.tracker.name %> #<%= @issue.id %> - <%=h @issue.subject %></h2> |
|
5 | <h2><%= @issue.tracker.name %> #<%= @issue.id %> - <%=h @issue.subject %></h2> | |
6 |
|
6 | |||
7 | <div class="box"> |
|
7 | <div class="box"> | |
8 | <table width="100%"> |
|
8 | <table width="100%"> | |
9 | <tr> |
|
9 | <tr> | |
10 | <td style="width:15%"><b><%=l(:field_status)%> :</b></td><td style="width:35%"><%= @issue.status.name %></td> |
|
10 | <td style="width:15%"><b><%=l(:field_status)%> :</b></td><td style="width:35%"><%= @issue.status.name %></td> | |
11 | <td style="width:15%"><b><%=l(:field_priority)%> :</b></td><td style="width:35%"><%= @issue.priority.name %></td> |
|
11 | <td style="width:15%"><b><%=l(:field_priority)%> :</b></td><td style="width:35%"><%= @issue.priority.name %></td> | |
12 | </tr> |
|
12 | </tr> | |
13 | <tr> |
|
13 | <tr> | |
14 | <td><b><%=l(:field_assigned_to)%> :</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td> |
|
14 | <td><b><%=l(:field_assigned_to)%> :</b></td><td><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td> | |
15 | <td><b><%=l(:field_category)%> :</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td> |
|
15 | <td><b><%=l(:field_category)%> :</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td> | |
16 | </tr> |
|
16 | </tr> | |
17 | <tr> |
|
17 | <tr> | |
18 | <td><b><%=l(:field_author)%> :</b></td><td><%= link_to_user @issue.author %></td> |
|
18 | <td><b><%=l(:field_author)%> :</b></td><td><%= link_to_user @issue.author %></td> | |
19 | <td><b><%=l(:field_start_date)%> :</b></td><td><%= format_date(@issue.start_date) %></td> |
|
19 | <td><b><%=l(:field_start_date)%> :</b></td><td><%= format_date(@issue.start_date) %></td> | |
20 | </tr> |
|
20 | </tr> | |
21 | <tr> |
|
21 | <tr> | |
22 | <td><b><%=l(:field_created_on)%> :</b></td><td><%= format_date(@issue.created_on) %></td> |
|
22 | <td><b><%=l(:field_created_on)%> :</b></td><td><%= format_date(@issue.created_on) %></td> | |
23 | <td><b><%=l(:field_due_date)%> :</b></td><td><%= format_date(@issue.due_date) %></td> |
|
23 | <td><b><%=l(:field_due_date)%> :</b></td><td><%= format_date(@issue.due_date) %></td> | |
24 | </tr> |
|
24 | </tr> | |
25 | <tr> |
|
25 | <tr> | |
26 | <td><b><%=l(:field_updated_on)%> :</b></td><td><%= format_date(@issue.updated_on) %></td> |
|
26 | <td><b><%=l(:field_updated_on)%> :</b></td><td><%= format_date(@issue.updated_on) %></td> | |
27 | <td><b><%=l(:field_done_ratio)%> :</b></td><td><%= @issue.done_ratio %> %</td> |
|
27 | <td><b><%=l(:field_done_ratio)%> :</b></td><td><%= @issue.done_ratio %> %</td> | |
28 | </tr> |
|
28 | </tr> | |
29 | <tr> |
|
29 | <tr> | |
30 | <td><b><%=l(:field_fixed_version)%> :</b></td><td><%= @issue.fixed_version ? @issue.fixed_version.name : "-" %></td> |
|
30 | <td><b><%=l(:field_fixed_version)%> :</b></td><td><%= @issue.fixed_version ? @issue.fixed_version.name : "-" %></td> | |
31 | <td><b><%=l(:label_spent_time)%> :</b></td> |
|
31 | <td><b><%=l(:label_spent_time)%> :</b></td> | |
32 | <td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td> |
|
32 | <td><%= @issue.spent_hours > 0 ? (link_to lwr(:label_f_hour, @issue.spent_hours), {:controller => 'timelog', :action => 'details', :issue_id => @issue}, :class => 'icon icon-time') : "-" %></td> | |
33 | </tr> |
|
33 | </tr> | |
34 | <tr> |
|
34 | <tr> | |
35 | <% n = 0 |
|
35 | <% n = 0 | |
36 | for custom_value in @custom_values %> |
|
36 | for custom_value in @custom_values %> | |
37 | <td><b><%= custom_value.custom_field.name %> :</b></td><td><%= h(show_value(custom_value)) %></td> |
|
37 | <td><b><%= custom_value.custom_field.name %> :</b></td><td><%= h(show_value(custom_value)) %></td> | |
38 | <% n = n + 1 |
|
38 | <% n = n + 1 | |
39 | if (n > 1) |
|
39 | if (n > 1) | |
40 | n = 0 %> |
|
40 | n = 0 %> | |
41 | </tr><tr> |
|
41 | </tr><tr> | |
42 | <%end |
|
42 | <%end | |
43 | end %> |
|
43 | end %> | |
44 | </tr> |
|
44 | </tr> | |
45 | </table> |
|
45 | </table> | |
46 | <hr /> |
|
46 | <hr /> | |
47 |
|
47 | |||
48 | <% if @issue.changesets.any? %> |
|
48 | <% if @issue.changesets.any? %> | |
49 | <div style="float:right;"> |
|
49 | <div style="float:right;"> | |
50 | <em><%= l(:label_revision_plural) %>: <%= @issue.changesets.collect{|changeset| link_to(changeset.revision, :controller => 'repositories', :action => 'revision', :id => @project, :rev => changeset.revision)}.join(", ") %></em> |
|
50 | <em><%= l(:label_revision_plural) %>: <%= @issue.changesets.collect{|changeset| link_to(changeset.revision, :controller => 'repositories', :action => 'revision', :id => @project, :rev => changeset.revision)}.join(", ") %></em> | |
51 | </div> |
|
51 | </div> | |
52 | <% end %> |
|
52 | <% end %> | |
53 |
|
53 | |||
54 | <b><%=l(:field_description)%> :</b><br /><br /> |
|
54 | <b><%=l(:field_description)%> :</b><br /><br /> | |
55 | <%= textilizable @issue.description %> |
|
55 | <%= textilizable @issue.description, :attachments => @issue.attachments %> | |
56 | <br /> |
|
56 | <br /> | |
57 |
|
57 | |||
58 | <div class="contextual"> |
|
58 | <div class="contextual"> | |
59 | <%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit' %> |
|
59 | <%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit' %> | |
60 | <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %> |
|
60 | <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %> | |
61 | <%= watcher_tag(@issue, @logged_in_user) %> |
|
61 | <%= watcher_tag(@issue, @logged_in_user) %> | |
62 | <%= link_to_if_authorized l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, :class => 'icon icon-move' %> |
|
62 | <%= link_to_if_authorized l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, :class => 'icon icon-move' %> | |
63 | <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> |
|
63 | <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %> | |
64 | </div> |
|
64 | </div> | |
65 |
|
65 | |||
66 | <% if authorize_for('issues', 'change_status') and @status_options and !@status_options.empty? %> |
|
66 | <% if authorize_for('issues', 'change_status') and @status_options and !@status_options.empty? %> | |
67 | <% form_tag({:controller => 'issues', :action => 'change_status', :id => @issue}) do %> |
|
67 | <% form_tag({:controller => 'issues', :action => 'change_status', :id => @issue}) do %> | |
68 | <%=l(:label_change_status)%> : |
|
68 | <%=l(:label_change_status)%> : | |
69 | <select name="new_status_id"> |
|
69 | <select name="new_status_id"> | |
70 | <%= options_from_collection_for_select @status_options, "id", "name" %> |
|
70 | <%= options_from_collection_for_select @status_options, "id", "name" %> | |
71 | </select> |
|
71 | </select> | |
72 | <%= submit_tag l(:button_change) %> |
|
72 | <%= submit_tag l(:button_change) %> | |
73 | <% end %> |
|
73 | <% end %> | |
74 | <% end %> |
|
74 | <% end %> | |
75 | |
|
75 | | |
76 | </div> |
|
76 | </div> | |
77 |
|
77 | |||
78 | <% if authorize_for('issue_relations', 'new') || @issue.relations.any? %> |
|
78 | <% if authorize_for('issue_relations', 'new') || @issue.relations.any? %> | |
79 | <div id="relations" class="box"> |
|
79 | <div id="relations" class="box"> | |
80 | <%= render :partial => 'relations' %> |
|
80 | <%= render :partial => 'relations' %> | |
81 | </div> |
|
81 | </div> | |
82 | <% end %> |
|
82 | <% end %> | |
83 |
|
83 | |||
84 | <div id="history" class="box"> |
|
84 | <div id="history" class="box"> | |
85 | <h3><%=l(:label_history)%> |
|
85 | <h3><%=l(:label_history)%> | |
86 | <% if @journals_count > @journals.length %>(<%= l(:label_last_changes, @journals.length) %>)<% end %></h3> |
|
86 | <% if @journals_count > @journals.length %>(<%= l(:label_last_changes, @journals.length) %>)<% end %></h3> | |
87 | <%= render :partial => 'history', :locals => { :journals => @journals } %> |
|
87 | <%= render :partial => 'history', :locals => { :journals => @journals } %> | |
88 | <% if @journals_count > @journals.length %> |
|
88 | <% if @journals_count > @journals.length %> | |
89 | <p><center><small><%= link_to l(:label_change_view_all), :action => 'history', :id => @issue %></small></center></p> |
|
89 | <p><center><small><%= link_to l(:label_change_view_all), :action => 'history', :id => @issue %></small></center></p> | |
90 | <% end %> |
|
90 | <% end %> | |
91 | </div> |
|
91 | </div> | |
92 |
|
92 | |||
93 | <div class="box"> |
|
93 | <div class="box"> | |
94 | <h3><%=l(:label_attachment_plural)%></h3> |
|
94 | <h3><%=l(:label_attachment_plural)%></h3> | |
95 | <table width="100%"> |
|
95 | <%= link_to_attachments @issue.attachments, :delete_url => (authorize_for('issues', 'destroy_attachment') ? {:controller => 'issues', :action => 'destroy_attachment', :id => @issue} : nil) %> | |
96 | <% for attachment in @issue.attachments %> |
|
96 | ||
97 | <tr> |
|
|||
98 | <td><%= link_to attachment.filename, { :action => 'download', :id => @issue, :attachment_id => attachment }, :class => 'icon icon-attachment' %> (<%= number_to_human_size(attachment.filesize) %>)</td> |
|
|||
99 | <td><%= format_date(attachment.created_on) %></td> |
|
|||
100 | <td><%= attachment.author.display_name %></td> |
|
|||
101 | <td><div class="contextual"><%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy_attachment', :id => @issue, :attachment_id => attachment }, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' %></div></td> |
|
|||
102 | </tr> |
|
|||
103 | <% end %> |
|
|||
104 | </table> |
|
|||
105 | <br /> |
|
|||
106 | <% if authorize_for('issues', 'add_attachment') %> |
|
97 | <% if authorize_for('issues', 'add_attachment') %> | |
107 | <% form_tag({ :controller => 'issues', :action => 'add_attachment', :id => @issue }, :multipart => true, :class => "tabular") do %> |
|
98 | <p><%= toggle_link l(:label_attachment_new), "add_attachment_form" %></p> | |
108 | <p id="attachments_p"><label><%=l(:label_attachment_new)%> |
|
99 | <% form_tag({ :controller => 'issues', :action => 'add_attachment', :id => @issue }, :multipart => true, :class => "tabular", :id => "add_attachment_form", :style => "display:none;") do %> | |
109 | <%= image_to_function "add.png", "addFileField();return false" %></label> |
|
100 | <%= render :partial => 'attachments/form' %> | |
110 | <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p> |
|
101 | <%= submit_tag l(:button_add) %> | |
111 | <%= submit_tag l(:button_add) %> |
|
102 | <% end %> | |
112 | <% end %> |
|
|||
113 | <% end %> |
|
103 | <% end %> | |
114 | </div> |
|
104 | </div> | |
115 |
|
105 | |||
116 | <% if authorize_for('issues', 'add_note') %> |
|
106 | <% if authorize_for('issues', 'add_note') %> | |
117 | <div class="box"> |
|
107 | <div class="box"> | |
118 | <h3><%= l(:label_add_note) %></h3> |
|
108 | <h3><%= l(:label_add_note) %></h3> | |
119 | <% form_tag({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) do %> |
|
109 | <% form_tag({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) do %> | |
120 | <p><label for="notes"><%=l(:field_notes)%></label> |
|
110 | <p><label for="notes"><%=l(:field_notes)%></label> | |
121 | <%= text_area_tag 'notes', '', :cols => 60, :rows => 10, :class => 'wiki-edit' %></p> |
|
111 | <%= text_area_tag 'notes', '', :cols => 60, :rows => 10, :class => 'wiki-edit' %></p> | |
122 | <%= submit_tag l(:button_add) %> |
|
112 | <%= submit_tag l(:button_add) %> | |
123 | <% end %> |
|
113 | <% end %> | |
124 | </div> |
|
114 | </div> | |
125 | <% end %> |
|
115 | <% end %> |
@@ -1,17 +1,14 | |||||
1 | <%= error_messages_for 'message' %> |
|
1 | <%= error_messages_for 'message' %> | |
2 |
|
2 | |||
3 | <div class="box"> |
|
3 | <div class="box"> | |
4 | <!--[form:message]--> |
|
4 | <!--[form:message]--> | |
5 | <p><label><%= l(:field_subject) %></label><br /> |
|
5 | <p><label><%= l(:field_subject) %></label><br /> | |
6 | <%= f.text_field :subject, :required => true, :size => 80 %></p> |
|
6 | <%= f.text_field :subject, :required => true, :size => 80 %></p> | |
7 |
|
7 | |||
8 | <p><%= f.text_area :content, :required => true, :cols => 80, :rows => 15 %></p> |
|
8 | <p><%= f.text_area :content, :required => true, :cols => 80, :rows => 15 %></p> | |
9 | <%= wikitoolbar_for 'message_content' %> |
|
9 | <%= wikitoolbar_for 'message_content' %> | |
10 | <!--[eoform:message]--> |
|
10 | <!--[eoform:message]--> | |
11 |
|
11 | |||
12 | <span class="tabular"> |
|
12 | <span class="tabular"> | |
13 | <p id="attachments_p"><label><%=l(:label_attachment)%> |
|
13 | <%= render :partial => 'attachments/form' %> | |
14 | <%= image_to_function "add.png", "addFileField();return false" %></label> |
|
|||
15 | <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= number_to_human_size(Setting.attachment_max_size.to_i.kilobytes) %>)</em></p> |
|
|||
16 | </span> |
|
|||
17 | </div> |
|
14 | </div> |
@@ -1,31 +1,26 | |||||
1 | <h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> » <%=h @message.subject %></h2> |
|
1 | <h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> » <%=h @message.subject %></h2> | |
2 |
|
2 | |||
3 | <p><em><%= @message.author.name %>, <%= format_time(@message.created_on) %></em></p> |
|
3 | <p><em><%= @message.author.name %>, <%= format_time(@message.created_on) %></em></p> | |
4 | <div class="wiki"> |
|
4 | <div class="wiki"> | |
5 | <%= textilizable(@message.content) %> |
|
5 | <%= textilizable(@message.content, :attachments => @message.attachments) %> | |
6 | </div> |
|
6 | </div> | |
7 | <div class="attachments"> |
|
7 | <%= link_to_attachments @message.attachments, :no_author => true %> | |
8 | <% @message.attachments.each do |attachment| %> |
|
8 | ||
9 | <%= link_to attachment.filename, { :action => 'download', :id => @message, :attachment_id => attachment }, :class => 'icon icon-attachment' %> |
|
|||
10 | (<%= number_to_human_size(attachment.filesize) %>)<br /> |
|
|||
11 | <% end %> |
|
|||
12 | </div> |
|
|||
13 | <br /> |
|
|||
14 | <h3 class="icon22 icon22-comment"><%= l(:label_reply_plural) %></h3> |
|
9 | <h3 class="icon22 icon22-comment"><%= l(:label_reply_plural) %></h3> | |
15 | <% @message.children.each do |message| %> |
|
10 | <% @message.children.each do |message| %> | |
16 | <a name="<%= "message-#{message.id}" %>"></a> |
|
11 | <a name="<%= "message-#{message.id}" %>"></a> | |
17 | <h4><%=h message.subject %> - <%= message.author.name %>, <%= format_time(message.created_on) %></h4> |
|
12 | <h4><%=h message.subject %> - <%= message.author.name %>, <%= format_time(message.created_on) %></h4> | |
18 | <div class="wiki"><p><%= textilizable message.content %></p></div> |
|
13 | <div class="wiki"><p><%= textilizable message.content %></p></div> | |
19 | <% end %> |
|
14 | <% end %> | |
20 |
|
15 | |||
21 | <% if @logged_in_user %> |
|
16 | <% if @logged_in_user %> | |
22 | <p><%= toggle_link l(:button_reply), "reply", :focus => "reply_content" %></p> |
|
17 | <p><%= toggle_link l(:button_reply), "reply", :focus => "reply_content" %></p> | |
23 | <div id="reply" style="display:none;"> |
|
18 | <div id="reply" style="display:none;"> | |
24 | <%= error_messages_for 'message' %> |
|
19 | <%= error_messages_for 'message' %> | |
25 | <% form_for :reply, @reply, :url => {:action => 'reply', :id => @message} do |f| %> |
|
20 | <% form_for :reply, @reply, :url => {:action => 'reply', :id => @message} do |f| %> | |
26 | <p><%= f.text_field :subject, :required => true, :size => 60 %></p> |
|
21 | <p><%= f.text_field :subject, :required => true, :size => 60 %></p> | |
27 | <p><%= f.text_area :content, :required => true, :cols => 80, :rows => 10 %></p> |
|
22 | <p><%= f.text_area :content, :required => true, :cols => 80, :rows => 10 %></p> | |
28 | <p><%= submit_tag l(:button_submit) %></p> |
|
23 | <p><%= submit_tag l(:button_submit) %></p> | |
29 | <% end %> |
|
24 | <% end %> | |
30 | </div> |
|
25 | </div> | |
31 | <% end %> No newline at end of file |
|
26 | <% end %> |
@@ -1,3 +1,3 | |||||
1 | <fieldset class="preview"><legend><%= l(:label_preview) %></legend> |
|
1 | <fieldset class="preview"><legend><%= l(:label_preview) %></legend> | |
2 | <%= textilizable @text %> |
|
2 | <%= textilizable @text, :attachments => @attachements %> | |
3 | </fieldset> |
|
3 | </fieldset> |
@@ -1,32 +1,42 | |||||
1 | <div class="contextual"> |
|
1 | <div class="contextual"> | |
2 | <%= link_to(l(:button_edit), {:action => 'edit', :page => @page.title}, :class => 'icon icon-edit') if @content.version == @page.content.version %> |
|
2 | <%= link_to(l(:button_edit), {:action => 'edit', :page => @page.title}, :class => 'icon icon-edit') if @content.version == @page.content.version %> | |
3 | <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :page => @page.title}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %> |
|
3 | <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :page => @page.title}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %> | |
4 | <%= link_to(l(:button_rollback), {:action => 'edit', :page => @page.title, :version => @content.version }, :class => 'icon icon-cancel') if @content.version < @page.content.version %> |
|
4 | <%= link_to(l(:button_rollback), {:action => 'edit', :page => @page.title, :version => @content.version }, :class => 'icon icon-cancel') if @content.version < @page.content.version %> | |
5 | <%= link_to(l(:label_history), {:action => 'history', :page => @page.title}, :class => 'icon icon-history') %> |
|
5 | <%= link_to(l(:label_history), {:action => 'history', :page => @page.title}, :class => 'icon icon-history') %> | |
6 | <%= link_to(l(:label_page_index), {:action => 'special', :page => 'Page_index'}, :class => 'icon icon-index') %> |
|
6 | <%= link_to(l(:label_page_index), {:action => 'special', :page => 'Page_index'}, :class => 'icon icon-index') %> | |
7 | </div> |
|
7 | </div> | |
8 |
|
8 | |||
9 | <% if @content.version != @page.content.version %> |
|
9 | <% if @content.version != @page.content.version %> | |
10 | <p> |
|
10 | <p> | |
11 | <%= link_to(('« ' + l(:label_previous)), :action => 'index', :page => @page.title, :version => (@content.version - 1)) + " - " if @content.version > 1 %> |
|
11 | <%= link_to(('« ' + l(:label_previous)), :action => 'index', :page => @page.title, :version => (@content.version - 1)) + " - " if @content.version > 1 %> | |
12 | <%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %> - |
|
12 | <%= "#{l(:label_version)} #{@content.version}/#{@page.content.version}" %> - | |
13 | <%= link_to((l(:label_next) + ' »'), :action => 'index', :page => @page.title, :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %> |
|
13 | <%= link_to((l(:label_next) + ' »'), :action => 'index', :page => @page.title, :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %> | |
14 | <%= link_to(l(:label_current_version), :action => 'index', :page => @page.title) %> |
|
14 | <%= link_to(l(:label_current_version), :action => 'index', :page => @page.title) %> | |
15 | <br /> |
|
15 | <br /> | |
16 | <em><%= @content.author ? @content.author.name : "anonyme" %>, <%= format_time(@content.updated_on) %> </em><br /> |
|
16 | <em><%= @content.author ? @content.author.name : "anonyme" %>, <%= format_time(@content.updated_on) %> </em><br /> | |
17 | <%=h @content.comments %> |
|
17 | <%=h @content.comments %> | |
18 | </p> |
|
18 | </p> | |
19 | <hr /> |
|
19 | <hr /> | |
20 | <% end %> |
|
20 | <% end %> | |
21 |
|
21 | |||
22 | <div class="wiki"> |
|
22 | <div class="wiki"> | |
23 | <% cache "wiki/show/#{@page.id}/#{@content.version}" do %> |
|
23 | <% cache "wiki/show/#{@page.id}/#{@content.version}" do %> | |
24 | <%= textilizable @content.text %> |
|
24 | <%= textilizable @content.text, :attachments => @page.attachments %> | |
25 | <% end %> |
|
25 | <% end %> | |
26 | </div> |
|
26 | </div> | |
27 |
|
27 | |||
|
28 | <%= link_to_attachments @page.attachments, :delete_url => (authorize_for('wiki', 'destroy_attachment') ? {:controller => 'wiki', :action => 'destroy_attachment', :page => @page.title} : nil) %> | |||
|
29 | ||||
28 | <div class="contextual"> |
|
30 | <div class="contextual"> | |
29 | <%= l(:label_export_to) %> |
|
31 | <%= l(:label_export_to) %> | |
30 | <%= link_to 'HTML', {:export => 'html', :version => @content.version}, :class => 'icon icon-html' %>, |
|
32 | <%= link_to 'HTML', {:export => 'html', :version => @content.version}, :class => 'icon icon-html' %>, | |
31 | <%= link_to 'TXT', {:export => 'txt', :version => @content.version}, :class => 'icon icon-txt' %> |
|
33 | <%= link_to 'TXT', {:export => 'txt', :version => @content.version}, :class => 'icon icon-txt' %> | |
32 | </div> No newline at end of file |
|
34 | </div> | |
|
35 | ||||
|
36 | <% if authorize_for('wiki', 'add_attachment') %> | |||
|
37 | <p><%= toggle_link l(:label_attachment_new), "add_attachment_form" %></p> | |||
|
38 | <% form_tag({ :controller => 'wiki', :action => 'add_attachment', :page => @page.title }, :multipart => true, :class => "tabular", :id => "add_attachment_form", :style => "display:none;") do %> | |||
|
39 | <%= render :partial => 'attachments/form' %> | |||
|
40 | <%= submit_tag l(:button_add) %> | |||
|
41 | <% end %> | |||
|
42 | <% end %> |
@@ -1,670 +1,671 | |||||
1 | /* andreas08 - an open source xhtml/css website layout by Andreas Viklund - http://andreasviklund.com . Free to use in any way and for any purpose as long as the proper credits are given to the original designer. Version: 1.0, November 28, 2005 */ |
|
1 | /* andreas08 - an open source xhtml/css website layout by Andreas Viklund - http://andreasviklund.com . Free to use in any way and for any purpose as long as the proper credits are given to the original designer. Version: 1.0, November 28, 2005 */ | |
2 | /* Edited by Jean-Philippe Lang *> |
|
2 | /* Edited by Jean-Philippe Lang *> | |
3 | /**************** Body and tag styles ****************/ |
|
3 | /**************** Body and tag styles ****************/ | |
4 |
|
4 | |||
5 | #header * {margin:0; padding:0;} |
|
5 | #header * {margin:0; padding:0;} | |
6 | p, ul, ol, li {margin:0; padding:0;} |
|
6 | p, ul, ol, li {margin:0; padding:0;} | |
7 |
|
7 | |||
8 | body{ |
|
8 | body{ | |
9 | font:76% Verdana,Tahoma,Arial,sans-serif; |
|
9 | font:76% Verdana,Tahoma,Arial,sans-serif; | |
10 | line-height:1.4em; |
|
10 | line-height:1.4em; | |
11 | text-align:center; |
|
11 | text-align:center; | |
12 | color:#303030; |
|
12 | color:#303030; | |
13 | background:#e8eaec; |
|
13 | background:#e8eaec; | |
14 | margin:0; |
|
14 | margin:0; | |
15 | } |
|
15 | } | |
16 |
|
16 | |||
17 | a{color:#467aa7;font-weight:bold;text-decoration:none;background-color:inherit;} |
|
17 | a{color:#467aa7;font-weight:bold;text-decoration:none;background-color:inherit;} | |
18 | a:hover{color:#2a5a8a; text-decoration:none; background-color:inherit;} |
|
18 | a:hover{color:#2a5a8a; text-decoration:none; background-color:inherit;} | |
19 | a img{border:none;} |
|
19 | a img{border:none;} | |
20 |
|
20 | |||
21 | p{margin:0 0 1em 0;} |
|
21 | p{margin:0 0 1em 0;} | |
22 | p form{margin-top:0; margin-bottom:20px;} |
|
22 | p form{margin-top:0; margin-bottom:20px;} | |
23 |
|
23 | |||
24 | img.left,img.center,img.right{padding:4px; border:1px solid #a0a0a0;} |
|
24 | img.left,img.center,img.right{padding:4px; border:1px solid #a0a0a0;} | |
25 | img.left{float:left; margin:0 12px 5px 0;} |
|
25 | img.left{float:left; margin:0 12px 5px 0;} | |
26 | img.center{display:block; margin:0 auto 5px auto;} |
|
26 | img.center{display:block; margin:0 auto 5px auto;} | |
27 | img.right{float:right; margin:0 0 5px 12px;} |
|
27 | img.right{float:right; margin:0 0 5px 12px;} | |
28 |
|
28 | |||
29 | /**************** Header and navigation styles ****************/ |
|
29 | /**************** Header and navigation styles ****************/ | |
30 |
|
30 | |||
31 | #container{ |
|
31 | #container{ | |
32 | width:100%; |
|
32 | width:100%; | |
33 | min-width: 800px; |
|
33 | min-width: 800px; | |
34 | margin:0; |
|
34 | margin:0; | |
35 | padding:0; |
|
35 | padding:0; | |
36 | text-align:left; |
|
36 | text-align:left; | |
37 | background:#ffffff; |
|
37 | background:#ffffff; | |
38 | color:#303030; |
|
38 | color:#303030; | |
39 | } |
|
39 | } | |
40 |
|
40 | |||
41 | #header{ |
|
41 | #header{ | |
42 | height:4.5em; |
|
42 | height:4.5em; | |
43 | margin:0; |
|
43 | margin:0; | |
44 | background:#467aa7; |
|
44 | background:#467aa7; | |
45 | color:#ffffff; |
|
45 | color:#ffffff; | |
46 | margin-bottom:1px; |
|
46 | margin-bottom:1px; | |
47 | } |
|
47 | } | |
48 |
|
48 | |||
49 | #header h1{ |
|
49 | #header h1{ | |
50 | padding:10px 0 0 20px; |
|
50 | padding:10px 0 0 20px; | |
51 | font-size:2em; |
|
51 | font-size:2em; | |
52 | background-color:inherit; |
|
52 | background-color:inherit; | |
53 | color:#fff; |
|
53 | color:#fff; | |
54 | letter-spacing:-1px; |
|
54 | letter-spacing:-1px; | |
55 | font-weight:bold; |
|
55 | font-weight:bold; | |
56 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; |
|
56 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; | |
57 | } |
|
57 | } | |
58 |
|
58 | |||
59 | #header h2{ |
|
59 | #header h2{ | |
60 | margin:3px 0 0 40px; |
|
60 | margin:3px 0 0 40px; | |
61 | font-size:1.5em; |
|
61 | font-size:1.5em; | |
62 | background-color:inherit; |
|
62 | background-color:inherit; | |
63 | color:#f0f2f4; |
|
63 | color:#f0f2f4; | |
64 | letter-spacing:-1px; |
|
64 | letter-spacing:-1px; | |
65 | font-weight:normal; |
|
65 | font-weight:normal; | |
66 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; |
|
66 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; | |
67 | } |
|
67 | } | |
68 |
|
68 | |||
69 | #header a {color:#fff;} |
|
69 | #header a {color:#fff;} | |
70 |
|
70 | |||
71 | #navigation{ |
|
71 | #navigation{ | |
72 | height:2.2em; |
|
72 | height:2.2em; | |
73 | line-height:2.2em; |
|
73 | line-height:2.2em; | |
74 | margin:0; |
|
74 | margin:0; | |
75 | background:#578bb8; |
|
75 | background:#578bb8; | |
76 | color:#ffffff; |
|
76 | color:#ffffff; | |
77 | } |
|
77 | } | |
78 |
|
78 | |||
79 | #navigation li{ |
|
79 | #navigation li{ | |
80 | float:left; |
|
80 | float:left; | |
81 | list-style-type:none; |
|
81 | list-style-type:none; | |
82 | border-right:1px solid #ffffff; |
|
82 | border-right:1px solid #ffffff; | |
83 | white-space:nowrap; |
|
83 | white-space:nowrap; | |
84 | } |
|
84 | } | |
85 |
|
85 | |||
86 | #navigation li.right { |
|
86 | #navigation li.right { | |
87 | float:right; |
|
87 | float:right; | |
88 | list-style-type:none; |
|
88 | list-style-type:none; | |
89 | border-right:0; |
|
89 | border-right:0; | |
90 | border-left:1px solid #ffffff; |
|
90 | border-left:1px solid #ffffff; | |
91 | white-space:nowrap; |
|
91 | white-space:nowrap; | |
92 | } |
|
92 | } | |
93 |
|
93 | |||
94 | #navigation li a{ |
|
94 | #navigation li a{ | |
95 | display:block; |
|
95 | display:block; | |
96 | padding:0px 10px 0px 22px; |
|
96 | padding:0px 10px 0px 22px; | |
97 | font-size:0.8em; |
|
97 | font-size:0.8em; | |
98 | font-weight:normal; |
|
98 | font-weight:normal; | |
99 | text-decoration:none; |
|
99 | text-decoration:none; | |
100 | background-color:inherit; |
|
100 | background-color:inherit; | |
101 | color: #ffffff; |
|
101 | color: #ffffff; | |
102 | } |
|
102 | } | |
103 |
|
103 | |||
104 | #navigation li.submenu {background:url(../images/arrow_down.png) 96% 80% no-repeat;} |
|
104 | #navigation li.submenu {background:url(../images/arrow_down.png) 96% 80% no-repeat;} | |
105 | #navigation li.submenu a {padding:0px 16px 0px 22px;} |
|
105 | #navigation li.submenu a {padding:0px 16px 0px 22px;} | |
106 | * html #navigation a {width:1%;} |
|
106 | * html #navigation a {width:1%;} | |
107 |
|
107 | |||
108 | #navigation .selected,#navigation a:hover{ |
|
108 | #navigation .selected,#navigation a:hover{ | |
109 | color:#ffffff; |
|
109 | color:#ffffff; | |
110 | text-decoration:none; |
|
110 | text-decoration:none; | |
111 | background-color: #80b0da; |
|
111 | background-color: #80b0da; | |
112 | } |
|
112 | } | |
113 |
|
113 | |||
114 | /**************** Icons *******************/ |
|
114 | /**************** Icons *******************/ | |
115 | .icon { |
|
115 | .icon { | |
116 | background-position: 0% 40%; |
|
116 | background-position: 0% 40%; | |
117 | background-repeat: no-repeat; |
|
117 | background-repeat: no-repeat; | |
118 | padding-left: 20px; |
|
118 | padding-left: 20px; | |
119 | padding-top: 2px; |
|
119 | padding-top: 2px; | |
120 | padding-bottom: 3px; |
|
120 | padding-bottom: 3px; | |
121 | vertical-align: middle; |
|
121 | vertical-align: middle; | |
122 | } |
|
122 | } | |
123 |
|
123 | |||
124 | #navigation .icon { |
|
124 | #navigation .icon { | |
125 | background-position: 4px 50%; |
|
125 | background-position: 4px 50%; | |
126 | } |
|
126 | } | |
127 |
|
127 | |||
128 | .icon22 { |
|
128 | .icon22 { | |
129 | background-position: 0% 40%; |
|
129 | background-position: 0% 40%; | |
130 | background-repeat: no-repeat; |
|
130 | background-repeat: no-repeat; | |
131 | padding-left: 26px; |
|
131 | padding-left: 26px; | |
132 | line-height: 22px; |
|
132 | line-height: 22px; | |
133 | vertical-align: middle; |
|
133 | vertical-align: middle; | |
134 | } |
|
134 | } | |
135 |
|
135 | |||
136 | .icon-add { background-image: url(../images/add.png); } |
|
136 | .icon-add { background-image: url(../images/add.png); } | |
137 | .icon-edit { background-image: url(../images/edit.png); } |
|
137 | .icon-edit { background-image: url(../images/edit.png); } | |
138 | .icon-del { background-image: url(../images/delete.png); } |
|
138 | .icon-del { background-image: url(../images/delete.png); } | |
139 | .icon-move { background-image: url(../images/move.png); } |
|
139 | .icon-move { background-image: url(../images/move.png); } | |
140 | .icon-save { background-image: url(../images/save.png); } |
|
140 | .icon-save { background-image: url(../images/save.png); } | |
141 | .icon-cancel { background-image: url(../images/cancel.png); } |
|
141 | .icon-cancel { background-image: url(../images/cancel.png); } | |
142 | .icon-pdf { background-image: url(../images/pdf.png); } |
|
142 | .icon-pdf { background-image: url(../images/pdf.png); } | |
143 | .icon-csv { background-image: url(../images/csv.png); } |
|
143 | .icon-csv { background-image: url(../images/csv.png); } | |
144 | .icon-html { background-image: url(../images/html.png); } |
|
144 | .icon-html { background-image: url(../images/html.png); } | |
145 | .icon-txt { background-image: url(../images/txt.png); } |
|
145 | .icon-txt { background-image: url(../images/txt.png); } | |
146 | .icon-file { background-image: url(../images/file.png); } |
|
146 | .icon-file { background-image: url(../images/file.png); } | |
147 | .icon-folder { background-image: url(../images/folder.png); } |
|
147 | .icon-folder { background-image: url(../images/folder.png); } | |
148 | .icon-package { background-image: url(../images/package.png); } |
|
148 | .icon-package { background-image: url(../images/package.png); } | |
149 | .icon-home { background-image: url(../images/home.png); } |
|
149 | .icon-home { background-image: url(../images/home.png); } | |
150 | .icon-user { background-image: url(../images/user.png); } |
|
150 | .icon-user { background-image: url(../images/user.png); } | |
151 | .icon-mypage { background-image: url(../images/user_page.png); } |
|
151 | .icon-mypage { background-image: url(../images/user_page.png); } | |
152 | .icon-admin { background-image: url(../images/admin.png); } |
|
152 | .icon-admin { background-image: url(../images/admin.png); } | |
153 | .icon-projects { background-image: url(../images/projects.png); } |
|
153 | .icon-projects { background-image: url(../images/projects.png); } | |
154 | .icon-logout { background-image: url(../images/logout.png); } |
|
154 | .icon-logout { background-image: url(../images/logout.png); } | |
155 | .icon-help { background-image: url(../images/help.png); } |
|
155 | .icon-help { background-image: url(../images/help.png); } | |
156 | .icon-attachment { background-image: url(../images/attachment.png); } |
|
156 | .icon-attachment { background-image: url(../images/attachment.png); } | |
157 | .icon-index { background-image: url(../images/index.png); } |
|
157 | .icon-index { background-image: url(../images/index.png); } | |
158 | .icon-history { background-image: url(../images/history.png); } |
|
158 | .icon-history { background-image: url(../images/history.png); } | |
159 | .icon-feed { background-image: url(../images/feed.png); } |
|
159 | .icon-feed { background-image: url(../images/feed.png); } | |
160 | .icon-time { background-image: url(../images/time.png); } |
|
160 | .icon-time { background-image: url(../images/time.png); } | |
161 | .icon-stats { background-image: url(../images/stats.png); } |
|
161 | .icon-stats { background-image: url(../images/stats.png); } | |
162 | .icon-warning { background-image: url(../images/warning.png); } |
|
162 | .icon-warning { background-image: url(../images/warning.png); } | |
163 | .icon-fav { background-image: url(../images/fav.png); } |
|
163 | .icon-fav { background-image: url(../images/fav.png); } | |
164 | .icon-fav-off { background-image: url(../images/fav_off.png); } |
|
164 | .icon-fav-off { background-image: url(../images/fav_off.png); } | |
165 | .icon-reload { background-image: url(../images/reload.png); } |
|
165 | .icon-reload { background-image: url(../images/reload.png); } | |
166 |
|
166 | |||
167 | .icon22-projects { background-image: url(../images/22x22/projects.png); } |
|
167 | .icon22-projects { background-image: url(../images/22x22/projects.png); } | |
168 | .icon22-users { background-image: url(../images/22x22/users.png); } |
|
168 | .icon22-users { background-image: url(../images/22x22/users.png); } | |
169 | .icon22-tracker { background-image: url(../images/22x22/tracker.png); } |
|
169 | .icon22-tracker { background-image: url(../images/22x22/tracker.png); } | |
170 | .icon22-role { background-image: url(../images/22x22/role.png); } |
|
170 | .icon22-role { background-image: url(../images/22x22/role.png); } | |
171 | .icon22-workflow { background-image: url(../images/22x22/workflow.png); } |
|
171 | .icon22-workflow { background-image: url(../images/22x22/workflow.png); } | |
172 | .icon22-options { background-image: url(../images/22x22/options.png); } |
|
172 | .icon22-options { background-image: url(../images/22x22/options.png); } | |
173 | .icon22-notifications { background-image: url(../images/22x22/notifications.png); } |
|
173 | .icon22-notifications { background-image: url(../images/22x22/notifications.png); } | |
174 | .icon22-authent { background-image: url(../images/22x22/authent.png); } |
|
174 | .icon22-authent { background-image: url(../images/22x22/authent.png); } | |
175 | .icon22-info { background-image: url(../images/22x22/info.png); } |
|
175 | .icon22-info { background-image: url(../images/22x22/info.png); } | |
176 | .icon22-comment { background-image: url(../images/22x22/comment.png); } |
|
176 | .icon22-comment { background-image: url(../images/22x22/comment.png); } | |
177 | .icon22-package { background-image: url(../images/22x22/package.png); } |
|
177 | .icon22-package { background-image: url(../images/22x22/package.png); } | |
178 | .icon22-settings { background-image: url(../images/22x22/settings.png); } |
|
178 | .icon22-settings { background-image: url(../images/22x22/settings.png); } | |
179 |
|
179 | |||
180 | /**************** Content styles ****************/ |
|
180 | /**************** Content styles ****************/ | |
181 |
|
181 | |||
182 | html>body #content { |
|
182 | html>body #content { | |
183 | height: auto; |
|
183 | height: auto; | |
184 | min-height: 500px; |
|
184 | min-height: 500px; | |
185 | } |
|
185 | } | |
186 |
|
186 | |||
187 | #content{ |
|
187 | #content{ | |
188 | width: auto; |
|
188 | width: auto; | |
189 | height:500px; |
|
189 | height:500px; | |
190 | font-size:0.9em; |
|
190 | font-size:0.9em; | |
191 | padding:20px 10px 10px 20px; |
|
191 | padding:20px 10px 10px 20px; | |
192 | margin-left: 120px; |
|
192 | margin-left: 120px; | |
193 | border-left: 1px dashed #c0c0c0; |
|
193 | border-left: 1px dashed #c0c0c0; | |
194 |
|
194 | |||
195 | } |
|
195 | } | |
196 |
|
196 | |||
197 | #content h2, #content div.wiki h1 { |
|
197 | #content h2, #content div.wiki h1 { | |
198 | display:block; |
|
198 | display:block; | |
199 | margin:0 0 16px 0; |
|
199 | margin:0 0 16px 0; | |
200 | font-size:1.7em; |
|
200 | font-size:1.7em; | |
201 | font-weight:normal; |
|
201 | font-weight:normal; | |
202 | letter-spacing:-1px; |
|
202 | letter-spacing:-1px; | |
203 | color:#606060; |
|
203 | color:#606060; | |
204 | background-color:inherit; |
|
204 | background-color:inherit; | |
205 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; |
|
205 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; | |
206 | } |
|
206 | } | |
207 |
|
207 | |||
208 | #content h2 a{font-weight:normal;} |
|
208 | #content h2 a{font-weight:normal;} | |
209 | #content h3{margin:0 0 12px 0; font-size:1.4em;color:#707070;font-family: Trebuchet MS,Georgia,"Times New Roman",serif;} |
|
209 | #content h3{margin:0 0 12px 0; font-size:1.4em;color:#707070;font-family: Trebuchet MS,Georgia,"Times New Roman",serif;} | |
210 | #content h4{font-size: 1em; margin-bottom: 12px; margin-top: 20px; font-weight: normal; border-bottom: dotted 1px #c0c0c0;} |
|
210 | #content h4{font-size: 1em; margin-bottom: 12px; margin-top: 20px; font-weight: normal; border-bottom: dotted 1px #c0c0c0;} | |
211 | #content a:hover,#subcontent a:hover{text-decoration:underline;} |
|
211 | #content a:hover,#subcontent a:hover{text-decoration:underline;} | |
212 | #content ul,#content ol{margin:0 5px 16px 35px;} |
|
212 | #content ul,#content ol{margin:0 5px 16px 35px;} | |
213 | #content dl{margin:0 5px 10px 25px;} |
|
213 | #content dl{margin:0 5px 10px 25px;} | |
214 | #content dt{font-weight:bold; margin-bottom:5px;} |
|
214 | #content dt{font-weight:bold; margin-bottom:5px;} | |
215 | #content dd{margin:0 0 10px 15px;} |
|
215 | #content dd{margin:0 0 10px 15px;} | |
216 |
|
216 | |||
217 | #content .tabs{height: 2.6em;} |
|
217 | #content .tabs{height: 2.6em;} | |
218 | #content .tabs ul{margin:0;} |
|
218 | #content .tabs ul{margin:0;} | |
219 | #content .tabs ul li{ |
|
219 | #content .tabs ul li{ | |
220 | float:left; |
|
220 | float:left; | |
221 | list-style-type:none; |
|
221 | list-style-type:none; | |
222 | white-space:nowrap; |
|
222 | white-space:nowrap; | |
223 | margin-right:8px; |
|
223 | margin-right:8px; | |
224 | background:#fff; |
|
224 | background:#fff; | |
225 | } |
|
225 | } | |
226 | #content .tabs ul li a{ |
|
226 | #content .tabs ul li a{ | |
227 | display:block; |
|
227 | display:block; | |
228 | font-size: 0.9em; |
|
228 | font-size: 0.9em; | |
229 | text-decoration:none; |
|
229 | text-decoration:none; | |
230 | line-height:1em; |
|
230 | line-height:1em; | |
231 | padding:4px; |
|
231 | padding:4px; | |
232 | border: 1px solid #c0c0c0; |
|
232 | border: 1px solid #c0c0c0; | |
233 | } |
|
233 | } | |
234 |
|
234 | |||
235 | #content .tabs ul li a.selected, #content .tabs ul li a:hover{ |
|
235 | #content .tabs ul li a.selected, #content .tabs ul li a:hover{ | |
236 | background-color: #80b0da; |
|
236 | background-color: #80b0da; | |
237 | border: 1px solid #80b0da; |
|
237 | border: 1px solid #80b0da; | |
238 | color: #fff; |
|
238 | color: #fff; | |
239 | text-decoration:none; |
|
239 | text-decoration:none; | |
240 | } |
|
240 | } | |
241 |
|
241 | |||
242 | /***********************************************/ |
|
242 | /***********************************************/ | |
243 |
|
243 | |||
244 | form {display: inline;} |
|
244 | form {display: inline;} | |
245 | blockquote {padding-left: 6px; border-left: 2px solid #ccc;} |
|
245 | blockquote {padding-left: 6px; border-left: 2px solid #ccc;} | |
246 | input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;} |
|
246 | input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;} | |
247 |
|
247 | |||
248 | input.button-small {font-size: 0.8em;} |
|
248 | input.button-small {font-size: 0.8em;} | |
249 | textarea.wiki-edit { width: 99.5%; } |
|
249 | textarea.wiki-edit { width: 99.5%; } | |
250 | .select-small {font-size: 0.8em;} |
|
250 | .select-small {font-size: 0.8em;} | |
251 | label {font-weight: bold; font-size: 1em; color: #505050;} |
|
251 | label {font-weight: bold; font-size: 1em; color: #505050;} | |
252 | fieldset {border:1px solid #c0c0c0; padding: 6px;} |
|
252 | fieldset {border:1px solid #c0c0c0; padding: 6px;} | |
253 | legend {color: #505050;} |
|
253 | legend {color: #505050;} | |
254 | .required {color: #bb0000;} |
|
254 | .required {color: #bb0000;} | |
255 | .odd {background-color:#f6f7f8;} |
|
255 | .odd {background-color:#f6f7f8;} | |
256 | .even {background-color: #fff;} |
|
256 | .even {background-color: #fff;} | |
257 | hr { border:0; border-top: dotted 1px #fff; border-bottom: dotted 1px #c0c0c0; } |
|
257 | hr { border:0; border-top: dotted 1px #fff; border-bottom: dotted 1px #c0c0c0; } | |
258 | table p {margin:0; padding:0;} |
|
258 | table p {margin:0; padding:0;} | |
259 |
|
259 | |||
260 | .highlight { background-color: #FCFD8D;} |
|
260 | .highlight { background-color: #FCFD8D;} | |
261 |
|
261 | |||
262 | div.square { |
|
262 | div.square { | |
263 | border: 1px solid #999; |
|
263 | border: 1px solid #999; | |
264 | float: left; |
|
264 | float: left; | |
265 | margin: .4em .5em 0 0; |
|
265 | margin: .4em .5em 0 0; | |
266 | overflow: hidden; |
|
266 | overflow: hidden; | |
267 | width: .6em; height: .6em; |
|
267 | width: .6em; height: .6em; | |
268 | } |
|
268 | } | |
269 |
|
269 | |||
270 | ul.documents { |
|
270 | ul.documents { | |
271 | list-style-type: none; |
|
271 | list-style-type: none; | |
272 | padding: 0; |
|
272 | padding: 0; | |
273 | margin: 0; |
|
273 | margin: 0; | |
274 | } |
|
274 | } | |
275 |
|
275 | |||
276 | ul.documents li { |
|
276 | ul.documents li { | |
277 | background-image: url(../images/32x32/file.png); |
|
277 | background-image: url(../images/32x32/file.png); | |
278 | background-repeat: no-repeat; |
|
278 | background-repeat: no-repeat; | |
279 | background-position: 0 1px; |
|
279 | background-position: 0 1px; | |
280 | padding-left: 36px; |
|
280 | padding-left: 36px; | |
281 | margin-bottom: 10px; |
|
281 | margin-bottom: 10px; | |
282 | margin-left: -37px; |
|
282 | margin-left: -37px; | |
283 | } |
|
283 | } | |
284 |
|
284 | |||
285 | /********** Table used to display lists of things ***********/ |
|
285 | /********** Table used to display lists of things ***********/ | |
286 |
|
286 | |||
287 | table.list { |
|
287 | table.list { | |
288 | width:100%; |
|
288 | width:100%; | |
289 | border-collapse: collapse; |
|
289 | border-collapse: collapse; | |
290 | border: 1px dotted #d0d0d0; |
|
290 | border: 1px dotted #d0d0d0; | |
291 | margin-bottom: 6px; |
|
291 | margin-bottom: 6px; | |
292 | } |
|
292 | } | |
293 |
|
293 | |||
294 | table.with-cells td { |
|
294 | table.with-cells td { | |
295 | border: 1px solid #d7d7d7; |
|
295 | border: 1px solid #d7d7d7; | |
296 | } |
|
296 | } | |
297 |
|
297 | |||
298 | table.list td { |
|
298 | table.list td { | |
299 | padding:2px; |
|
299 | padding:2px; | |
300 | } |
|
300 | } | |
301 |
|
301 | |||
302 | table.list thead th { |
|
302 | table.list thead th { | |
303 | text-align: center; |
|
303 | text-align: center; | |
304 | background: #eee; |
|
304 | background: #eee; | |
305 | border: 1px solid #d7d7d7; |
|
305 | border: 1px solid #d7d7d7; | |
306 | color: #777; |
|
306 | color: #777; | |
307 | } |
|
307 | } | |
308 |
|
308 | |||
309 | table.list tbody th { |
|
309 | table.list tbody th { | |
310 | font-weight: bold; |
|
310 | font-weight: bold; | |
311 | background: #eed; |
|
311 | background: #eed; | |
312 | border: 1px solid #d7d7d7; |
|
312 | border: 1px solid #d7d7d7; | |
313 | color: #777; |
|
313 | color: #777; | |
314 | } |
|
314 | } | |
315 |
|
315 | |||
316 | /********** Validation error messages *************/ |
|
316 | /********** Validation error messages *************/ | |
317 | #errorExplanation { |
|
317 | #errorExplanation { | |
318 | width: 400px; |
|
318 | width: 400px; | |
319 | border: 0; |
|
319 | border: 0; | |
320 | padding: 7px; |
|
320 | padding: 7px; | |
321 | padding-bottom: 3px; |
|
321 | padding-bottom: 3px; | |
322 | margin-bottom: 0px; |
|
322 | margin-bottom: 0px; | |
323 | } |
|
323 | } | |
324 |
|
324 | |||
325 | #errorExplanation h2 { |
|
325 | #errorExplanation h2 { | |
326 | text-align: left; |
|
326 | text-align: left; | |
327 | font-weight: bold; |
|
327 | font-weight: bold; | |
328 | padding: 5px 5px 10px 26px; |
|
328 | padding: 5px 5px 10px 26px; | |
329 | font-size: 1em; |
|
329 | font-size: 1em; | |
330 | margin: -7px; |
|
330 | margin: -7px; | |
331 | background: url(../images/alert.png) no-repeat 6px 6px; |
|
331 | background: url(../images/alert.png) no-repeat 6px 6px; | |
332 | } |
|
332 | } | |
333 |
|
333 | |||
334 | #errorExplanation p { |
|
334 | #errorExplanation p { | |
335 | color: #333; |
|
335 | color: #333; | |
336 | margin-bottom: 0; |
|
336 | margin-bottom: 0; | |
337 | padding: 5px; |
|
337 | padding: 5px; | |
338 | } |
|
338 | } | |
339 |
|
339 | |||
340 | #errorExplanation ul li { |
|
340 | #errorExplanation ul li { | |
341 | font-size: 1em; |
|
341 | font-size: 1em; | |
342 | list-style: none; |
|
342 | list-style: none; | |
343 | margin-left: -16px; |
|
343 | margin-left: -16px; | |
344 | } |
|
344 | } | |
345 |
|
345 | |||
346 | /*========== Drop down menu ==============*/ |
|
346 | /*========== Drop down menu ==============*/ | |
347 | div.menu { |
|
347 | div.menu { | |
348 | background-color: #FFFFFF; |
|
348 | background-color: #FFFFFF; | |
349 | border-style: solid; |
|
349 | border-style: solid; | |
350 | border-width: 1px; |
|
350 | border-width: 1px; | |
351 | border-color: #7F9DB9; |
|
351 | border-color: #7F9DB9; | |
352 | position: absolute; |
|
352 | position: absolute; | |
353 | top: 0px; |
|
353 | top: 0px; | |
354 | left: 0px; |
|
354 | left: 0px; | |
355 | padding: 0; |
|
355 | padding: 0; | |
356 | visibility: hidden; |
|
356 | visibility: hidden; | |
357 | z-index: 101; |
|
357 | z-index: 101; | |
358 | } |
|
358 | } | |
359 |
|
359 | |||
360 | div.menu a.menuItem { |
|
360 | div.menu a.menuItem { | |
361 | font-size: 10px; |
|
361 | font-size: 10px; | |
362 | font-weight: normal; |
|
362 | font-weight: normal; | |
363 | line-height: 2em; |
|
363 | line-height: 2em; | |
364 | color: #000000; |
|
364 | color: #000000; | |
365 | background-color: #FFFFFF; |
|
365 | background-color: #FFFFFF; | |
366 | cursor: default; |
|
366 | cursor: default; | |
367 | display: block; |
|
367 | display: block; | |
368 | padding: 0 1em; |
|
368 | padding: 0 1em; | |
369 | margin: 0; |
|
369 | margin: 0; | |
370 | border: 0; |
|
370 | border: 0; | |
371 | text-decoration: none; |
|
371 | text-decoration: none; | |
372 | white-space: nowrap; |
|
372 | white-space: nowrap; | |
373 | } |
|
373 | } | |
374 |
|
374 | |||
375 | div.menu a.menuItem:hover, div.menu a.menuItemHighlight { |
|
375 | div.menu a.menuItem:hover, div.menu a.menuItemHighlight { | |
376 | background-color: #80b0da; |
|
376 | background-color: #80b0da; | |
377 | color: #ffffff; |
|
377 | color: #ffffff; | |
378 | } |
|
378 | } | |
379 |
|
379 | |||
380 | div.menu a.menuItem span.menuItemText {} |
|
380 | div.menu a.menuItem span.menuItemText {} | |
381 |
|
381 | |||
382 | div.menu a.menuItem span.menuItemArrow { |
|
382 | div.menu a.menuItem span.menuItemArrow { | |
383 | margin-right: -.75em; |
|
383 | margin-right: -.75em; | |
384 | } |
|
384 | } | |
385 |
|
385 | |||
386 | /**************** Sidebar styles ****************/ |
|
386 | /**************** Sidebar styles ****************/ | |
387 |
|
387 | |||
388 | #subcontent{ |
|
388 | #subcontent{ | |
389 | position: absolute; |
|
389 | position: absolute; | |
390 | left: 0px; |
|
390 | left: 0px; | |
391 | width:95px; |
|
391 | width:95px; | |
392 | padding:20px 20px 10px 5px; |
|
392 | padding:20px 20px 10px 5px; | |
393 | overflow: hidden; |
|
393 | overflow: hidden; | |
394 | } |
|
394 | } | |
395 |
|
395 | |||
396 | #subcontent h2{ |
|
396 | #subcontent h2{ | |
397 | display:block; |
|
397 | display:block; | |
398 | margin:0 0 5px 0; |
|
398 | margin:0 0 5px 0; | |
399 | font-size:1.0em; |
|
399 | font-size:1.0em; | |
400 | font-weight:bold; |
|
400 | font-weight:bold; | |
401 | text-align:left; |
|
401 | text-align:left; | |
402 | color:#606060; |
|
402 | color:#606060; | |
403 | background-color:inherit; |
|
403 | background-color:inherit; | |
404 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; |
|
404 | font-family: Trebuchet MS,Georgia,"Times New Roman",serif; | |
405 | } |
|
405 | } | |
406 |
|
406 | |||
407 | #subcontent p{margin:0 0 16px 0; font-size:0.9em;} |
|
407 | #subcontent p{margin:0 0 16px 0; font-size:0.9em;} | |
408 |
|
408 | |||
409 | /**************** Menublock styles ****************/ |
|
409 | /**************** Menublock styles ****************/ | |
410 |
|
410 | |||
411 | .menublock{margin:0 0 20px 8px; font-size:0.8em;} |
|
411 | .menublock{margin:0 0 20px 8px; font-size:0.8em;} | |
412 | .menublock li{list-style:none; display:block; padding:1px; margin-bottom:0px;} |
|
412 | .menublock li{list-style:none; display:block; padding:1px; margin-bottom:0px;} | |
413 | .menublock li a{font-weight:bold; text-decoration:none;} |
|
413 | .menublock li a{font-weight:bold; text-decoration:none;} | |
414 | .menublock li a:hover{text-decoration:none;} |
|
414 | .menublock li a:hover{text-decoration:none;} | |
415 | .menublock li ul{margin:0; font-size:1em; font-weight:normal;} |
|
415 | .menublock li ul{margin:0; font-size:1em; font-weight:normal;} | |
416 | .menublock li ul li{margin-bottom:0;} |
|
416 | .menublock li ul li{margin-bottom:0;} | |
417 | .menublock li ul a{font-weight:normal;} |
|
417 | .menublock li ul a{font-weight:normal;} | |
418 |
|
418 | |||
419 | /**************** Footer styles ****************/ |
|
419 | /**************** Footer styles ****************/ | |
420 |
|
420 | |||
421 | #footer{ |
|
421 | #footer{ | |
422 | clear:both; |
|
422 | clear:both; | |
423 | padding:5px 0; |
|
423 | padding:5px 0; | |
424 | margin:0; |
|
424 | margin:0; | |
425 | font-size:0.9em; |
|
425 | font-size:0.9em; | |
426 | color:#f0f0f0; |
|
426 | color:#f0f0f0; | |
427 | background:#467aa7; |
|
427 | background:#467aa7; | |
428 | } |
|
428 | } | |
429 |
|
429 | |||
430 | #footer p{padding:0; margin:0; text-align:center;} |
|
430 | #footer p{padding:0; margin:0; text-align:center;} | |
431 | #footer a{color:#f0f0f0; background-color:inherit; font-weight:bold;} |
|
431 | #footer a{color:#f0f0f0; background-color:inherit; font-weight:bold;} | |
432 | #footer a:hover{color:#ffffff; background-color:inherit; text-decoration: underline;} |
|
432 | #footer a:hover{color:#ffffff; background-color:inherit; text-decoration: underline;} | |
433 |
|
433 | |||
434 | /**************** Misc classes and styles ****************/ |
|
434 | /**************** Misc classes and styles ****************/ | |
435 |
|
435 | |||
436 | .splitcontentleft{float:left; width:49%;} |
|
436 | .splitcontentleft{float:left; width:49%;} | |
437 | .splitcontentright{float:right; width:49%;} |
|
437 | .splitcontentright{float:right; width:49%;} | |
438 | .clear{clear:both;} |
|
438 | .clear{clear:both;} | |
439 | .small{font-size:0.8em;line-height:1.4em;padding:0 0 0 0;} |
|
439 | .small{font-size:0.8em;line-height:1.4em;padding:0 0 0 0;} | |
440 | .hide{display:none;} |
|
440 | .hide{display:none;} | |
441 | .textcenter{text-align:center;} |
|
441 | .textcenter{text-align:center;} | |
442 | .textright{text-align:right;} |
|
442 | .textright{text-align:right;} | |
443 | .important{color:#f02025; background-color:inherit; font-weight:bold;} |
|
443 | .important{color:#f02025; background-color:inherit; font-weight:bold;} | |
444 |
|
444 | |||
445 | .box{ |
|
445 | .box{ | |
446 | margin:0 0 20px 0; |
|
446 | margin:0 0 20px 0; | |
447 | padding:10px; |
|
447 | padding:10px; | |
448 | border:1px solid #c0c0c0; |
|
448 | border:1px solid #c0c0c0; | |
449 | background-color:#fafbfc; |
|
449 | background-color:#fafbfc; | |
450 | color:#505050; |
|
450 | color:#505050; | |
451 | line-height:1.5em; |
|
451 | line-height:1.5em; | |
452 | } |
|
452 | } | |
453 |
|
453 | |||
454 | a.close-icon { |
|
454 | a.close-icon { | |
455 | display:block; |
|
455 | display:block; | |
456 | margin-top:3px; |
|
456 | margin-top:3px; | |
457 | overflow:hidden; |
|
457 | overflow:hidden; | |
458 | width:12px; |
|
458 | width:12px; | |
459 | height:12px; |
|
459 | height:12px; | |
460 | background-repeat: no-repeat; |
|
460 | background-repeat: no-repeat; | |
461 | cursor:pointer; |
|
461 | cursor:pointer; | |
462 | background-image:url('../images/close.png'); |
|
462 | background-image:url('../images/close.png'); | |
463 | } |
|
463 | } | |
464 |
|
464 | |||
465 | a.close-icon:hover { |
|
465 | a.close-icon:hover { | |
466 | background-image:url('../images/close_hl.png'); |
|
466 | background-image:url('../images/close_hl.png'); | |
467 | } |
|
467 | } | |
468 |
|
468 | |||
469 | .rightbox{ |
|
469 | .rightbox{ | |
470 | background: #fafbfc; |
|
470 | background: #fafbfc; | |
471 | border: 1px solid #c0c0c0; |
|
471 | border: 1px solid #c0c0c0; | |
472 | float: right; |
|
472 | float: right; | |
473 | padding: 8px; |
|
473 | padding: 8px; | |
474 | position: relative; |
|
474 | position: relative; | |
475 | margin: 0 5px 5px; |
|
475 | margin: 0 5px 5px; | |
476 | } |
|
476 | } | |
477 |
|
477 | |||
478 | div.attachments {padding-left: 6px; border-left: 2px solid #ccc;} |
|
478 | div.attachments {padding-left: 6px; border-left: 2px solid #ccc; margin-bottom: 8px;} | |
|
479 | div.attachments p {margin-bottom:2px;} | |||
479 |
|
480 | |||
480 | .overlay{ |
|
481 | .overlay{ | |
481 | position: absolute; |
|
482 | position: absolute; | |
482 | margin-left:0; |
|
483 | margin-left:0; | |
483 | z-index: 50; |
|
484 | z-index: 50; | |
484 | } |
|
485 | } | |
485 |
|
486 | |||
486 | .layout-active { |
|
487 | .layout-active { | |
487 | background: #ECF3E1; |
|
488 | background: #ECF3E1; | |
488 | } |
|
489 | } | |
489 |
|
490 | |||
490 | .block-receiver { |
|
491 | .block-receiver { | |
491 | border:1px dashed #c0c0c0; |
|
492 | border:1px dashed #c0c0c0; | |
492 | margin-bottom: 20px; |
|
493 | margin-bottom: 20px; | |
493 | padding: 15px 0 15px 0; |
|
494 | padding: 15px 0 15px 0; | |
494 | } |
|
495 | } | |
495 |
|
496 | |||
496 | .mypage-box { |
|
497 | .mypage-box { | |
497 | margin:0 0 20px 0; |
|
498 | margin:0 0 20px 0; | |
498 | color:#505050; |
|
499 | color:#505050; | |
499 | line-height:1.5em; |
|
500 | line-height:1.5em; | |
500 | } |
|
501 | } | |
501 |
|
502 | |||
502 | .handle { |
|
503 | .handle { | |
503 | cursor: move; |
|
504 | cursor: move; | |
504 | } |
|
505 | } | |
505 |
|
506 | |||
506 | .login { |
|
507 | .login { | |
507 | width: 50%; |
|
508 | width: 50%; | |
508 | text-align: left; |
|
509 | text-align: left; | |
509 | } |
|
510 | } | |
510 |
|
511 | |||
511 | img.calendar-trigger { |
|
512 | img.calendar-trigger { | |
512 | cursor: pointer; |
|
513 | cursor: pointer; | |
513 | vertical-align: middle; |
|
514 | vertical-align: middle; | |
514 | margin-left: 4px; |
|
515 | margin-left: 4px; | |
515 | } |
|
516 | } | |
516 |
|
517 | |||
517 | #history p { |
|
518 | #history p { | |
518 | margin-left: 34px; |
|
519 | margin-left: 34px; | |
519 | } |
|
520 | } | |
520 |
|
521 | |||
521 | .progress { |
|
522 | .progress { | |
522 | border: 1px solid #D7D7D7; |
|
523 | border: 1px solid #D7D7D7; | |
523 | border-collapse: collapse; |
|
524 | border-collapse: collapse; | |
524 | border-spacing: 0pt; |
|
525 | border-spacing: 0pt; | |
525 | empty-cells: show; |
|
526 | empty-cells: show; | |
526 | padding: 3px; |
|
527 | padding: 3px; | |
527 | width: 40em; |
|
528 | width: 40em; | |
528 | text-align: center; |
|
529 | text-align: center; | |
529 | } |
|
530 | } | |
530 |
|
531 | |||
531 | .progress td { height: 1em; } |
|
532 | .progress td { height: 1em; } | |
532 | .progress .closed { background: #BAE0BA none repeat scroll 0%; } |
|
533 | .progress .closed { background: #BAE0BA none repeat scroll 0%; } | |
533 | .progress .open { background: #FFF none repeat scroll 0%; } |
|
534 | .progress .open { background: #FFF none repeat scroll 0%; } | |
534 |
|
535 | |||
535 | /***** Contextual links div *****/ |
|
536 | /***** Contextual links div *****/ | |
536 | .contextual { |
|
537 | .contextual { | |
537 | float: right; |
|
538 | float: right; | |
538 | font-size: 0.8em; |
|
539 | font-size: 0.8em; | |
539 | line-height: 16px; |
|
540 | line-height: 16px; | |
540 | padding: 2px; |
|
541 | padding: 2px; | |
541 | } |
|
542 | } | |
542 |
|
543 | |||
543 | .contextual select, .contextual input { |
|
544 | .contextual select, .contextual input { | |
544 | font-size: 1em; |
|
545 | font-size: 1em; | |
545 | } |
|
546 | } | |
546 |
|
547 | |||
547 | /***** Gantt chart *****/ |
|
548 | /***** Gantt chart *****/ | |
548 | .gantt_hdr { |
|
549 | .gantt_hdr { | |
549 | position:absolute; |
|
550 | position:absolute; | |
550 | top:0; |
|
551 | top:0; | |
551 | height:16px; |
|
552 | height:16px; | |
552 | border-top: 1px solid #c0c0c0; |
|
553 | border-top: 1px solid #c0c0c0; | |
553 | border-bottom: 1px solid #c0c0c0; |
|
554 | border-bottom: 1px solid #c0c0c0; | |
554 | border-right: 1px solid #c0c0c0; |
|
555 | border-right: 1px solid #c0c0c0; | |
555 | text-align: center; |
|
556 | text-align: center; | |
556 | overflow: hidden; |
|
557 | overflow: hidden; | |
557 | } |
|
558 | } | |
558 |
|
559 | |||
559 | .task { |
|
560 | .task { | |
560 | position: absolute; |
|
561 | position: absolute; | |
561 | height:8px; |
|
562 | height:8px; | |
562 | font-size:0.8em; |
|
563 | font-size:0.8em; | |
563 | color:#888; |
|
564 | color:#888; | |
564 | padding:0; |
|
565 | padding:0; | |
565 | margin:0; |
|
566 | margin:0; | |
566 | line-height:0.8em; |
|
567 | line-height:0.8em; | |
567 | } |
|
568 | } | |
568 |
|
569 | |||
569 | .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; } |
|
570 | .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; } | |
570 | .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; } |
|
571 | .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; } | |
571 | .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; } |
|
572 | .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; } | |
572 | .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; } |
|
573 | .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; } | |
573 |
|
574 | |||
574 | /***** Tooltips ******/ |
|
575 | /***** Tooltips ******/ | |
575 | .tooltip{position:relative;z-index:24;} |
|
576 | .tooltip{position:relative;z-index:24;} | |
576 | .tooltip:hover{z-index:25;color:#000;} |
|
577 | .tooltip:hover{z-index:25;color:#000;} | |
577 | .tooltip span.tip{display: none; text-align:left;} |
|
578 | .tooltip span.tip{display: none; text-align:left;} | |
578 |
|
579 | |||
579 | div.tooltip:hover span.tip{ |
|
580 | div.tooltip:hover span.tip{ | |
580 | display:block; |
|
581 | display:block; | |
581 | position:absolute; |
|
582 | position:absolute; | |
582 | top:12px; left:24px; width:270px; |
|
583 | top:12px; left:24px; width:270px; | |
583 | border:1px solid #555; |
|
584 | border:1px solid #555; | |
584 | background-color:#fff; |
|
585 | background-color:#fff; | |
585 | padding: 4px; |
|
586 | padding: 4px; | |
586 | font-size: 0.8em; |
|
587 | font-size: 0.8em; | |
587 | color:#505050; |
|
588 | color:#505050; | |
588 | } |
|
589 | } | |
589 |
|
590 | |||
590 | /***** CSS FORM ******/ |
|
591 | /***** CSS FORM ******/ | |
591 | .tabular p{ |
|
592 | .tabular p{ | |
592 | margin: 0; |
|
593 | margin: 0; | |
593 | padding: 5px 0 8px 0; |
|
594 | padding: 5px 0 8px 0; | |
594 | padding-left: 180px; /*width of left column containing the label elements*/ |
|
595 | padding-left: 180px; /*width of left column containing the label elements*/ | |
595 | height: 1%; |
|
596 | height: 1%; | |
596 | } |
|
597 | } | |
597 |
|
598 | |||
598 | .tabular label{ |
|
599 | .tabular label{ | |
599 | font-weight: bold; |
|
600 | font-weight: bold; | |
600 | float: left; |
|
601 | float: left; | |
601 | margin-left: -180px; /*width of left column*/ |
|
602 | margin-left: -180px; /*width of left column*/ | |
602 | width: 175px; /*width of labels. Should be smaller than left column to create some right |
|
603 | width: 175px; /*width of labels. Should be smaller than left column to create some right | |
603 | margin*/ |
|
604 | margin*/ | |
604 | } |
|
605 | } | |
605 |
|
606 | |||
606 | .error { |
|
607 | .error { | |
607 | color: #cc0000; |
|
608 | color: #cc0000; | |
608 | } |
|
609 | } | |
609 |
|
610 | |||
610 | #settings .tabular p{ padding-left: 300px; } |
|
611 | #settings .tabular p{ padding-left: 300px; } | |
611 | #settings .tabular label{ margin-left: -300px; width: 295px; } |
|
612 | #settings .tabular label{ margin-left: -300px; width: 295px; } | |
612 |
|
613 | |||
613 | /*.threepxfix class below: |
|
614 | /*.threepxfix class below: | |
614 | Targets IE6- ONLY. Adds 3 pixel indent for multi-line form contents. |
|
615 | Targets IE6- ONLY. Adds 3 pixel indent for multi-line form contents. | |
615 | to account for 3 pixel bug: http://www.positioniseverything.net/explorer/threepxtest.html |
|
616 | to account for 3 pixel bug: http://www.positioniseverything.net/explorer/threepxtest.html | |
616 | */ |
|
617 | */ | |
617 |
|
618 | |||
618 | * html .threepxfix{ |
|
619 | * html .threepxfix{ | |
619 | margin-left: 3px; |
|
620 | margin-left: 3px; | |
620 | } |
|
621 | } | |
621 |
|
622 | |||
622 | /***** Wiki sections ****/ |
|
623 | /***** Wiki sections ****/ | |
623 | #content div.wiki { font-size: 110%} |
|
624 | #content div.wiki { font-size: 110%} | |
624 |
|
625 | |||
625 | #content div.wiki h2, div.wiki h3 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; color:#606060; } |
|
626 | #content div.wiki h2, div.wiki h3 { font-family: Trebuchet MS,Georgia,"Times New Roman",serif; color:#606060; } | |
626 | #content div.wiki h2 { font-size: 1.4em;} |
|
627 | #content div.wiki h2 { font-size: 1.4em;} | |
627 | #content div.wiki h3 { font-size: 1.2em;} |
|
628 | #content div.wiki h3 { font-size: 1.2em;} | |
628 |
|
629 | |||
629 | div.wiki table { |
|
630 | div.wiki table { | |
630 | border: 1px solid #505050; |
|
631 | border: 1px solid #505050; | |
631 | border-collapse: collapse; |
|
632 | border-collapse: collapse; | |
632 | } |
|
633 | } | |
633 |
|
634 | |||
634 | div.wiki table, div.wiki td { |
|
635 | div.wiki table, div.wiki td { | |
635 | border: 1px solid #bbb; |
|
636 | border: 1px solid #bbb; | |
636 | padding: 4px; |
|
637 | padding: 4px; | |
637 | } |
|
638 | } | |
638 |
|
639 | |||
639 | div.wiki code { |
|
640 | div.wiki code { | |
640 | font-size: 1.2em; |
|
641 | font-size: 1.2em; | |
641 | } |
|
642 | } | |
642 |
|
643 | |||
643 | #preview .preview { background: #fafbfc url(../images/draft.png); } |
|
644 | #preview .preview { background: #fafbfc url(../images/draft.png); } | |
644 |
|
645 | |||
645 | #ajax-indicator { |
|
646 | #ajax-indicator { | |
646 | position: absolute; /* fixed not supported by IE */ |
|
647 | position: absolute; /* fixed not supported by IE */ | |
647 | background-color:#eee; |
|
648 | background-color:#eee; | |
648 | border: 1px solid #bbb; |
|
649 | border: 1px solid #bbb; | |
649 | top:35%; |
|
650 | top:35%; | |
650 | left:40%; |
|
651 | left:40%; | |
651 | width:20%; |
|
652 | width:20%; | |
652 | font-weight:bold; |
|
653 | font-weight:bold; | |
653 | text-align:center; |
|
654 | text-align:center; | |
654 | padding:0.6em; |
|
655 | padding:0.6em; | |
655 | z-index:100; |
|
656 | z-index:100; | |
656 | filter:alpha(opacity=50); |
|
657 | filter:alpha(opacity=50); | |
657 | -moz-opacity:0.5; |
|
658 | -moz-opacity:0.5; | |
658 | opacity: 0.5; |
|
659 | opacity: 0.5; | |
659 | -khtml-opacity: 0.5; |
|
660 | -khtml-opacity: 0.5; | |
660 | } |
|
661 | } | |
661 |
|
662 | |||
662 | html>body #ajax-indicator { position: fixed; } |
|
663 | html>body #ajax-indicator { position: fixed; } | |
663 |
|
664 | |||
664 | #ajax-indicator span { |
|
665 | #ajax-indicator span { | |
665 | background-position: 0% 40%; |
|
666 | background-position: 0% 40%; | |
666 | background-repeat: no-repeat; |
|
667 | background-repeat: no-repeat; | |
667 | background-image: url(../images/loading.gif); |
|
668 | background-image: url(../images/loading.gif); | |
668 | padding-left: 26px; |
|
669 | padding-left: 26px; | |
669 | vertical-align: bottom; |
|
670 | vertical-align: bottom; | |
670 | } |
|
671 | } |
General Comments 0
You need to be logged in to leave comments.
Login now