##// END OF EJS Templates
Attachments can now be added to wiki pages (original patch by Pavol Murin). Only authorized users can add/delete attachments....
Jean-Philippe Lang -
r538:f8ef65e8f641
parent child
Show More
@@ -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, :except => [:preview]
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 ], :redirect_to => { :action => :index }
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(('&#171; ' + l(:label_previous)),
98 html << link_to_remote(('&#171; ' + 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) + ' &#187;'),
108 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
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 << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
184 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + 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 << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
193 full_messages << "&#171; " + v.custom_field.name + " &#187; " + 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 &nbsp;
75 &nbsp;
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 %> &#187; <%=h @message.subject %></h2>
1 <h2><%= link_to h(@board.name), :controller => 'boards', :action => 'show', :project_id => @project, :id => @board %> &#187; <%=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(('&#171; ' + l(:label_previous)), :action => 'index', :page => @page.title, :version => (@content.version - 1)) + " - " if @content.version > 1 %>
11 <%= link_to(('&#171; ' + 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) + ' &#187;'), :action => 'index', :page => @page.title, :version => (@content.version + 1)) + " - " if @content.version < @page.content.version %>
13 <%= link_to((l(:label_next) + ' &#187;'), :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