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