##// END OF EJS Templates
The following menus can now be extended by plugins: top_menu, account_menu, application_menu (empty by default)....
Jean-Philippe Lang -
r1123:c5d998528fed
parent child
Show More
@@ -1,450 +1,442
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 module ApplicationHelper
18 module ApplicationHelper
19 include Redmine::WikiFormatting::Macros::Definitions
19 include Redmine::WikiFormatting::Macros::Definitions
20
20
21 def current_role
21 def current_role
22 @current_role ||= User.current.role_for_project(@project)
22 @current_role ||= User.current.role_for_project(@project)
23 end
23 end
24
24
25 # Return true if user is authorized for controller/action, otherwise false
25 # Return true if user is authorized for controller/action, otherwise false
26 def authorize_for(controller, action)
26 def authorize_for(controller, action)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
27 User.current.allowed_to?({:controller => controller, :action => action}, @project)
28 end
28 end
29
29
30 # Display a link if user is authorized
30 # Display a link if user is authorized
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
31 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
32 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
33 end
33 end
34
34
35 def link_to_signin
36 link_to l(:label_login), { :controller => 'account', :action => 'login' }, :class => 'signin'
37 end
38
39 def link_to_signout
40 link_to l(:label_logout), { :controller => 'account', :action => 'logout' }, :class => 'logout'
41 end
42
43 # Display a link to user's account page
35 # Display a link to user's account page
44 def link_to_user(user)
36 def link_to_user(user)
45 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
37 user ? link_to(user, :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
46 end
38 end
47
39
48 def link_to_issue(issue)
40 def link_to_issue(issue)
49 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
41 link_to "#{issue.tracker.name} ##{issue.id}", :controller => "issues", :action => "show", :id => issue
50 end
42 end
51
43
52 def toggle_link(name, id, options={})
44 def toggle_link(name, id, options={})
53 onclick = "Element.toggle('#{id}'); "
45 onclick = "Element.toggle('#{id}'); "
54 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
46 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
55 onclick << "return false;"
47 onclick << "return false;"
56 link_to(name, "#", :onclick => onclick)
48 link_to(name, "#", :onclick => onclick)
57 end
49 end
58
50
59 def show_and_goto_link(name, id, options={})
51 def show_and_goto_link(name, id, options={})
60 onclick = "Element.show('#{id}'); "
52 onclick = "Element.show('#{id}'); "
61 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
53 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
62 onclick << "Element.scrollTo('#{id}'); "
54 onclick << "Element.scrollTo('#{id}'); "
63 onclick << "return false;"
55 onclick << "return false;"
64 link_to(name, "#", options.merge(:onclick => onclick))
56 link_to(name, "#", options.merge(:onclick => onclick))
65 end
57 end
66
58
67 def image_to_function(name, function, html_options = {})
59 def image_to_function(name, function, html_options = {})
68 html_options.symbolize_keys!
60 html_options.symbolize_keys!
69 tag(:input, html_options.merge({
61 tag(:input, html_options.merge({
70 :type => "image", :src => image_path(name),
62 :type => "image", :src => image_path(name),
71 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
63 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
72 }))
64 }))
73 end
65 end
74
66
75 def prompt_to_remote(name, text, param, url, html_options = {})
67 def prompt_to_remote(name, text, param, url, html_options = {})
76 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
68 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
77 link_to name, {}, html_options
69 link_to name, {}, html_options
78 end
70 end
79
71
80 def format_date(date)
72 def format_date(date)
81 return nil unless date
73 return nil unless date
82 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
74 # "Setting.date_format.size < 2" is a temporary fix (content of date_format setting changed)
83 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
75 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
84 date.strftime(@date_format)
76 date.strftime(@date_format)
85 end
77 end
86
78
87 def format_time(time, include_date = true)
79 def format_time(time, include_date = true)
88 return nil unless time
80 return nil unless time
89 time = time.to_time if time.is_a?(String)
81 time = time.to_time if time.is_a?(String)
90 zone = User.current.time_zone
82 zone = User.current.time_zone
91 if time.utc?
83 if time.utc?
92 local = zone ? zone.adjust(time) : time.getlocal
84 local = zone ? zone.adjust(time) : time.getlocal
93 else
85 else
94 local = zone ? zone.adjust(time.getutc) : time
86 local = zone ? zone.adjust(time.getutc) : time
95 end
87 end
96 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
88 @date_format ||= (Setting.date_format.blank? || Setting.date_format.size < 2 ? l(:general_fmt_date) : Setting.date_format)
97 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
89 @time_format ||= (Setting.time_format.blank? ? l(:general_fmt_time) : Setting.time_format)
98 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
90 include_date ? local.strftime("#{@date_format} #{@time_format}") : local.strftime(@time_format)
99 end
91 end
100
92
101 def authoring(created, author)
93 def authoring(created, author)
102 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
94 time_tag = content_tag('acronym', distance_of_time_in_words(Time.now, created), :title => format_time(created))
103 l(:label_added_time_by, author || 'Anonymous', time_tag)
95 l(:label_added_time_by, author || 'Anonymous', time_tag)
104 end
96 end
105
97
106 def day_name(day)
98 def day_name(day)
107 l(:general_day_names).split(',')[day-1]
99 l(:general_day_names).split(',')[day-1]
108 end
100 end
109
101
110 def month_name(month)
102 def month_name(month)
111 l(:actionview_datehelper_select_month_names).split(',')[month-1]
103 l(:actionview_datehelper_select_month_names).split(',')[month-1]
112 end
104 end
113
105
114 def pagination_links_full(paginator, count=nil, options={})
106 def pagination_links_full(paginator, count=nil, options={})
115 page_param = options.delete(:page_param) || :page
107 page_param = options.delete(:page_param) || :page
116 url_param = params.dup
108 url_param = params.dup
117 # don't reuse params if filters are present
109 # don't reuse params if filters are present
118 url_param.clear if url_param.has_key?(:set_filter)
110 url_param.clear if url_param.has_key?(:set_filter)
119
111
120 html = ''
112 html = ''
121 html << link_to_remote(('&#171; ' + l(:label_previous)),
113 html << link_to_remote(('&#171; ' + l(:label_previous)),
122 {:update => 'content',
114 {:update => 'content',
123 :url => url_param.merge(page_param => paginator.current.previous),
115 :url => url_param.merge(page_param => paginator.current.previous),
124 :complete => 'window.scrollTo(0,0)'},
116 :complete => 'window.scrollTo(0,0)'},
125 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
117 {:href => url_for(:params => url_param.merge(page_param => paginator.current.previous))}) + ' ' if paginator.current.previous
126
118
127 html << (pagination_links_each(paginator, options) do |n|
119 html << (pagination_links_each(paginator, options) do |n|
128 link_to_remote(n.to_s,
120 link_to_remote(n.to_s,
129 {:url => {:params => url_param.merge(page_param => n)},
121 {:url => {:params => url_param.merge(page_param => n)},
130 :update => 'content',
122 :update => 'content',
131 :complete => 'window.scrollTo(0,0)'},
123 :complete => 'window.scrollTo(0,0)'},
132 {:href => url_for(:params => url_param.merge(page_param => n))})
124 {:href => url_for(:params => url_param.merge(page_param => n))})
133 end || '')
125 end || '')
134
126
135 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
127 html << ' ' + link_to_remote((l(:label_next) + ' &#187;'),
136 {:update => 'content',
128 {:update => 'content',
137 :url => url_param.merge(page_param => paginator.current.next),
129 :url => url_param.merge(page_param => paginator.current.next),
138 :complete => 'window.scrollTo(0,0)'},
130 :complete => 'window.scrollTo(0,0)'},
139 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
131 {:href => url_for(:params => url_param.merge(page_param => paginator.current.next))}) if paginator.current.next
140
132
141 unless count.nil?
133 unless count.nil?
142 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
134 html << [" (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})", per_page_links(paginator.items_per_page)].compact.join(' | ')
143 end
135 end
144
136
145 html
137 html
146 end
138 end
147
139
148 def per_page_links(selected=nil)
140 def per_page_links(selected=nil)
149 url_param = params.dup
141 url_param = params.dup
150 url_param.clear if url_param.has_key?(:set_filter)
142 url_param.clear if url_param.has_key?(:set_filter)
151
143
152 links = Setting.per_page_options_array.collect do |n|
144 links = Setting.per_page_options_array.collect do |n|
153 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
145 n == selected ? n : link_to_remote(n, {:update => "content", :url => params.dup.merge(:per_page => n)},
154 {:href => url_for(url_param.merge(:per_page => n))})
146 {:href => url_for(url_param.merge(:per_page => n))})
155 end
147 end
156 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
148 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
157 end
149 end
158
150
159 def html_title(*args)
151 def html_title(*args)
160 if args.empty?
152 if args.empty?
161 title = []
153 title = []
162 title << @project.name if @project
154 title << @project.name if @project
163 title += @html_title if @html_title
155 title += @html_title if @html_title
164 title << Setting.app_title
156 title << Setting.app_title
165 title.compact.join(' - ')
157 title.compact.join(' - ')
166 else
158 else
167 @html_title ||= []
159 @html_title ||= []
168 @html_title += args
160 @html_title += args
169 end
161 end
170 end
162 end
171
163
172 def accesskey(s)
164 def accesskey(s)
173 Redmine::AccessKeys.key_for s
165 Redmine::AccessKeys.key_for s
174 end
166 end
175
167
176 # Formats text according to system settings.
168 # Formats text according to system settings.
177 # 2 ways to call this method:
169 # 2 ways to call this method:
178 # * with a String: textilizable(text, options)
170 # * with a String: textilizable(text, options)
179 # * with an object and one of its attribute: textilizable(issue, :description, options)
171 # * with an object and one of its attribute: textilizable(issue, :description, options)
180 def textilizable(*args)
172 def textilizable(*args)
181 options = args.last.is_a?(Hash) ? args.pop : {}
173 options = args.last.is_a?(Hash) ? args.pop : {}
182 case args.size
174 case args.size
183 when 1
175 when 1
184 obj = nil
176 obj = nil
185 text = args.shift || ''
177 text = args.shift || ''
186 when 2
178 when 2
187 obj = args.shift
179 obj = args.shift
188 text = obj.send(args.shift)
180 text = obj.send(args.shift)
189 else
181 else
190 raise ArgumentError, 'invalid arguments to textilizable'
182 raise ArgumentError, 'invalid arguments to textilizable'
191 end
183 end
192
184
193 # when using an image link, try to use an attachment, if possible
185 # when using an image link, try to use an attachment, if possible
194 attachments = options[:attachments]
186 attachments = options[:attachments]
195 if attachments
187 if attachments
196 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
188 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(gif|jpg|jpeg|png))!/) do |m|
197 style = $1
189 style = $1
198 filename = $6
190 filename = $6
199 rf = Regexp.new(filename, Regexp::IGNORECASE)
191 rf = Regexp.new(filename, Regexp::IGNORECASE)
200 # search for the picture in attachments
192 # search for the picture in attachments
201 if found = attachments.detect { |att| att.filename =~ rf }
193 if found = attachments.detect { |att| att.filename =~ rf }
202 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
194 image_url = url_for :controller => 'attachments', :action => 'download', :id => found.id
203 "!#{style}#{image_url}!"
195 "!#{style}#{image_url}!"
204 else
196 else
205 "!#{style}#{filename}!"
197 "!#{style}#{filename}!"
206 end
198 end
207 end
199 end
208 end
200 end
209
201
210 text = (Setting.text_formatting == 'textile') ?
202 text = (Setting.text_formatting == 'textile') ?
211 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
203 Redmine::WikiFormatting.to_html(text) { |macro, args| exec_macro(macro, obj, args) } :
212 simple_format(auto_link(h(text)))
204 simple_format(auto_link(h(text)))
213
205
214 # different methods for formatting wiki links
206 # different methods for formatting wiki links
215 case options[:wiki_links]
207 case options[:wiki_links]
216 when :local
208 when :local
217 # used for local links to html files
209 # used for local links to html files
218 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
210 format_wiki_link = Proc.new {|project, title| "#{title}.html" }
219 when :anchor
211 when :anchor
220 # used for single-file wiki export
212 # used for single-file wiki export
221 format_wiki_link = Proc.new {|project, title| "##{title}" }
213 format_wiki_link = Proc.new {|project, title| "##{title}" }
222 else
214 else
223 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
215 format_wiki_link = Proc.new {|project, title| url_for :controller => 'wiki', :action => 'index', :id => project, :page => title }
224 end
216 end
225
217
226 project = options[:project] || @project
218 project = options[:project] || @project
227
219
228 # Wiki links
220 # Wiki links
229 #
221 #
230 # Examples:
222 # Examples:
231 # [[mypage]]
223 # [[mypage]]
232 # [[mypage|mytext]]
224 # [[mypage|mytext]]
233 # wiki links can refer other project wikis, using project name or identifier:
225 # wiki links can refer other project wikis, using project name or identifier:
234 # [[project:]] -> wiki starting page
226 # [[project:]] -> wiki starting page
235 # [[project:|mytext]]
227 # [[project:|mytext]]
236 # [[project:mypage]]
228 # [[project:mypage]]
237 # [[project:mypage|mytext]]
229 # [[project:mypage|mytext]]
238 text = text.gsub(/(!)?(\[\[([^\]\|]+)(\|([^\]\|]+))?\]\])/) do |m|
230 text = text.gsub(/(!)?(\[\[([^\]\|]+)(\|([^\]\|]+))?\]\])/) do |m|
239 link_project = project
231 link_project = project
240 esc, all, page, title = $1, $2, $3, $5
232 esc, all, page, title = $1, $2, $3, $5
241 if esc.nil?
233 if esc.nil?
242 if page =~ /^([^\:]+)\:(.*)$/
234 if page =~ /^([^\:]+)\:(.*)$/
243 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
235 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
244 page = $2
236 page = $2
245 title ||= $1 if page.blank?
237 title ||= $1 if page.blank?
246 end
238 end
247
239
248 if link_project && link_project.wiki
240 if link_project && link_project.wiki
249 # check if page exists
241 # check if page exists
250 wiki_page = link_project.wiki.find_page(page)
242 wiki_page = link_project.wiki.find_page(page)
251 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
243 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page)),
252 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
244 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
253 else
245 else
254 # project or wiki doesn't exist
246 # project or wiki doesn't exist
255 title || page
247 title || page
256 end
248 end
257 else
249 else
258 all
250 all
259 end
251 end
260 end
252 end
261
253
262 # Redmine links
254 # Redmine links
263 #
255 #
264 # Examples:
256 # Examples:
265 # Issues:
257 # Issues:
266 # #52 -> Link to issue #52
258 # #52 -> Link to issue #52
267 # Changesets:
259 # Changesets:
268 # r52 -> Link to revision 52
260 # r52 -> Link to revision 52
269 # Documents:
261 # Documents:
270 # document#17 -> Link to document with id 17
262 # document#17 -> Link to document with id 17
271 # document:Greetings -> Link to the document with title "Greetings"
263 # document:Greetings -> Link to the document with title "Greetings"
272 # document:"Some document" -> Link to the document with title "Some document"
264 # document:"Some document" -> Link to the document with title "Some document"
273 # Versions:
265 # Versions:
274 # version#3 -> Link to version with id 3
266 # version#3 -> Link to version with id 3
275 # version:1.0.0 -> Link to version named "1.0.0"
267 # version:1.0.0 -> Link to version named "1.0.0"
276 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
268 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
277 # Attachments:
269 # Attachments:
278 # attachment:file.zip -> Link to the attachment of the current object named file.zip
270 # attachment:file.zip -> Link to the attachment of the current object named file.zip
279 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version)?((#|r)(\d+)|(:)([^"][^\s<>]+|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
271 text = text.gsub(%r{([\s\(,-^])(!)?(attachment|document|version)?((#|r)(\d+)|(:)([^"][^\s<>]+|"[^"]+"))(?=[[:punct:]]|\s|<|$)}) do |m|
280 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
272 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
281 link = nil
273 link = nil
282 if esc.nil?
274 if esc.nil?
283 if prefix.nil? && sep == 'r'
275 if prefix.nil? && sep == 'r'
284 if project && (changeset = project.changesets.find_by_revision(oid))
276 if project && (changeset = project.changesets.find_by_revision(oid))
285 link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset',
277 link = link_to("r#{oid}", {:controller => 'repositories', :action => 'revision', :id => project.id, :rev => oid}, :class => 'changeset',
286 :title => truncate(changeset.comments, 100))
278 :title => truncate(changeset.comments, 100))
287 end
279 end
288 elsif sep == '#'
280 elsif sep == '#'
289 oid = oid.to_i
281 oid = oid.to_i
290 case prefix
282 case prefix
291 when nil
283 when nil
292 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
284 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
293 link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue',
285 link = link_to("##{oid}", {:controller => 'issues', :action => 'show', :id => oid}, :class => 'issue',
294 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
286 :title => "#{truncate(issue.subject, 100)} (#{issue.status.name})")
295 link = content_tag('del', link) if issue.closed?
287 link = content_tag('del', link) if issue.closed?
296 end
288 end
297 when 'document'
289 when 'document'
298 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
290 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
299 link = link_to h(document.title), {:controller => 'documents', :action => 'show', :id => document}, :class => 'document'
291 link = link_to h(document.title), {:controller => 'documents', :action => 'show', :id => document}, :class => 'document'
300 end
292 end
301 when 'version'
293 when 'version'
302 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
294 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
303 link = link_to h(version.name), {:controller => 'versions', :action => 'show', :id => version}, :class => 'version'
295 link = link_to h(version.name), {:controller => 'versions', :action => 'show', :id => version}, :class => 'version'
304 end
296 end
305 end
297 end
306 elsif sep == ':'
298 elsif sep == ':'
307 # removes the double quotes if any
299 # removes the double quotes if any
308 name = oid.gsub(%r{^"(.*)"$}, "\\1")
300 name = oid.gsub(%r{^"(.*)"$}, "\\1")
309 case prefix
301 case prefix
310 when 'document'
302 when 'document'
311 if project && document = project.documents.find_by_title(name)
303 if project && document = project.documents.find_by_title(name)
312 link = link_to h(document.title), {:controller => 'documents', :action => 'show', :id => document}, :class => 'document'
304 link = link_to h(document.title), {:controller => 'documents', :action => 'show', :id => document}, :class => 'document'
313 end
305 end
314 when 'version'
306 when 'version'
315 if project && version = project.versions.find_by_name(name)
307 if project && version = project.versions.find_by_name(name)
316 link = link_to h(version.name), {:controller => 'versions', :action => 'show', :id => version}, :class => 'version'
308 link = link_to h(version.name), {:controller => 'versions', :action => 'show', :id => version}, :class => 'version'
317 end
309 end
318 when 'attachment'
310 when 'attachment'
319 if attachments && attachment = attachments.detect {|a| a.filename == name }
311 if attachments && attachment = attachments.detect {|a| a.filename == name }
320 link = link_to h(attachment.filename), {:controller => 'attachments', :action => 'download', :id => attachment}, :class => 'attachment'
312 link = link_to h(attachment.filename), {:controller => 'attachments', :action => 'download', :id => attachment}, :class => 'attachment'
321 end
313 end
322 end
314 end
323 end
315 end
324 end
316 end
325 leading + (link || "#{prefix}#{sep}#{oid}")
317 leading + (link || "#{prefix}#{sep}#{oid}")
326 end
318 end
327
319
328 text
320 text
329 end
321 end
330
322
331 # Same as Rails' simple_format helper without using paragraphs
323 # Same as Rails' simple_format helper without using paragraphs
332 def simple_format_without_paragraph(text)
324 def simple_format_without_paragraph(text)
333 text.to_s.
325 text.to_s.
334 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
326 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
335 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
327 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
336 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
328 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
337 end
329 end
338
330
339 def error_messages_for(object_name, options = {})
331 def error_messages_for(object_name, options = {})
340 options = options.symbolize_keys
332 options = options.symbolize_keys
341 object = instance_variable_get("@#{object_name}")
333 object = instance_variable_get("@#{object_name}")
342 if object && !object.errors.empty?
334 if object && !object.errors.empty?
343 # build full_messages here with controller current language
335 # build full_messages here with controller current language
344 full_messages = []
336 full_messages = []
345 object.errors.each do |attr, msg|
337 object.errors.each do |attr, msg|
346 next if msg.nil?
338 next if msg.nil?
347 msg = msg.first if msg.is_a? Array
339 msg = msg.first if msg.is_a? Array
348 if attr == "base"
340 if attr == "base"
349 full_messages << l(msg)
341 full_messages << l(msg)
350 else
342 else
351 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
343 full_messages << "&#171; " + (l_has_string?("field_" + attr) ? l("field_" + attr) : object.class.human_attribute_name(attr)) + " &#187; " + l(msg) unless attr == "custom_values"
352 end
344 end
353 end
345 end
354 # retrieve custom values error messages
346 # retrieve custom values error messages
355 if object.errors[:custom_values]
347 if object.errors[:custom_values]
356 object.custom_values.each do |v|
348 object.custom_values.each do |v|
357 v.errors.each do |attr, msg|
349 v.errors.each do |attr, msg|
358 next if msg.nil?
350 next if msg.nil?
359 msg = msg.first if msg.is_a? Array
351 msg = msg.first if msg.is_a? Array
360 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
352 full_messages << "&#171; " + v.custom_field.name + " &#187; " + l(msg)
361 end
353 end
362 end
354 end
363 end
355 end
364 content_tag("div",
356 content_tag("div",
365 content_tag(
357 content_tag(
366 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
358 options[:header_tag] || "span", lwr(:gui_validation_error, full_messages.length) + ":"
367 ) +
359 ) +
368 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
360 content_tag("ul", full_messages.collect { |msg| content_tag("li", msg) }),
369 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
361 "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
370 )
362 )
371 else
363 else
372 ""
364 ""
373 end
365 end
374 end
366 end
375
367
376 def lang_options_for_select(blank=true)
368 def lang_options_for_select(blank=true)
377 (blank ? [["(auto)", ""]] : []) +
369 (blank ? [["(auto)", ""]] : []) +
378 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
370 GLoc.valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
379 end
371 end
380
372
381 def label_tag_for(name, option_tags = nil, options = {})
373 def label_tag_for(name, option_tags = nil, options = {})
382 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
374 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
383 content_tag("label", label_text)
375 content_tag("label", label_text)
384 end
376 end
385
377
386 def labelled_tabular_form_for(name, object, options, &proc)
378 def labelled_tabular_form_for(name, object, options, &proc)
387 options[:html] ||= {}
379 options[:html] ||= {}
388 options[:html].store :class, "tabular"
380 options[:html].store :class, "tabular"
389 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
381 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
390 end
382 end
391
383
392 def check_all_links(form_name)
384 def check_all_links(form_name)
393 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
385 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
394 " | " +
386 " | " +
395 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
387 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
396 end
388 end
397
389
398 def progress_bar(pcts, options={})
390 def progress_bar(pcts, options={})
399 pcts = [pcts, pcts] unless pcts.is_a?(Array)
391 pcts = [pcts, pcts] unless pcts.is_a?(Array)
400 pcts[1] = pcts[1] - pcts[0]
392 pcts[1] = pcts[1] - pcts[0]
401 pcts << (100 - pcts[1] - pcts[0])
393 pcts << (100 - pcts[1] - pcts[0])
402 width = options[:width] || '100px;'
394 width = options[:width] || '100px;'
403 legend = options[:legend] || ''
395 legend = options[:legend] || ''
404 content_tag('table',
396 content_tag('table',
405 content_tag('tr',
397 content_tag('tr',
406 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
398 (pcts[0] > 0 ? content_tag('td', '', :width => "#{pcts[0].floor}%;", :class => 'closed') : '') +
407 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
399 (pcts[1] > 0 ? content_tag('td', '', :width => "#{pcts[1].floor}%;", :class => 'done') : '') +
408 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
400 (pcts[2] > 0 ? content_tag('td', '', :width => "#{pcts[2].floor}%;", :class => 'todo') : '')
409 ), :class => 'progress', :style => "width: #{width};") +
401 ), :class => 'progress', :style => "width: #{width};") +
410 content_tag('p', legend, :class => 'pourcent')
402 content_tag('p', legend, :class => 'pourcent')
411 end
403 end
412
404
413 def context_menu_link(name, url, options={})
405 def context_menu_link(name, url, options={})
414 options[:class] ||= ''
406 options[:class] ||= ''
415 if options.delete(:selected)
407 if options.delete(:selected)
416 options[:class] << ' icon-checked disabled'
408 options[:class] << ' icon-checked disabled'
417 options[:disabled] = true
409 options[:disabled] = true
418 end
410 end
419 if options.delete(:disabled)
411 if options.delete(:disabled)
420 options.delete(:method)
412 options.delete(:method)
421 options.delete(:confirm)
413 options.delete(:confirm)
422 options.delete(:onclick)
414 options.delete(:onclick)
423 options[:class] << ' disabled'
415 options[:class] << ' disabled'
424 url = '#'
416 url = '#'
425 end
417 end
426 link_to name, url, options
418 link_to name, url, options
427 end
419 end
428
420
429 def calendar_for(field_id)
421 def calendar_for(field_id)
430 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
422 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
431 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
423 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
432 end
424 end
433
425
434 def wikitoolbar_for(field_id)
426 def wikitoolbar_for(field_id)
435 return '' unless Setting.text_formatting == 'textile'
427 return '' unless Setting.text_formatting == 'textile'
436 javascript_include_tag('jstoolbar/jstoolbar') +
428 javascript_include_tag('jstoolbar/jstoolbar') +
437 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
429 javascript_include_tag("jstoolbar/lang/jstoolbar-#{current_language}") +
438 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
430 javascript_tag("var toolbar = new jsToolBar($('#{field_id}')); toolbar.draw();")
439 end
431 end
440
432
441 def content_for(name, content = nil, &block)
433 def content_for(name, content = nil, &block)
442 @has_content ||= {}
434 @has_content ||= {}
443 @has_content[name] = true
435 @has_content[name] = true
444 super(name, content, &block)
436 super(name, content, &block)
445 end
437 end
446
438
447 def has_content?(name)
439 def has_content?(name)
448 (@has_content && @has_content[name]) || false
440 (@has_content && @has_content[name]) || false
449 end
441 end
450 end
442 end
@@ -1,75 +1,65
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 <head>
3 <head>
4 <title><%=h html_title %></title>
4 <title><%=h html_title %></title>
5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
6 <meta name="description" content="<%= Redmine::Info.app_name %>" />
6 <meta name="description" content="<%= Redmine::Info.app_name %>" />
7 <meta name="keywords" content="issue,bug,tracker" />
7 <meta name="keywords" content="issue,bug,tracker" />
8 <%= stylesheet_link_tag 'application', :media => 'all' %>
8 <%= stylesheet_link_tag 'application', :media => 'all' %>
9 <%= javascript_include_tag :defaults %>
9 <%= javascript_include_tag :defaults %>
10 <%= stylesheet_link_tag 'jstoolbar' %>
10 <%= stylesheet_link_tag 'jstoolbar' %>
11 <!--[if IE]>
11 <!--[if IE]>
12 <style type="text/css">
12 <style type="text/css">
13 * html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); }
13 * html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); }
14 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
14 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
15 </style>
15 </style>
16 <![endif]-->
16 <![endif]-->
17
17
18 <!-- page specific tags --><%= yield :header_tags %>
18 <!-- page specific tags --><%= yield :header_tags %>
19 </head>
19 </head>
20 <body>
20 <body>
21 <div id="wrapper">
21 <div id="wrapper">
22 <div id="top-menu">
22 <div id="top-menu">
23 <div id="account">
23 <div id="account">
24 <% if User.current.logged? %>
24 <%= render_menu :account_menu -%>
25 <%=l(:label_logged_as)%> <%= User.current.login %> -
26 <%= link_to l(:label_my_account), { :controller => 'my', :action => 'account' }, :class => 'myaccount' %>
27 <%= link_to_signout %>
28 <% else %>
29 <%= link_to_signin %>
30 <%= link_to(l(:label_register), { :controller => 'account',:action => 'register' }, :class => 'register') if Setting.self_registration? %>
31 <% end %>
32 </div>
25 </div>
33 <%= link_to l(:label_home), home_url, :class => 'home' %>
26 <%= content_tag('div', "#{l(:label_logged_as)} #{User.current.login}", :id => 'loggedas') if User.current.logged? %>
34 <%= link_to l(:label_my_page), { :controller => 'my', :action => 'page'}, :class => 'mypage' if User.current.logged? %>
27 <%= render_menu :top_menu -%>
35 <%= link_to l(:label_project_plural), { :controller => 'projects' }, :class => 'projects' %>
36 <%= link_to l(:label_administration), { :controller => 'admin' }, :class => 'admin' if User.current.admin? %>
37 <%= link_to l(:label_help), Redmine::Info.help_url, :class => 'help' %>
38 </div>
28 </div>
39
29
40 <div id="header">
30 <div id="header">
41 <div id="quick-search">
31 <div id="quick-search">
42 <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
32 <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
43 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
33 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
44 <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
34 <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
45 <% end %>
35 <% end %>
46 <%= render :partial => 'layouts/project_selector' if User.current.memberships.any? %>
36 <%= render :partial => 'layouts/project_selector' if User.current.memberships.any? %>
47 </div>
37 </div>
48
38
49 <h1><%= h(@project ? @project.name : Setting.app_title) %></h1>
39 <h1><%= h(@project ? @project.name : Setting.app_title) %></h1>
50
40
51 <div id="main-menu">
41 <div id="main-menu">
52 <%= render_main_menu(@project) %>
42 <%= render_main_menu(@project) %>
53 </div>
43 </div>
54 </div>
44 </div>
55
45
56 <%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
46 <%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
57 <div id="sidebar">
47 <div id="sidebar">
58 <%= yield :sidebar %>
48 <%= yield :sidebar %>
59 </div>
49 </div>
60
50
61 <div id="content">
51 <div id="content">
62 <%= content_tag('div', flash[:error], :class => 'flash error') if flash[:error] %>
52 <%= content_tag('div', flash[:error], :class => 'flash error') if flash[:error] %>
63 <%= content_tag('div', flash[:notice], :class => 'flash notice') if flash[:notice] %>
53 <%= content_tag('div', flash[:notice], :class => 'flash notice') if flash[:notice] %>
64 <%= yield %>
54 <%= yield %>
65 </div>
55 </div>
66 </div>
56 </div>
67
57
68 <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
58 <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
69
59
70 <div id="footer">
60 <div id="footer">
71 Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> <%= Redmine::VERSION %> &copy; 2006-2008 Jean-Philippe Lang
61 Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> <%= Redmine::VERSION %> &copy; 2006-2008 Jean-Philippe Lang
72 </div>
62 </div>
73 </div>
63 </div>
74 </body>
64 </body>
75 </html>
65 </html>
@@ -1,38 +1,40
1 ActionController::Routing::Routes.draw do |map|
1 ActionController::Routing::Routes.draw do |map|
2 # Add your own custom routes here.
2 # Add your own custom routes here.
3 # The priority is based upon order of creation: first created -> highest priority.
3 # The priority is based upon order of creation: first created -> highest priority.
4
4
5 # Here's a sample route:
5 # Here's a sample route:
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 # Keep in mind you can assign values other than :controller and :action
7 # Keep in mind you can assign values other than :controller and :action
8
8
9 map.home '', :controller => 'welcome'
9 map.home '', :controller => 'welcome'
10 map.signin 'login', :controller => 'account', :action => 'login'
11 map.signout 'logout', :controller => 'account', :action => 'logout'
10
12
11 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
13 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
12 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
13 map.connect 'help/:ctrl/:page', :controller => 'help'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
14 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
16 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
15
17
16 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
18 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
17 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
19 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
18 map.connect 'projects/:project_id/news/:action', :controller => 'news'
20 map.connect 'projects/:project_id/news/:action', :controller => 'news'
19 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
21 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
20 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
22 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
21 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
23 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
22
24
23 map.with_options :controller => 'repositories' do |omap|
25 map.with_options :controller => 'repositories' do |omap|
24 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
26 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
25 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
27 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
26 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
28 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
27 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
29 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
28 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
30 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
29 end
31 end
30
32
31 # Allow downloading Web Service WSDL as a file with an extension
33 # Allow downloading Web Service WSDL as a file with an extension
32 # instead of a file named 'wsdl'
34 # instead of a file named 'wsdl'
33 map.connect ':controller/service.wsdl', :action => 'wsdl'
35 map.connect ':controller/service.wsdl', :action => 'wsdl'
34
36
35
37
36 # Install the default route as the lowest priority.
38 # Install the default route as the lowest priority.
37 map.connect ':controller/:action/:id'
39 map.connect ':controller/:action/:id'
38 end
40 end
@@ -1,110 +1,128
1 require 'redmine/access_control'
1 require 'redmine/access_control'
2 require 'redmine/menu_manager'
2 require 'redmine/menu_manager'
3 require 'redmine/mime_type'
3 require 'redmine/mime_type'
4 require 'redmine/themes'
4 require 'redmine/themes'
5 require 'redmine/plugin'
5 require 'redmine/plugin'
6
6
7 begin
7 begin
8 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
8 require_library_or_gem 'RMagick' unless Object.const_defined?(:Magick)
9 rescue LoadError
9 rescue LoadError
10 # RMagick is not available
10 # RMagick is not available
11 end
11 end
12
12
13 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar )
13 REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar )
14
14
15 # Permissions
15 # Permissions
16 Redmine::AccessControl.map do |map|
16 Redmine::AccessControl.map do |map|
17 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
17 map.permission :view_project, {:projects => [:show, :activity]}, :public => true
18 map.permission :search_project, {:search => :index}, :public => true
18 map.permission :search_project, {:search => :index}, :public => true
19 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
19 map.permission :edit_project, {:projects => [:settings, :edit]}, :require => :member
20 map.permission :select_project_modules, {:projects => :modules}, :require => :member
20 map.permission :select_project_modules, {:projects => :modules}, :require => :member
21 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy]}, :require => :member
21 map.permission :manage_members, {:projects => :settings, :members => [:new, :edit, :destroy]}, :require => :member
22 map.permission :manage_versions, {:projects => [:settings, :add_version], :versions => [:edit, :destroy]}, :require => :member
22 map.permission :manage_versions, {:projects => [:settings, :add_version], :versions => [:edit, :destroy]}, :require => :member
23
23
24 map.project_module :issue_tracking do |map|
24 map.project_module :issue_tracking do |map|
25 # Issue categories
25 # Issue categories
26 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
26 map.permission :manage_categories, {:projects => [:settings, :add_issue_category], :issue_categories => [:edit, :destroy]}, :require => :member
27 # Issues
27 # Issues
28 map.permission :view_issues, {:projects => [:changelog, :roadmap],
28 map.permission :view_issues, {:projects => [:changelog, :roadmap],
29 :issues => [:index, :changes, :show, :context_menu],
29 :issues => [:index, :changes, :show, :context_menu],
30 :versions => [:show, :status_by],
30 :versions => [:show, :status_by],
31 :queries => :index,
31 :queries => :index,
32 :reports => :issue_report}, :public => true
32 :reports => :issue_report}, :public => true
33 map.permission :add_issues, {:issues => :new}
33 map.permission :add_issues, {:issues => :new}
34 map.permission :edit_issues, {:issues => [:edit, :bulk_edit, :destroy_attachment]}
34 map.permission :edit_issues, {:issues => [:edit, :bulk_edit, :destroy_attachment]}
35 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
35 map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
36 map.permission :add_issue_notes, {:issues => :edit}
36 map.permission :add_issue_notes, {:issues => :edit}
37 map.permission :move_issues, {:issues => :move}, :require => :loggedin
37 map.permission :move_issues, {:issues => :move}, :require => :loggedin
38 map.permission :delete_issues, {:issues => :destroy}, :require => :member
38 map.permission :delete_issues, {:issues => :destroy}, :require => :member
39 # Queries
39 # Queries
40 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
40 map.permission :manage_public_queries, {:queries => [:new, :edit, :destroy]}, :require => :member
41 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
41 map.permission :save_queries, {:queries => [:new, :edit, :destroy]}, :require => :loggedin
42 # Gantt & calendar
42 # Gantt & calendar
43 map.permission :view_gantt, :projects => :gantt
43 map.permission :view_gantt, :projects => :gantt
44 map.permission :view_calendar, :projects => :calendar
44 map.permission :view_calendar, :projects => :calendar
45 end
45 end
46
46
47 map.project_module :time_tracking do |map|
47 map.project_module :time_tracking do |map|
48 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
48 map.permission :log_time, {:timelog => :edit}, :require => :loggedin
49 map.permission :view_time_entries, :timelog => [:details, :report]
49 map.permission :view_time_entries, :timelog => [:details, :report]
50 end
50 end
51
51
52 map.project_module :news do |map|
52 map.project_module :news do |map|
53 map.permission :manage_news, {:news => [:new, :edit, :destroy, :destroy_comment]}, :require => :member
53 map.permission :manage_news, {:news => [:new, :edit, :destroy, :destroy_comment]}, :require => :member
54 map.permission :view_news, {:news => [:index, :show]}, :public => true
54 map.permission :view_news, {:news => [:index, :show]}, :public => true
55 map.permission :comment_news, {:news => :add_comment}
55 map.permission :comment_news, {:news => :add_comment}
56 end
56 end
57
57
58 map.project_module :documents do |map|
58 map.project_module :documents do |map|
59 map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
59 map.permission :manage_documents, {:documents => [:new, :edit, :destroy, :add_attachment, :destroy_attachment]}, :require => :loggedin
60 map.permission :view_documents, :documents => [:index, :show, :download]
60 map.permission :view_documents, :documents => [:index, :show, :download]
61 end
61 end
62
62
63 map.project_module :files do |map|
63 map.project_module :files do |map|
64 map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
64 map.permission :manage_files, {:projects => :add_file, :versions => :destroy_file}, :require => :loggedin
65 map.permission :view_files, :projects => :list_files, :versions => :download
65 map.permission :view_files, :projects => :list_files, :versions => :download
66 end
66 end
67
67
68 map.project_module :wiki do |map|
68 map.project_module :wiki do |map|
69 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
69 map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
70 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
70 map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
71 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
71 map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
72 map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :annotate, :special]
72 map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :annotate, :special]
73 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment]
73 map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment]
74 end
74 end
75
75
76 map.project_module :repository do |map|
76 map.project_module :repository do |map|
77 map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
77 map.permission :manage_repository, {:repositories => [:edit, :destroy]}, :require => :member
78 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
78 map.permission :browse_repository, :repositories => [:show, :browse, :entry, :annotate, :changes, :diff, :stats, :graph]
79 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
79 map.permission :view_changesets, :repositories => [:show, :revisions, :revision]
80 end
80 end
81
81
82 map.project_module :boards do |map|
82 map.project_module :boards do |map|
83 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
83 map.permission :manage_boards, {:boards => [:new, :edit, :destroy]}, :require => :member
84 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
84 map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :public => true
85 map.permission :add_messages, {:messages => [:new, :reply]}
85 map.permission :add_messages, {:messages => [:new, :reply]}
86 map.permission :edit_messages, {:messages => :edit}, :require => :member
86 map.permission :edit_messages, {:messages => :edit}, :require => :member
87 map.permission :delete_messages, {:messages => :destroy}, :require => :member
87 map.permission :delete_messages, {:messages => :destroy}, :require => :member
88 end
88 end
89 end
89 end
90
90
91 # Project menu configuration
91 Redmine::MenuManager.map :top_menu do |menu|
92 menu.push :home, :home_url, :html => { :class => 'home' }
93 menu.push :my_page, { :controller => 'my', :action => 'page' }, :html => { :class => 'mypage' }, :if => Proc.new { User.current.logged? }
94 menu.push :projects, { :controller => 'projects', :action => 'index' }, :caption => :label_project_plural, :html => { :class => 'projects' }
95 menu.push :administration, { :controller => 'admin', :action => 'index' }, :html => { :class => 'admin' }, :if => Proc.new { User.current.admin? }
96 menu.push :help, Redmine::Info.help_url, :html => { :class => 'help' }
97 end
98
99 Redmine::MenuManager.map :account_menu do |menu|
100 menu.push :login, :signin_url, :html => { :class => 'login' }, :if => Proc.new { !User.current.logged? }
101 menu.push :register, { :controller => 'account', :action => 'register' }, :html => { :class => 'register' }, :if => Proc.new { !User.current.logged? && Setting.self_registration? }
102 menu.push :my_account, { :controller => 'my', :action => 'account' }, :html => { :class => 'myaccount' }, :if => Proc.new { User.current.logged? }
103 menu.push :logout, :signout_url, :html => { :class => 'logout' }, :if => Proc.new { User.current.logged? }
104 end
105
106 Redmine::MenuManager.map :application_menu do |menu|
107 # Empty
108 end
109
92 Redmine::MenuManager.map :project_menu do |menu|
110 Redmine::MenuManager.map :project_menu do |menu|
93 menu.push :overview, { :controller => 'projects', :action => 'show' }, :caption => :label_overview
111 menu.push :overview, { :controller => 'projects', :action => 'show' }
94 menu.push :activity, { :controller => 'projects', :action => 'activity' }, :caption => :label_activity
112 menu.push :activity, { :controller => 'projects', :action => 'activity' }
95 menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' },
113 menu.push :roadmap, { :controller => 'projects', :action => 'roadmap' },
96 :if => Proc.new { |p| p.versions.any? }, :caption => :label_roadmap
114 :if => Proc.new { |p| p.versions.any? }
97 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
115 menu.push :issues, { :controller => 'issues', :action => 'index' }, :param => :project_id, :caption => :label_issue_plural
98 menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new,
116 menu.push :new_issue, { :controller => 'issues', :action => 'new' }, :param => :project_id, :caption => :label_issue_new,
99 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
117 :html => { :accesskey => Redmine::AccessKeys.key_for(:new_issue) }
100 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
118 menu.push :news, { :controller => 'news', :action => 'index' }, :param => :project_id, :caption => :label_news_plural
101 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
119 menu.push :documents, { :controller => 'documents', :action => 'index' }, :param => :project_id, :caption => :label_document_plural
102 menu.push :wiki, { :controller => 'wiki', :action => 'index', :page => nil },
120 menu.push :wiki, { :controller => 'wiki', :action => 'index', :page => nil },
103 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }, :caption => :label_wiki
121 :if => Proc.new { |p| p.wiki && !p.wiki.new_record? }
104 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
122 menu.push :boards, { :controller => 'boards', :action => 'index', :id => nil }, :param => :project_id,
105 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
123 :if => Proc.new { |p| p.boards.any? }, :caption => :label_board_plural
106 menu.push :files, { :controller => 'projects', :action => 'list_files' }, :caption => :label_attachment_plural
124 menu.push :files, { :controller => 'projects', :action => 'list_files' }, :caption => :label_attachment_plural
107 menu.push :repository, { :controller => 'repositories', :action => 'show' },
125 menu.push :repository, { :controller => 'repositories', :action => 'show' },
108 :if => Proc.new { |p| p.repository && !p.repository.new_record? }, :caption => :label_repository
126 :if => Proc.new { |p| p.repository && !p.repository.new_record? }
109 menu.push :settings, { :controller => 'projects', :action => 'settings' }, :caption => :label_settings
127 menu.push :settings, { :controller => 'projects', :action => 'settings' }
110 end
128 end
@@ -1,125 +1,139
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 module Redmine
18 module Redmine
19 module MenuManager
19 module MenuManager
20 module MenuController
20 module MenuController
21 def self.included(base)
21 def self.included(base)
22 base.extend(ClassMethods)
22 base.extend(ClassMethods)
23 end
23 end
24
24
25 module ClassMethods
25 module ClassMethods
26 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
26 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
27 mattr_accessor :menu_items
27 mattr_accessor :menu_items
28
28
29 # Set the menu item name for a controller or specific actions
29 # Set the menu item name for a controller or specific actions
30 # Examples:
30 # Examples:
31 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
31 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
32 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
32 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
33 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
33 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
34 #
34 #
35 # The default menu item name for a controller is controller_name by default
35 # The default menu item name for a controller is controller_name by default
36 # Eg. the default menu item name for ProjectsController is :projects
36 # Eg. the default menu item name for ProjectsController is :projects
37 def menu_item(id, options = {})
37 def menu_item(id, options = {})
38 if actions = options[:only]
38 if actions = options[:only]
39 actions = [] << actions unless actions.is_a?(Array)
39 actions = [] << actions unless actions.is_a?(Array)
40 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
40 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
41 else
41 else
42 menu_items[controller_name.to_sym][:default] = id
42 menu_items[controller_name.to_sym][:default] = id
43 end
43 end
44 end
44 end
45 end
45 end
46
46
47 def menu_items
47 def menu_items
48 self.class.menu_items
48 self.class.menu_items
49 end
49 end
50
50
51 # Returns the menu item name according to the current action
51 # Returns the menu item name according to the current action
52 def current_menu_item
52 def current_menu_item
53 menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
53 menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
54 menu_items[controller_name.to_sym][:default]
54 menu_items[controller_name.to_sym][:default]
55 end
55 end
56 end
56 end
57
57
58 module MenuHelper
58 module MenuHelper
59 # Returns the current menu item name
59 # Returns the current menu item name
60 def current_menu_item
60 def current_menu_item
61 @controller.current_menu_item
61 @controller.current_menu_item
62 end
62 end
63
63
64 # Renders the application main menu as a ul element
64 # Renders the application main menu
65 def render_main_menu(project)
65 def render_main_menu(project)
66 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
67 end
68
69 def render_menu(menu, project=nil)
66 links = []
70 links = []
67 Redmine::MenuManager.allowed_items(:project_menu, User.current, project).each do |item|
71 Redmine::MenuManager.allowed_items(menu, User.current, project).each do |item|
68 unless item.condition && !item.condition.call(project)
72 unless item.condition && !item.condition.call(project)
73 url = case item.url
74 when Hash
75 project.nil? ? item.url : {item.param => project}.merge(item.url)
76 when Symbol
77 send(item.url)
78 else
79 item.url
80 end
81 #url = (project && item.url.is_a?(Hash)) ? {item.param => project}.merge(item.url) : (item.url.is_a?(Symbol) ? send(item.url) : item.url)
69 links << content_tag('li',
82 links << content_tag('li',
70 link_to(l(item.caption), {item.param => project}.merge(item.url),
83 link_to(l(item.caption), url, (current_menu_item == item.name ? item.html_options.merge(:class => 'selected') : item.html_options)))
71 (current_menu_item == item.name ? item.html_options.merge(:class => 'selected') : item.html_options)))
84 end
72 end
85 end
73 end if project && !project.new_record?
74 links.empty? ? nil : content_tag('ul', links.join("\n"))
86 links.empty? ? nil : content_tag('ul', links.join("\n"))
75 end
87 end
76 end
88 end
77
89
78 class << self
90 class << self
79 def map(menu_name)
91 def map(menu_name)
80 mapper = Mapper.new
92 mapper = Mapper.new
81 yield mapper
93 yield mapper
82 @items ||= {}
94 @items ||= {}
83 @items[menu_name.to_sym] ||= []
95 @items[menu_name.to_sym] ||= []
84 @items[menu_name.to_sym] += mapper.items
96 @items[menu_name.to_sym] += mapper.items
85 end
97 end
86
98
87 def items(menu_name)
99 def items(menu_name)
88 @items[menu_name.to_sym] || []
100 @items[menu_name.to_sym] || []
89 end
101 end
90
102
91 def allowed_items(menu_name, user, project)
103 def allowed_items(menu_name, user, project)
92 items(menu_name).select {|item| user && user.allowed_to?(item.url, project)}
104 project ? items(menu_name).select {|item| user && user.allowed_to?(item.url, project)} : items(menu_name)
93 end
105 end
94 end
106 end
95
107
96 class Mapper
108 class Mapper
97 # Adds an item at the end of the menu. Available options:
109 # Adds an item at the end of the menu. Available options:
98 # * param: the parameter name that is used for the project id (default is :id)
110 # * param: the parameter name that is used for the project id (default is :id)
99 # * condition: a proc that is called before rendering the item, the item is displayed only if it returns true
111 # * if: a proc that is called before rendering the item, the item is displayed only if it returns true
100 # * caption: the localized string key that is used as the item label
112 # * caption: the localized string key that is used as the item label
101 # * html_options: a hash of html options that are passed to link_to
113 # * html_options: a hash of html options that are passed to link_to
102 def push(name, url, options={})
114 def push(name, url, options={})
103 @items ||= []
115 items << MenuItem.new(name, url, options)
104 @items << MenuItem.new(name, url, options)
105 end
116 end
106
117
107 def items
118 def items
108 @items
119 @items ||= []
109 end
120 end
110 end
121 end
111
122
112 class MenuItem
123 class MenuItem
124 include GLoc
113 attr_reader :name, :url, :param, :condition, :caption, :html_options
125 attr_reader :name, :url, :param, :condition, :caption, :html_options
114
126
115 def initialize(name, url, options)
127 def initialize(name, url, options)
128 raise "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
129 raise "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
116 @name = name
130 @name = name
117 @url = url
131 @url = url
118 @condition = options[:if]
132 @condition = options[:if]
119 @param = options[:param] || :id
133 @param = options[:param] || :id
120 @caption = options[:caption] || name.to_s.humanize
134 @caption = options[:caption] || (l_has_string?("label_#{name}".to_sym) ? "label_#{name}".to_sym : name.to_s.humanize)
121 @html_options = options[:html] || {}
135 @html_options = options[:html] || {}
122 end
136 end
123 end
137 end
124 end
138 end
125 end
139 end
@@ -1,536 +1,546
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
1 body { font-family: Verdana, sans-serif; font-size: 12px; color:#484848; margin: 0; padding: 0; min-width: 900px; }
2
2
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
3 h1, h2, h3, h4 { font-family: "Trebuchet MS", Verdana, sans-serif;}
4 h1 {margin:0; padding:0; font-size: 24px;}
4 h1 {margin:0; padding:0; font-size: 24px;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
5 h2, .wiki h1 {font-size: 20px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
6 h3, .wiki h2 {font-size: 16px;padding: 2px 10px 1px 0px;margin: 0 0 10px 0; border-bottom: 1px solid #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 12px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 12px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
8
8
9 /***** Layout *****/
9 /***** Layout *****/
10 #wrapper {background: white;}
10 #wrapper {background: white;}
11
11
12 #top-menu {background: #2C4056;color: #fff;height:1.5em; padding: 2px 6px 0px 6px;}
12 #top-menu {background: #2C4056;color: #fff;height:1.5em; padding: 2px 6px 0px 6px;}
13 #top-menu ul {margin: 0; padding: 0;}
14 #top-menu li {
15 float:left;
16 list-style-type:none;
17 margin: 0px 0px 0px 0px;
18 padding: 0px 0px 0px 0px;
19 white-space:nowrap;
20 }
13 #top-menu a {color: #fff; padding-right: 4px;}
21 #top-menu a {color: #fff; padding-right: 4px;}
22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
23
14 #account {float:right;}
24 #account {float:right;}
15
25
16 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
26 #header {height:5.3em;margin:0;background-color:#507AAA;color:#f8f8f8; padding: 4px 8px 0px 6px; position:relative;}
17 #header a {color:#f8f8f8;}
27 #header a {color:#f8f8f8;}
18 #quick-search {float:right;}
28 #quick-search {float:right;}
19
29
20 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
30 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
21 #main-menu ul {margin: 0; padding: 0;}
31 #main-menu ul {margin: 0; padding: 0;}
22 #main-menu li {
32 #main-menu li {
23 float:left;
33 float:left;
24 list-style-type:none;
34 list-style-type:none;
25 margin: 0px 10px 0px 0px;
35 margin: 0px 10px 0px 0px;
26 padding: 0px 0px 0px 0px;
36 padding: 0px 0px 0px 0px;
27 white-space:nowrap;
37 white-space:nowrap;
28 }
38 }
29 #main-menu li a {
39 #main-menu li a {
30 display: block;
40 display: block;
31 color: #fff;
41 color: #fff;
32 text-decoration: none;
42 text-decoration: none;
33 margin: 0;
43 margin: 0;
34 padding: 4px 4px 4px 4px;
44 padding: 4px 4px 4px 4px;
35 background: #2C4056;
45 background: #2C4056;
36 }
46 }
37 #main-menu li a:hover, #main-menu li a.selected {background:#759FCF;}
47 #main-menu li a:hover, #main-menu li a.selected {background:#759FCF;}
38
48
39 #main {background: url(../images/mainbg.png) repeat-x; background-color:#EEEEEE;}
49 #main {background: url(../images/mainbg.png) repeat-x; background-color:#EEEEEE;}
40
50
41 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
51 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
42 * html #sidebar{ width: 17%; }
52 * html #sidebar{ width: 17%; }
43 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
53 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
44 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
54 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
45 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
55 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
46
56
47 #content { width: 80%; background: url(../images/contentbg.png) repeat-x; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; height:600px; min-height: 600px;}
57 #content { width: 80%; background: url(../images/contentbg.png) repeat-x; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; height:600px; min-height: 600px;}
48 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
58 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
49 html>body #content {
59 html>body #content {
50 height: auto;
60 height: auto;
51 min-height: 600px;
61 min-height: 600px;
52 }
62 }
53
63
54 #main.nosidebar #sidebar{ display: none; }
64 #main.nosidebar #sidebar{ display: none; }
55 #main.nosidebar #content{ width: auto; border-right: 0; }
65 #main.nosidebar #content{ width: auto; border-right: 0; }
56
66
57 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
67 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
58
68
59 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
69 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
60 #login-form table td {padding: 6px;}
70 #login-form table td {padding: 6px;}
61 #login-form label {font-weight: bold;}
71 #login-form label {font-weight: bold;}
62
72
63 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
73 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
64
74
65 /***** Links *****/
75 /***** Links *****/
66 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
76 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
67 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
77 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
68 a img{ border: 0; }
78 a img{ border: 0; }
69
79
70 /***** Tables *****/
80 /***** Tables *****/
71 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
81 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
72 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
82 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
73 table.list td { overflow: hidden; text-overflow: ellipsis; vertical-align: top;}
83 table.list td { overflow: hidden; text-overflow: ellipsis; vertical-align: top;}
74 table.list td.id { width: 2%; text-align: center;}
84 table.list td.id { width: 2%; text-align: center;}
75 table.list td.checkbox { width: 15px; padding: 0px;}
85 table.list td.checkbox { width: 15px; padding: 0px;}
76
86
77 tr.issue { text-align: center; white-space: nowrap; }
87 tr.issue { text-align: center; white-space: nowrap; }
78 tr.issue td.subject, tr.issue td.category { white-space: normal; }
88 tr.issue td.subject, tr.issue td.category { white-space: normal; }
79 tr.issue td.subject { text-align: left; }
89 tr.issue td.subject { text-align: left; }
80 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
90 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
81
91
82 tr.entry { border: 1px solid #f8f8f8; }
92 tr.entry { border: 1px solid #f8f8f8; }
83 tr.entry td { white-space: nowrap; }
93 tr.entry td { white-space: nowrap; }
84 tr.entry td.filename { width: 30%; }
94 tr.entry td.filename { width: 30%; }
85 tr.entry td.size { text-align: right; font-size: 90%; }
95 tr.entry td.size { text-align: right; font-size: 90%; }
86 tr.entry td.revision, tr.entry td.author { text-align: center; }
96 tr.entry td.revision, tr.entry td.author { text-align: center; }
87 tr.entry td.age { text-align: right; }
97 tr.entry td.age { text-align: right; }
88
98
89 tr.changeset td.author { text-align: center; width: 15%; }
99 tr.changeset td.author { text-align: center; width: 15%; }
90 tr.changeset td.committed_on { text-align: center; width: 15%; }
100 tr.changeset td.committed_on { text-align: center; width: 15%; }
91
101
92 tr.message { height: 2.6em; }
102 tr.message { height: 2.6em; }
93 tr.message td.last_message { font-size: 80%; }
103 tr.message td.last_message { font-size: 80%; }
94 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
104 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
95 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
105 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
96
106
97 tr.user td { width:13%; }
107 tr.user td { width:13%; }
98 tr.user td.email { width:18%; }
108 tr.user td.email { width:18%; }
99 tr.user td { white-space: nowrap; }
109 tr.user td { white-space: nowrap; }
100 tr.user.locked, tr.user.registered { color: #aaa; }
110 tr.user.locked, tr.user.registered { color: #aaa; }
101 tr.user.locked a, tr.user.registered a { color: #aaa; }
111 tr.user.locked a, tr.user.registered a { color: #aaa; }
102
112
103 table.list tbody tr:hover { background-color:#ffffdd; }
113 table.list tbody tr:hover { background-color:#ffffdd; }
104 table td {padding:2px;}
114 table td {padding:2px;}
105 table p {margin:0;}
115 table p {margin:0;}
106 .odd {background-color:#f6f7f8;}
116 .odd {background-color:#f6f7f8;}
107 .even {background-color: #fff;}
117 .even {background-color: #fff;}
108
118
109 .highlight { background-color: #FCFD8D;}
119 .highlight { background-color: #FCFD8D;}
110 .highlight.token-1 { background-color: #faa;}
120 .highlight.token-1 { background-color: #faa;}
111 .highlight.token-2 { background-color: #afa;}
121 .highlight.token-2 { background-color: #afa;}
112 .highlight.token-3 { background-color: #aaf;}
122 .highlight.token-3 { background-color: #aaf;}
113
123
114 .box{
124 .box{
115 padding:6px;
125 padding:6px;
116 margin-bottom: 10px;
126 margin-bottom: 10px;
117 background-color:#f6f6f6;
127 background-color:#f6f6f6;
118 color:#505050;
128 color:#505050;
119 line-height:1.5em;
129 line-height:1.5em;
120 border: 1px solid #e4e4e4;
130 border: 1px solid #e4e4e4;
121 }
131 }
122
132
123 div.square {
133 div.square {
124 border: 1px solid #999;
134 border: 1px solid #999;
125 float: left;
135 float: left;
126 margin: .3em .4em 0 .4em;
136 margin: .3em .4em 0 .4em;
127 overflow: hidden;
137 overflow: hidden;
128 width: .6em; height: .6em;
138 width: .6em; height: .6em;
129 }
139 }
130
140
131 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
141 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
132 .contextual input {font-size:0.9em;}
142 .contextual input {font-size:0.9em;}
133
143
134 .splitcontentleft{float:left; width:49%;}
144 .splitcontentleft{float:left; width:49%;}
135 .splitcontentright{float:right; width:49%;}
145 .splitcontentright{float:right; width:49%;}
136 form {display: inline;}
146 form {display: inline;}
137 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
147 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
138 fieldset {border: 1px solid #e4e4e4; margin:0;}
148 fieldset {border: 1px solid #e4e4e4; margin:0;}
139 legend {color: #484848;}
149 legend {color: #484848;}
140 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
150 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
141 textarea.wiki-edit { width: 99%; }
151 textarea.wiki-edit { width: 99%; }
142 li p {margin-top: 0;}
152 li p {margin-top: 0;}
143 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
153 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
144
154
145 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
155 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
146 div#issue-changesets .changeset { padding: 4px;}
156 div#issue-changesets .changeset { padding: 4px;}
147 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
157 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
148 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
158 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
149
159
150 div#activity dl { margin-left: 2em; }
160 div#activity dl { margin-left: 2em; }
151 div#activity dd { margin-bottom: 1em; }
161 div#activity dd { margin-bottom: 1em; }
152 div#activity dt { margin-bottom: 1px; }
162 div#activity dt { margin-bottom: 1px; }
153 div#activity dt .time { color: #777; font-size: 80%; }
163 div#activity dt .time { color: #777; font-size: 80%; }
154 div#activity dd .description { font-style: italic; }
164 div#activity dd .description { font-style: italic; }
155
165
156 .autoscroll {overflow-x: auto; padding:1px; width:100%; margin-bottom: 1.2em;}
166 .autoscroll {overflow-x: auto; padding:1px; width:100%; margin-bottom: 1.2em;}
157 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
167 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
158
168
159 .pagination {font-size: 90%}
169 .pagination {font-size: 90%}
160 p.pagination {margin-top:8px;}
170 p.pagination {margin-top:8px;}
161
171
162 /***** Tabular forms ******/
172 /***** Tabular forms ******/
163 .tabular p{
173 .tabular p{
164 margin: 0;
174 margin: 0;
165 padding: 5px 0 8px 0;
175 padding: 5px 0 8px 0;
166 padding-left: 180px; /*width of left column containing the label elements*/
176 padding-left: 180px; /*width of left column containing the label elements*/
167 height: 1%;
177 height: 1%;
168 clear:left;
178 clear:left;
169 }
179 }
170
180
171 .tabular label{
181 .tabular label{
172 font-weight: bold;
182 font-weight: bold;
173 float: left;
183 float: left;
174 text-align: right;
184 text-align: right;
175 margin-left: -180px; /*width of left column*/
185 margin-left: -180px; /*width of left column*/
176 width: 175px; /*width of labels. Should be smaller than left column to create some right
186 width: 175px; /*width of labels. Should be smaller than left column to create some right
177 margin*/
187 margin*/
178 }
188 }
179
189
180 .tabular label.floating{
190 .tabular label.floating{
181 font-weight: normal;
191 font-weight: normal;
182 margin-left: 0px;
192 margin-left: 0px;
183 text-align: left;
193 text-align: left;
184 width: 200px;
194 width: 200px;
185 }
195 }
186
196
187 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
197 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
188
198
189 .tabular.settings p{ padding-left: 300px; }
199 .tabular.settings p{ padding-left: 300px; }
190 .tabular.settings label{ margin-left: -300px; width: 295px; }
200 .tabular.settings label{ margin-left: -300px; width: 295px; }
191
201
192 .required {color: #bb0000;}
202 .required {color: #bb0000;}
193 .summary {font-style: italic;}
203 .summary {font-style: italic;}
194
204
195 div.attachments p { margin:4px 0 2px 0; }
205 div.attachments p { margin:4px 0 2px 0; }
196
206
197 /***** Flash & error messages ****/
207 /***** Flash & error messages ****/
198 #errorExplanation, div.flash, .nodata {
208 #errorExplanation, div.flash, .nodata {
199 padding: 4px 4px 4px 30px;
209 padding: 4px 4px 4px 30px;
200 margin-bottom: 12px;
210 margin-bottom: 12px;
201 font-size: 1.1em;
211 font-size: 1.1em;
202 border: 2px solid;
212 border: 2px solid;
203 }
213 }
204
214
205 div.flash {margin-top: 8px;}
215 div.flash {margin-top: 8px;}
206
216
207 div.flash.error, #errorExplanation {
217 div.flash.error, #errorExplanation {
208 background: url(../images/false.png) 8px 5px no-repeat;
218 background: url(../images/false.png) 8px 5px no-repeat;
209 background-color: #ffe3e3;
219 background-color: #ffe3e3;
210 border-color: #dd0000;
220 border-color: #dd0000;
211 color: #550000;
221 color: #550000;
212 }
222 }
213
223
214 div.flash.notice {
224 div.flash.notice {
215 background: url(../images/true.png) 8px 5px no-repeat;
225 background: url(../images/true.png) 8px 5px no-repeat;
216 background-color: #dfffdf;
226 background-color: #dfffdf;
217 border-color: #9fcf9f;
227 border-color: #9fcf9f;
218 color: #005f00;
228 color: #005f00;
219 }
229 }
220
230
221 .nodata {
231 .nodata {
222 text-align: center;
232 text-align: center;
223 background-color: #FFEBC1;
233 background-color: #FFEBC1;
224 border-color: #FDBF3B;
234 border-color: #FDBF3B;
225 color: #A6750C;
235 color: #A6750C;
226 }
236 }
227
237
228 #errorExplanation ul { font-size: 0.9em;}
238 #errorExplanation ul { font-size: 0.9em;}
229
239
230 /***** Ajax indicator ******/
240 /***** Ajax indicator ******/
231 #ajax-indicator {
241 #ajax-indicator {
232 position: absolute; /* fixed not supported by IE */
242 position: absolute; /* fixed not supported by IE */
233 background-color:#eee;
243 background-color:#eee;
234 border: 1px solid #bbb;
244 border: 1px solid #bbb;
235 top:35%;
245 top:35%;
236 left:40%;
246 left:40%;
237 width:20%;
247 width:20%;
238 font-weight:bold;
248 font-weight:bold;
239 text-align:center;
249 text-align:center;
240 padding:0.6em;
250 padding:0.6em;
241 z-index:100;
251 z-index:100;
242 filter:alpha(opacity=50);
252 filter:alpha(opacity=50);
243 opacity: 0.5;
253 opacity: 0.5;
244 -khtml-opacity: 0.5;
254 -khtml-opacity: 0.5;
245 }
255 }
246
256
247 html>body #ajax-indicator { position: fixed; }
257 html>body #ajax-indicator { position: fixed; }
248
258
249 #ajax-indicator span {
259 #ajax-indicator span {
250 background-position: 0% 40%;
260 background-position: 0% 40%;
251 background-repeat: no-repeat;
261 background-repeat: no-repeat;
252 background-image: url(../images/loading.gif);
262 background-image: url(../images/loading.gif);
253 padding-left: 26px;
263 padding-left: 26px;
254 vertical-align: bottom;
264 vertical-align: bottom;
255 }
265 }
256
266
257 /***** Calendar *****/
267 /***** Calendar *****/
258 table.cal {border-collapse: collapse; width: 100%; margin: 8px 0 6px 0;border: 1px solid #d7d7d7;}
268 table.cal {border-collapse: collapse; width: 100%; margin: 8px 0 6px 0;border: 1px solid #d7d7d7;}
259 table.cal thead th {width: 14%;}
269 table.cal thead th {width: 14%;}
260 table.cal tbody tr {height: 100px;}
270 table.cal tbody tr {height: 100px;}
261 table.cal th { background-color:#EEEEEE; padding: 4px; }
271 table.cal th { background-color:#EEEEEE; padding: 4px; }
262 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
272 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
263 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
273 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
264 table.cal td.odd p.day-num {color: #bbb;}
274 table.cal td.odd p.day-num {color: #bbb;}
265 table.cal td.today {background:#ffffdd;}
275 table.cal td.today {background:#ffffdd;}
266 table.cal td.today p.day-num {font-weight: bold;}
276 table.cal td.today p.day-num {font-weight: bold;}
267
277
268 /***** Tooltips ******/
278 /***** Tooltips ******/
269 .tooltip{position:relative;z-index:24;}
279 .tooltip{position:relative;z-index:24;}
270 .tooltip:hover{z-index:25;color:#000;}
280 .tooltip:hover{z-index:25;color:#000;}
271 .tooltip span.tip{display: none; text-align:left;}
281 .tooltip span.tip{display: none; text-align:left;}
272
282
273 div.tooltip:hover span.tip{
283 div.tooltip:hover span.tip{
274 display:block;
284 display:block;
275 position:absolute;
285 position:absolute;
276 top:12px; left:24px; width:270px;
286 top:12px; left:24px; width:270px;
277 border:1px solid #555;
287 border:1px solid #555;
278 background-color:#fff;
288 background-color:#fff;
279 padding: 4px;
289 padding: 4px;
280 font-size: 0.8em;
290 font-size: 0.8em;
281 color:#505050;
291 color:#505050;
282 }
292 }
283
293
284 /***** Progress bar *****/
294 /***** Progress bar *****/
285 table.progress {
295 table.progress {
286 border: 1px solid #D7D7D7;
296 border: 1px solid #D7D7D7;
287 border-collapse: collapse;
297 border-collapse: collapse;
288 border-spacing: 0pt;
298 border-spacing: 0pt;
289 empty-cells: show;
299 empty-cells: show;
290 text-align: center;
300 text-align: center;
291 float:left;
301 float:left;
292 margin: 1px 6px 1px 0px;
302 margin: 1px 6px 1px 0px;
293 }
303 }
294
304
295 table.progress td { height: 0.9em; }
305 table.progress td { height: 0.9em; }
296 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
306 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
297 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
307 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
298 table.progress td.open { background: #FFF none repeat scroll 0%; }
308 table.progress td.open { background: #FFF none repeat scroll 0%; }
299 p.pourcent {font-size: 80%;}
309 p.pourcent {font-size: 80%;}
300 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
310 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
301
311
302 div#status_by { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; }
312 div#status_by { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; }
303
313
304 /***** Tabs *****/
314 /***** Tabs *****/
305 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative;}
315 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative;}
306 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em;}
316 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em;}
307 #content .tabs>ul { bottom:-1px; } /* others */
317 #content .tabs>ul { bottom:-1px; } /* others */
308 #content .tabs ul li {
318 #content .tabs ul li {
309 float:left;
319 float:left;
310 list-style-type:none;
320 list-style-type:none;
311 white-space:nowrap;
321 white-space:nowrap;
312 margin-right:8px;
322 margin-right:8px;
313 background:#fff;
323 background:#fff;
314 }
324 }
315 #content .tabs ul li a{
325 #content .tabs ul li a{
316 display:block;
326 display:block;
317 font-size: 0.9em;
327 font-size: 0.9em;
318 text-decoration:none;
328 text-decoration:none;
319 line-height:1.3em;
329 line-height:1.3em;
320 padding:4px 6px 4px 6px;
330 padding:4px 6px 4px 6px;
321 border: 1px solid #ccc;
331 border: 1px solid #ccc;
322 border-bottom: 1px solid #bbbbbb;
332 border-bottom: 1px solid #bbbbbb;
323 background-color: #eeeeee;
333 background-color: #eeeeee;
324 color:#777;
334 color:#777;
325 font-weight:bold;
335 font-weight:bold;
326 }
336 }
327
337
328 #content .tabs ul li a:hover {
338 #content .tabs ul li a:hover {
329 background-color: #ffffdd;
339 background-color: #ffffdd;
330 text-decoration:none;
340 text-decoration:none;
331 }
341 }
332
342
333 #content .tabs ul li a.selected {
343 #content .tabs ul li a.selected {
334 background-color: #fff;
344 background-color: #fff;
335 border: 1px solid #bbbbbb;
345 border: 1px solid #bbbbbb;
336 border-bottom: 1px solid #fff;
346 border-bottom: 1px solid #fff;
337 }
347 }
338
348
339 #content .tabs ul li a.selected:hover {
349 #content .tabs ul li a.selected:hover {
340 background-color: #fff;
350 background-color: #fff;
341 }
351 }
342
352
343 /***** Diff *****/
353 /***** Diff *****/
344 .diff_out { background: #fcc; }
354 .diff_out { background: #fcc; }
345 .diff_in { background: #cfc; }
355 .diff_in { background: #cfc; }
346
356
347 /***** Wiki *****/
357 /***** Wiki *****/
348 div.wiki table {
358 div.wiki table {
349 border: 1px solid #505050;
359 border: 1px solid #505050;
350 border-collapse: collapse;
360 border-collapse: collapse;
351 }
361 }
352
362
353 div.wiki table, div.wiki td, div.wiki th {
363 div.wiki table, div.wiki td, div.wiki th {
354 border: 1px solid #bbb;
364 border: 1px solid #bbb;
355 padding: 4px;
365 padding: 4px;
356 }
366 }
357
367
358 div.wiki .external {
368 div.wiki .external {
359 background-position: 0% 60%;
369 background-position: 0% 60%;
360 background-repeat: no-repeat;
370 background-repeat: no-repeat;
361 padding-left: 12px;
371 padding-left: 12px;
362 background-image: url(../images/external.png);
372 background-image: url(../images/external.png);
363 }
373 }
364
374
365 div.wiki a.new {
375 div.wiki a.new {
366 color: #b73535;
376 color: #b73535;
367 }
377 }
368
378
369 div.wiki pre {
379 div.wiki pre {
370 margin: 1em 1em 1em 1.6em;
380 margin: 1em 1em 1em 1.6em;
371 padding: 2px;
381 padding: 2px;
372 background-color: #fafafa;
382 background-color: #fafafa;
373 border: 1px solid #dadada;
383 border: 1px solid #dadada;
374 width:95%;
384 width:95%;
375 overflow-x: auto;
385 overflow-x: auto;
376 }
386 }
377
387
378 div.wiki div.toc {
388 div.wiki div.toc {
379 background-color: #ffffdd;
389 background-color: #ffffdd;
380 border: 1px solid #e4e4e4;
390 border: 1px solid #e4e4e4;
381 padding: 4px;
391 padding: 4px;
382 line-height: 1.2em;
392 line-height: 1.2em;
383 margin-bottom: 12px;
393 margin-bottom: 12px;
384 margin-right: 12px;
394 margin-right: 12px;
385 display: table
395 display: table
386 }
396 }
387 * html div.wiki div.toc { width: 50%; } /* IE6 doesn't autosize div */
397 * html div.wiki div.toc { width: 50%; } /* IE6 doesn't autosize div */
388
398
389 div.wiki div.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
399 div.wiki div.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
390 div.wiki div.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
400 div.wiki div.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
391
401
392 div.wiki div.toc a {
402 div.wiki div.toc a {
393 display: block;
403 display: block;
394 font-size: 0.9em;
404 font-size: 0.9em;
395 font-weight: normal;
405 font-weight: normal;
396 text-decoration: none;
406 text-decoration: none;
397 color: #606060;
407 color: #606060;
398 }
408 }
399 div.wiki div.toc a:hover { color: #c61a1a; text-decoration: underline;}
409 div.wiki div.toc a:hover { color: #c61a1a; text-decoration: underline;}
400
410
401 div.wiki div.toc a.heading2 { margin-left: 6px; }
411 div.wiki div.toc a.heading2 { margin-left: 6px; }
402 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
412 div.wiki div.toc a.heading3 { margin-left: 12px; font-size: 0.8em; }
403
413
404 /***** My page layout *****/
414 /***** My page layout *****/
405 .block-receiver {
415 .block-receiver {
406 border:1px dashed #c0c0c0;
416 border:1px dashed #c0c0c0;
407 margin-bottom: 20px;
417 margin-bottom: 20px;
408 padding: 15px 0 15px 0;
418 padding: 15px 0 15px 0;
409 }
419 }
410
420
411 .mypage-box {
421 .mypage-box {
412 margin:0 0 20px 0;
422 margin:0 0 20px 0;
413 color:#505050;
423 color:#505050;
414 line-height:1.5em;
424 line-height:1.5em;
415 }
425 }
416
426
417 .handle {
427 .handle {
418 cursor: move;
428 cursor: move;
419 }
429 }
420
430
421 a.close-icon {
431 a.close-icon {
422 display:block;
432 display:block;
423 margin-top:3px;
433 margin-top:3px;
424 overflow:hidden;
434 overflow:hidden;
425 width:12px;
435 width:12px;
426 height:12px;
436 height:12px;
427 background-repeat: no-repeat;
437 background-repeat: no-repeat;
428 cursor:pointer;
438 cursor:pointer;
429 background-image:url('../images/close.png');
439 background-image:url('../images/close.png');
430 }
440 }
431
441
432 a.close-icon:hover {
442 a.close-icon:hover {
433 background-image:url('../images/close_hl.png');
443 background-image:url('../images/close_hl.png');
434 }
444 }
435
445
436 /***** Gantt chart *****/
446 /***** Gantt chart *****/
437 .gantt_hdr {
447 .gantt_hdr {
438 position:absolute;
448 position:absolute;
439 top:0;
449 top:0;
440 height:16px;
450 height:16px;
441 border-top: 1px solid #c0c0c0;
451 border-top: 1px solid #c0c0c0;
442 border-bottom: 1px solid #c0c0c0;
452 border-bottom: 1px solid #c0c0c0;
443 border-right: 1px solid #c0c0c0;
453 border-right: 1px solid #c0c0c0;
444 text-align: center;
454 text-align: center;
445 overflow: hidden;
455 overflow: hidden;
446 }
456 }
447
457
448 .task {
458 .task {
449 position: absolute;
459 position: absolute;
450 height:8px;
460 height:8px;
451 font-size:0.8em;
461 font-size:0.8em;
452 color:#888;
462 color:#888;
453 padding:0;
463 padding:0;
454 margin:0;
464 margin:0;
455 line-height:0.8em;
465 line-height:0.8em;
456 }
466 }
457
467
458 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
468 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
459 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
469 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
460 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
470 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
461 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
471 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
462
472
463 /***** Icons *****/
473 /***** Icons *****/
464 .icon {
474 .icon {
465 background-position: 0% 40%;
475 background-position: 0% 40%;
466 background-repeat: no-repeat;
476 background-repeat: no-repeat;
467 padding-left: 20px;
477 padding-left: 20px;
468 padding-top: 2px;
478 padding-top: 2px;
469 padding-bottom: 3px;
479 padding-bottom: 3px;
470 }
480 }
471
481
472 .icon22 {
482 .icon22 {
473 background-position: 0% 40%;
483 background-position: 0% 40%;
474 background-repeat: no-repeat;
484 background-repeat: no-repeat;
475 padding-left: 26px;
485 padding-left: 26px;
476 line-height: 22px;
486 line-height: 22px;
477 vertical-align: middle;
487 vertical-align: middle;
478 }
488 }
479
489
480 .icon-add { background-image: url(../images/add.png); }
490 .icon-add { background-image: url(../images/add.png); }
481 .icon-edit { background-image: url(../images/edit.png); }
491 .icon-edit { background-image: url(../images/edit.png); }
482 .icon-copy { background-image: url(../images/copy.png); }
492 .icon-copy { background-image: url(../images/copy.png); }
483 .icon-del { background-image: url(../images/delete.png); }
493 .icon-del { background-image: url(../images/delete.png); }
484 .icon-move { background-image: url(../images/move.png); }
494 .icon-move { background-image: url(../images/move.png); }
485 .icon-save { background-image: url(../images/save.png); }
495 .icon-save { background-image: url(../images/save.png); }
486 .icon-cancel { background-image: url(../images/cancel.png); }
496 .icon-cancel { background-image: url(../images/cancel.png); }
487 .icon-pdf { background-image: url(../images/pdf.png); }
497 .icon-pdf { background-image: url(../images/pdf.png); }
488 .icon-csv { background-image: url(../images/csv.png); }
498 .icon-csv { background-image: url(../images/csv.png); }
489 .icon-html { background-image: url(../images/html.png); }
499 .icon-html { background-image: url(../images/html.png); }
490 .icon-image { background-image: url(../images/image.png); }
500 .icon-image { background-image: url(../images/image.png); }
491 .icon-txt { background-image: url(../images/txt.png); }
501 .icon-txt { background-image: url(../images/txt.png); }
492 .icon-file { background-image: url(../images/file.png); }
502 .icon-file { background-image: url(../images/file.png); }
493 .icon-folder { background-image: url(../images/folder.png); }
503 .icon-folder { background-image: url(../images/folder.png); }
494 .open .icon-folder { background-image: url(../images/folder_open.png); }
504 .open .icon-folder { background-image: url(../images/folder_open.png); }
495 .icon-package { background-image: url(../images/package.png); }
505 .icon-package { background-image: url(../images/package.png); }
496 .icon-home { background-image: url(../images/home.png); }
506 .icon-home { background-image: url(../images/home.png); }
497 .icon-user { background-image: url(../images/user.png); }
507 .icon-user { background-image: url(../images/user.png); }
498 .icon-mypage { background-image: url(../images/user_page.png); }
508 .icon-mypage { background-image: url(../images/user_page.png); }
499 .icon-admin { background-image: url(../images/admin.png); }
509 .icon-admin { background-image: url(../images/admin.png); }
500 .icon-projects { background-image: url(../images/projects.png); }
510 .icon-projects { background-image: url(../images/projects.png); }
501 .icon-logout { background-image: url(../images/logout.png); }
511 .icon-logout { background-image: url(../images/logout.png); }
502 .icon-help { background-image: url(../images/help.png); }
512 .icon-help { background-image: url(../images/help.png); }
503 .icon-attachment { background-image: url(../images/attachment.png); }
513 .icon-attachment { background-image: url(../images/attachment.png); }
504 .icon-index { background-image: url(../images/index.png); }
514 .icon-index { background-image: url(../images/index.png); }
505 .icon-history { background-image: url(../images/history.png); }
515 .icon-history { background-image: url(../images/history.png); }
506 .icon-feed { background-image: url(../images/feed.png); }
516 .icon-feed { background-image: url(../images/feed.png); }
507 .icon-time { background-image: url(../images/time.png); }
517 .icon-time { background-image: url(../images/time.png); }
508 .icon-stats { background-image: url(../images/stats.png); }
518 .icon-stats { background-image: url(../images/stats.png); }
509 .icon-warning { background-image: url(../images/warning.png); }
519 .icon-warning { background-image: url(../images/warning.png); }
510 .icon-fav { background-image: url(../images/fav.png); }
520 .icon-fav { background-image: url(../images/fav.png); }
511 .icon-fav-off { background-image: url(../images/fav_off.png); }
521 .icon-fav-off { background-image: url(../images/fav_off.png); }
512 .icon-reload { background-image: url(../images/reload.png); }
522 .icon-reload { background-image: url(../images/reload.png); }
513 .icon-lock { background-image: url(../images/locked.png); }
523 .icon-lock { background-image: url(../images/locked.png); }
514 .icon-unlock { background-image: url(../images/unlock.png); }
524 .icon-unlock { background-image: url(../images/unlock.png); }
515 .icon-checked { background-image: url(../images/true.png); }
525 .icon-checked { background-image: url(../images/true.png); }
516
526
517 .icon22-projects { background-image: url(../images/22x22/projects.png); }
527 .icon22-projects { background-image: url(../images/22x22/projects.png); }
518 .icon22-users { background-image: url(../images/22x22/users.png); }
528 .icon22-users { background-image: url(../images/22x22/users.png); }
519 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
529 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
520 .icon22-role { background-image: url(../images/22x22/role.png); }
530 .icon22-role { background-image: url(../images/22x22/role.png); }
521 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
531 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
522 .icon22-options { background-image: url(../images/22x22/options.png); }
532 .icon22-options { background-image: url(../images/22x22/options.png); }
523 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
533 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
524 .icon22-authent { background-image: url(../images/22x22/authent.png); }
534 .icon22-authent { background-image: url(../images/22x22/authent.png); }
525 .icon22-info { background-image: url(../images/22x22/info.png); }
535 .icon22-info { background-image: url(../images/22x22/info.png); }
526 .icon22-comment { background-image: url(../images/22x22/comment.png); }
536 .icon22-comment { background-image: url(../images/22x22/comment.png); }
527 .icon22-package { background-image: url(../images/22x22/package.png); }
537 .icon22-package { background-image: url(../images/22x22/package.png); }
528 .icon22-settings { background-image: url(../images/22x22/settings.png); }
538 .icon22-settings { background-image: url(../images/22x22/settings.png); }
529 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
539 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
530
540
531 /***** Media print specific styles *****/
541 /***** Media print specific styles *****/
532 @media print {
542 @media print {
533 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual { display:none; }
543 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual { display:none; }
534 #main { background: #fff; }
544 #main { background: #fff; }
535 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
545 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; }
536 }
546 }
General Comments 0
You need to be logged in to leave comments. Login now