##// END OF EJS Templates
Adds an admin layout that displays the admin menu in the sidebar....
Jean-Philippe Lang -
r3062:dfabadf4f7e0
parent child
Show More
@@ -0,0 +1,8
1 <% unless controller_name == 'admin' && action_name == 'index' %>
2 <% content_for :sidebar do %>
3 <h3><%=l(:label_administration)%></h3>
4 <%= render :partial => 'admin/menu' %>
5 <% end %>
6 <% end %>
7
8 <%= render :file => "layouts/base" %>
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -1,84 +1,86
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AdminController < ApplicationController
18 class AdminController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 helper :sort
23 helper :sort
22 include SortHelper
24 include SortHelper
23
25
24 def index
26 def index
25 @no_configuration_data = Redmine::DefaultData::Loader::no_data?
27 @no_configuration_data = Redmine::DefaultData::Loader::no_data?
26 end
28 end
27
29
28 def projects
30 def projects
29 @status = params[:status] ? params[:status].to_i : 1
31 @status = params[:status] ? params[:status].to_i : 1
30 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
32 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
31
33
32 unless params[:name].blank?
34 unless params[:name].blank?
33 name = "%#{params[:name].strip.downcase}%"
35 name = "%#{params[:name].strip.downcase}%"
34 c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
36 c << ["LOWER(identifier) LIKE ? OR LOWER(name) LIKE ?", name, name]
35 end
37 end
36
38
37 @projects = Project.find :all, :order => 'lft',
39 @projects = Project.find :all, :order => 'lft',
38 :conditions => c.conditions
40 :conditions => c.conditions
39
41
40 render :action => "projects", :layout => false if request.xhr?
42 render :action => "projects", :layout => false if request.xhr?
41 end
43 end
42
44
43 def plugins
45 def plugins
44 @plugins = Redmine::Plugin.all
46 @plugins = Redmine::Plugin.all
45 end
47 end
46
48
47 # Loads the default configuration
49 # Loads the default configuration
48 # (roles, trackers, statuses, workflow, enumerations)
50 # (roles, trackers, statuses, workflow, enumerations)
49 def default_configuration
51 def default_configuration
50 if request.post?
52 if request.post?
51 begin
53 begin
52 Redmine::DefaultData::Loader::load(params[:lang])
54 Redmine::DefaultData::Loader::load(params[:lang])
53 flash[:notice] = l(:notice_default_data_loaded)
55 flash[:notice] = l(:notice_default_data_loaded)
54 rescue Exception => e
56 rescue Exception => e
55 flash[:error] = l(:error_can_t_load_default_data, e.message)
57 flash[:error] = l(:error_can_t_load_default_data, e.message)
56 end
58 end
57 end
59 end
58 redirect_to :action => 'index'
60 redirect_to :action => 'index'
59 end
61 end
60
62
61 def test_email
63 def test_email
62 raise_delivery_errors = ActionMailer::Base.raise_delivery_errors
64 raise_delivery_errors = ActionMailer::Base.raise_delivery_errors
63 # Force ActionMailer to raise delivery errors so we can catch it
65 # Force ActionMailer to raise delivery errors so we can catch it
64 ActionMailer::Base.raise_delivery_errors = true
66 ActionMailer::Base.raise_delivery_errors = true
65 begin
67 begin
66 @test = Mailer.deliver_test(User.current)
68 @test = Mailer.deliver_test(User.current)
67 flash[:notice] = l(:notice_email_sent, User.current.mail)
69 flash[:notice] = l(:notice_email_sent, User.current.mail)
68 rescue Exception => e
70 rescue Exception => e
69 flash[:error] = l(:notice_email_error, e.message)
71 flash[:error] = l(:notice_email_error, e.message)
70 end
72 end
71 ActionMailer::Base.raise_delivery_errors = raise_delivery_errors
73 ActionMailer::Base.raise_delivery_errors = raise_delivery_errors
72 redirect_to :controller => 'settings', :action => 'edit', :tab => 'notifications'
74 redirect_to :controller => 'settings', :action => 'edit', :tab => 'notifications'
73 end
75 end
74
76
75 def info
77 def info
76 @db_adapter_name = ActiveRecord::Base.connection.adapter_name
78 @db_adapter_name = ActiveRecord::Base.connection.adapter_name
77 @flags = {
79 @flags = {
78 :default_admin_changed => User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?,
80 :default_admin_changed => User.find(:first, :conditions => ["login=? and hashed_password=?", 'admin', User.hash_password('admin')]).nil?,
79 :file_repository_writable => File.writable?(Attachment.storage_path),
81 :file_repository_writable => File.writable?(Attachment.storage_path),
80 :plugin_assets_writable => File.writable?(Engines.public_directory),
82 :plugin_assets_writable => File.writable?(Engines.public_directory),
81 :rmagick_available => Object.const_defined?(:Magick)
83 :rmagick_available => Object.const_defined?(:Magick)
82 }
84 }
83 end
85 end
84 end
86 end
@@ -1,276 +1,276
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'uri'
18 require 'uri'
19 require 'cgi'
19 require 'cgi'
20
20
21 class ApplicationController < ActionController::Base
21 class ApplicationController < ActionController::Base
22 include Redmine::I18n
22 include Redmine::I18n
23
23
24 layout 'base'
24 layout 'base'
25
25
26 # Remove broken cookie after upgrade from 0.8.x (#4292)
26 # Remove broken cookie after upgrade from 0.8.x (#4292)
27 # See https://rails.lighthouseapp.com/projects/8994/tickets/3360
27 # See https://rails.lighthouseapp.com/projects/8994/tickets/3360
28 # TODO: remove it when Rails is fixed
28 # TODO: remove it when Rails is fixed
29 before_filter :delete_broken_cookies
29 before_filter :delete_broken_cookies
30 def delete_broken_cookies
30 def delete_broken_cookies
31 if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
31 if cookies['_redmine_session'] && cookies['_redmine_session'] !~ /--/
32 cookies.delete '_redmine_session'
32 cookies.delete '_redmine_session'
33 redirect_to home_path and return false
33 redirect_to home_path and return false
34 end
34 end
35 end
35 end
36
36
37 before_filter :user_setup, :check_if_login_required, :set_localization
37 before_filter :user_setup, :check_if_login_required, :set_localization
38 filter_parameter_logging :password
38 filter_parameter_logging :password
39 protect_from_forgery
39 protect_from_forgery
40
40
41 rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
41 rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
42
42
43 include Redmine::Search::Controller
43 include Redmine::Search::Controller
44 include Redmine::MenuManager::MenuController
44 include Redmine::MenuManager::MenuController
45 helper Redmine::MenuManager::MenuHelper
45 helper Redmine::MenuManager::MenuHelper
46
46
47 REDMINE_SUPPORTED_SCM.each do |scm|
47 REDMINE_SUPPORTED_SCM.each do |scm|
48 require_dependency "repository/#{scm.underscore}"
48 require_dependency "repository/#{scm.underscore}"
49 end
49 end
50
50
51 def user_setup
51 def user_setup
52 # Check the settings cache for each request
52 # Check the settings cache for each request
53 Setting.check_cache
53 Setting.check_cache
54 # Find the current user
54 # Find the current user
55 User.current = find_current_user
55 User.current = find_current_user
56 end
56 end
57
57
58 # Returns the current user or nil if no user is logged in
58 # Returns the current user or nil if no user is logged in
59 # and starts a session if needed
59 # and starts a session if needed
60 def find_current_user
60 def find_current_user
61 if session[:user_id]
61 if session[:user_id]
62 # existing session
62 # existing session
63 (User.active.find(session[:user_id]) rescue nil)
63 (User.active.find(session[:user_id]) rescue nil)
64 elsif cookies[:autologin] && Setting.autologin?
64 elsif cookies[:autologin] && Setting.autologin?
65 # auto-login feature starts a new session
65 # auto-login feature starts a new session
66 user = User.try_to_autologin(cookies[:autologin])
66 user = User.try_to_autologin(cookies[:autologin])
67 session[:user_id] = user.id if user
67 session[:user_id] = user.id if user
68 user
68 user
69 elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
69 elsif params[:format] == 'atom' && params[:key] && accept_key_auth_actions.include?(params[:action])
70 # RSS key authentication does not start a session
70 # RSS key authentication does not start a session
71 User.find_by_rss_key(params[:key])
71 User.find_by_rss_key(params[:key])
72 end
72 end
73 end
73 end
74
74
75 # Sets the logged in user
75 # Sets the logged in user
76 def logged_user=(user)
76 def logged_user=(user)
77 reset_session
77 reset_session
78 if user && user.is_a?(User)
78 if user && user.is_a?(User)
79 User.current = user
79 User.current = user
80 session[:user_id] = user.id
80 session[:user_id] = user.id
81 else
81 else
82 User.current = User.anonymous
82 User.current = User.anonymous
83 end
83 end
84 end
84 end
85
85
86 # check if login is globally required to access the application
86 # check if login is globally required to access the application
87 def check_if_login_required
87 def check_if_login_required
88 # no check needed if user is already logged in
88 # no check needed if user is already logged in
89 return true if User.current.logged?
89 return true if User.current.logged?
90 require_login if Setting.login_required?
90 require_login if Setting.login_required?
91 end
91 end
92
92
93 def set_localization
93 def set_localization
94 lang = nil
94 lang = nil
95 if User.current.logged?
95 if User.current.logged?
96 lang = find_language(User.current.language)
96 lang = find_language(User.current.language)
97 end
97 end
98 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
98 if lang.nil? && request.env['HTTP_ACCEPT_LANGUAGE']
99 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.downcase
99 accept_lang = parse_qvalues(request.env['HTTP_ACCEPT_LANGUAGE']).first.downcase
100 if !accept_lang.blank?
100 if !accept_lang.blank?
101 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
101 lang = find_language(accept_lang) || find_language(accept_lang.split('-').first)
102 end
102 end
103 end
103 end
104 lang ||= Setting.default_language
104 lang ||= Setting.default_language
105 set_language_if_valid(lang)
105 set_language_if_valid(lang)
106 end
106 end
107
107
108 def require_login
108 def require_login
109 if !User.current.logged?
109 if !User.current.logged?
110 # Extract only the basic url parameters on non-GET requests
110 # Extract only the basic url parameters on non-GET requests
111 if request.get?
111 if request.get?
112 url = url_for(params)
112 url = url_for(params)
113 else
113 else
114 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
114 url = url_for(:controller => params[:controller], :action => params[:action], :id => params[:id], :project_id => params[:project_id])
115 end
115 end
116 redirect_to :controller => "account", :action => "login", :back_url => url
116 redirect_to :controller => "account", :action => "login", :back_url => url
117 return false
117 return false
118 end
118 end
119 true
119 true
120 end
120 end
121
121
122 def require_admin
122 def require_admin
123 return unless require_login
123 return unless require_login
124 if !User.current.admin?
124 if !User.current.admin?
125 render_403
125 render_403
126 return false
126 return false
127 end
127 end
128 true
128 true
129 end
129 end
130
130
131 def deny_access
131 def deny_access
132 User.current.logged? ? render_403 : require_login
132 User.current.logged? ? render_403 : require_login
133 end
133 end
134
134
135 # Authorize the user for the requested action
135 # Authorize the user for the requested action
136 def authorize(ctrl = params[:controller], action = params[:action], global = false)
136 def authorize(ctrl = params[:controller], action = params[:action], global = false)
137 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, :global => global)
137 allowed = User.current.allowed_to?({:controller => ctrl, :action => action}, @project, :global => global)
138 allowed ? true : deny_access
138 allowed ? true : deny_access
139 end
139 end
140
140
141 # Authorize the user for the requested action outside a project
141 # Authorize the user for the requested action outside a project
142 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
142 def authorize_global(ctrl = params[:controller], action = params[:action], global = true)
143 authorize(ctrl, action, global)
143 authorize(ctrl, action, global)
144 end
144 end
145
145
146 # make sure that the user is a member of the project (or admin) if project is private
146 # make sure that the user is a member of the project (or admin) if project is private
147 # used as a before_filter for actions that do not require any particular permission on the project
147 # used as a before_filter for actions that do not require any particular permission on the project
148 def check_project_privacy
148 def check_project_privacy
149 if @project && @project.active?
149 if @project && @project.active?
150 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
150 if @project.is_public? || User.current.member_of?(@project) || User.current.admin?
151 true
151 true
152 else
152 else
153 User.current.logged? ? render_403 : require_login
153 User.current.logged? ? render_403 : require_login
154 end
154 end
155 else
155 else
156 @project = nil
156 @project = nil
157 render_404
157 render_404
158 false
158 false
159 end
159 end
160 end
160 end
161
161
162 def redirect_back_or_default(default)
162 def redirect_back_or_default(default)
163 back_url = CGI.unescape(params[:back_url].to_s)
163 back_url = CGI.unescape(params[:back_url].to_s)
164 if !back_url.blank?
164 if !back_url.blank?
165 begin
165 begin
166 uri = URI.parse(back_url)
166 uri = URI.parse(back_url)
167 # do not redirect user to another host or to the login or register page
167 # do not redirect user to another host or to the login or register page
168 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
168 if (uri.relative? || (uri.host == request.host)) && !uri.path.match(%r{/(login|account/register)})
169 redirect_to(back_url) and return
169 redirect_to(back_url) and return
170 end
170 end
171 rescue URI::InvalidURIError
171 rescue URI::InvalidURIError
172 # redirect to default
172 # redirect to default
173 end
173 end
174 end
174 end
175 redirect_to default
175 redirect_to default
176 end
176 end
177
177
178 def render_403
178 def render_403
179 @project = nil
179 @project = nil
180 render :template => "common/403", :layout => !request.xhr?, :status => 403
180 render :template => "common/403", :layout => (request.xhr? ? false : 'base'), :status => 403
181 return false
181 return false
182 end
182 end
183
183
184 def render_404
184 def render_404
185 render :template => "common/404", :layout => !request.xhr?, :status => 404
185 render :template => "common/404", :layout => !request.xhr?, :status => 404
186 return false
186 return false
187 end
187 end
188
188
189 def render_error(msg)
189 def render_error(msg)
190 flash.now[:error] = msg
190 flash.now[:error] = msg
191 render :text => '', :layout => !request.xhr?, :status => 500
191 render :text => '', :layout => !request.xhr?, :status => 500
192 end
192 end
193
193
194 def invalid_authenticity_token
194 def invalid_authenticity_token
195 render_error "Invalid form authenticity token."
195 render_error "Invalid form authenticity token."
196 end
196 end
197
197
198 def render_feed(items, options={})
198 def render_feed(items, options={})
199 @items = items || []
199 @items = items || []
200 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
200 @items.sort! {|x,y| y.event_datetime <=> x.event_datetime }
201 @items = @items.slice(0, Setting.feeds_limit.to_i)
201 @items = @items.slice(0, Setting.feeds_limit.to_i)
202 @title = options[:title] || Setting.app_title
202 @title = options[:title] || Setting.app_title
203 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
203 render :template => "common/feed.atom.rxml", :layout => false, :content_type => 'application/atom+xml'
204 end
204 end
205
205
206 def self.accept_key_auth(*actions)
206 def self.accept_key_auth(*actions)
207 actions = actions.flatten.map(&:to_s)
207 actions = actions.flatten.map(&:to_s)
208 write_inheritable_attribute('accept_key_auth_actions', actions)
208 write_inheritable_attribute('accept_key_auth_actions', actions)
209 end
209 end
210
210
211 def accept_key_auth_actions
211 def accept_key_auth_actions
212 self.class.read_inheritable_attribute('accept_key_auth_actions') || []
212 self.class.read_inheritable_attribute('accept_key_auth_actions') || []
213 end
213 end
214
214
215 # TODO: move to model
215 # TODO: move to model
216 def attach_files(obj, attachments)
216 def attach_files(obj, attachments)
217 attached = []
217 attached = []
218 unsaved = []
218 unsaved = []
219 if attachments && attachments.is_a?(Hash)
219 if attachments && attachments.is_a?(Hash)
220 attachments.each_value do |attachment|
220 attachments.each_value do |attachment|
221 file = attachment['file']
221 file = attachment['file']
222 next unless file && file.size > 0
222 next unless file && file.size > 0
223 a = Attachment.create(:container => obj,
223 a = Attachment.create(:container => obj,
224 :file => file,
224 :file => file,
225 :description => attachment['description'].to_s.strip,
225 :description => attachment['description'].to_s.strip,
226 :author => User.current)
226 :author => User.current)
227 a.new_record? ? (unsaved << a) : (attached << a)
227 a.new_record? ? (unsaved << a) : (attached << a)
228 end
228 end
229 if unsaved.any?
229 if unsaved.any?
230 flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
230 flash[:warning] = l(:warning_attachments_not_saved, unsaved.size)
231 end
231 end
232 end
232 end
233 attached
233 attached
234 end
234 end
235
235
236 # Returns the number of objects that should be displayed
236 # Returns the number of objects that should be displayed
237 # on the paginated list
237 # on the paginated list
238 def per_page_option
238 def per_page_option
239 per_page = nil
239 per_page = nil
240 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
240 if params[:per_page] && Setting.per_page_options_array.include?(params[:per_page].to_s.to_i)
241 per_page = params[:per_page].to_s.to_i
241 per_page = params[:per_page].to_s.to_i
242 session[:per_page] = per_page
242 session[:per_page] = per_page
243 elsif session[:per_page]
243 elsif session[:per_page]
244 per_page = session[:per_page]
244 per_page = session[:per_page]
245 else
245 else
246 per_page = Setting.per_page_options_array.first || 25
246 per_page = Setting.per_page_options_array.first || 25
247 end
247 end
248 per_page
248 per_page
249 end
249 end
250
250
251 # qvalues http header parser
251 # qvalues http header parser
252 # code taken from webrick
252 # code taken from webrick
253 def parse_qvalues(value)
253 def parse_qvalues(value)
254 tmp = []
254 tmp = []
255 if value
255 if value
256 parts = value.split(/,\s*/)
256 parts = value.split(/,\s*/)
257 parts.each {|part|
257 parts.each {|part|
258 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
258 if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
259 val = m[1]
259 val = m[1]
260 q = (m[2] or 1).to_f
260 q = (m[2] or 1).to_f
261 tmp.push([val, q])
261 tmp.push([val, q])
262 end
262 end
263 }
263 }
264 tmp = tmp.sort_by{|val, q| -q}
264 tmp = tmp.sort_by{|val, q| -q}
265 tmp.collect!{|val, q| val}
265 tmp.collect!{|val, q| val}
266 end
266 end
267 return tmp
267 return tmp
268 rescue
268 rescue
269 nil
269 nil
270 end
270 end
271
271
272 # Returns a string that can be used as filename value in Content-Disposition header
272 # Returns a string that can be used as filename value in Content-Disposition header
273 def filename_for_content_disposition(name)
273 def filename_for_content_disposition(name)
274 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
274 request.env['HTTP_USER_AGENT'] =~ %r{MSIE} ? ERB::Util.url_encode(name) : name
275 end
275 end
276 end
276 end
@@ -1,82 +1,84
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AuthSourcesController < ApplicationController
18 class AuthSourcesController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 def index
23 def index
22 list
24 list
23 render :action => 'list' unless request.xhr?
25 render :action => 'list' unless request.xhr?
24 end
26 end
25
27
26 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
28 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
27 verify :method => :post, :only => [ :destroy, :create, :update ],
29 verify :method => :post, :only => [ :destroy, :create, :update ],
28 :redirect_to => { :action => :list }
30 :redirect_to => { :action => :list }
29
31
30 def list
32 def list
31 @auth_source_pages, @auth_sources = paginate :auth_sources, :per_page => 10
33 @auth_source_pages, @auth_sources = paginate :auth_sources, :per_page => 10
32 render :action => "list", :layout => false if request.xhr?
34 render :action => "list", :layout => false if request.xhr?
33 end
35 end
34
36
35 def new
37 def new
36 @auth_source = AuthSourceLdap.new
38 @auth_source = AuthSourceLdap.new
37 end
39 end
38
40
39 def create
41 def create
40 @auth_source = AuthSourceLdap.new(params[:auth_source])
42 @auth_source = AuthSourceLdap.new(params[:auth_source])
41 if @auth_source.save
43 if @auth_source.save
42 flash[:notice] = l(:notice_successful_create)
44 flash[:notice] = l(:notice_successful_create)
43 redirect_to :action => 'list'
45 redirect_to :action => 'list'
44 else
46 else
45 render :action => 'new'
47 render :action => 'new'
46 end
48 end
47 end
49 end
48
50
49 def edit
51 def edit
50 @auth_source = AuthSource.find(params[:id])
52 @auth_source = AuthSource.find(params[:id])
51 end
53 end
52
54
53 def update
55 def update
54 @auth_source = AuthSource.find(params[:id])
56 @auth_source = AuthSource.find(params[:id])
55 if @auth_source.update_attributes(params[:auth_source])
57 if @auth_source.update_attributes(params[:auth_source])
56 flash[:notice] = l(:notice_successful_update)
58 flash[:notice] = l(:notice_successful_update)
57 redirect_to :action => 'list'
59 redirect_to :action => 'list'
58 else
60 else
59 render :action => 'edit'
61 render :action => 'edit'
60 end
62 end
61 end
63 end
62
64
63 def test_connection
65 def test_connection
64 @auth_method = AuthSource.find(params[:id])
66 @auth_method = AuthSource.find(params[:id])
65 begin
67 begin
66 @auth_method.test_connection
68 @auth_method.test_connection
67 flash[:notice] = l(:notice_successful_connection)
69 flash[:notice] = l(:notice_successful_connection)
68 rescue => text
70 rescue => text
69 flash[:error] = "Unable to connect (#{text})"
71 flash[:error] = "Unable to connect (#{text})"
70 end
72 end
71 redirect_to :action => 'list'
73 redirect_to :action => 'list'
72 end
74 end
73
75
74 def destroy
76 def destroy
75 @auth_source = AuthSource.find(params[:id])
77 @auth_source = AuthSource.find(params[:id])
76 unless @auth_source.users.find(:first)
78 unless @auth_source.users.find(:first)
77 @auth_source.destroy
79 @auth_source.destroy
78 flash[:notice] = l(:notice_successful_delete)
80 flash[:notice] = l(:notice_successful_delete)
79 end
81 end
80 redirect_to :action => 'list'
82 redirect_to :action => 'list'
81 end
83 end
82 end
84 end
@@ -1,60 +1,62
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class CustomFieldsController < ApplicationController
18 class CustomFieldsController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 def index
23 def index
22 @custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
24 @custom_fields_by_type = CustomField.find(:all).group_by {|f| f.class.name }
23 @tab = params[:tab] || 'IssueCustomField'
25 @tab = params[:tab] || 'IssueCustomField'
24 end
26 end
25
27
26 def new
28 def new
27 @custom_field = begin
29 @custom_field = begin
28 if params[:type].to_s.match(/.+CustomField$/)
30 if params[:type].to_s.match(/.+CustomField$/)
29 params[:type].to_s.constantize.new(params[:custom_field])
31 params[:type].to_s.constantize.new(params[:custom_field])
30 end
32 end
31 rescue
33 rescue
32 end
34 end
33 redirect_to(:action => 'index') and return unless @custom_field.is_a?(CustomField)
35 redirect_to(:action => 'index') and return unless @custom_field.is_a?(CustomField)
34
36
35 if request.post? and @custom_field.save
37 if request.post? and @custom_field.save
36 flash[:notice] = l(:notice_successful_create)
38 flash[:notice] = l(:notice_successful_create)
37 call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
39 call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field)
38 redirect_to :action => 'index', :tab => @custom_field.class.name
40 redirect_to :action => 'index', :tab => @custom_field.class.name
39 end
41 end
40 @trackers = Tracker.find(:all, :order => 'position')
42 @trackers = Tracker.find(:all, :order => 'position')
41 end
43 end
42
44
43 def edit
45 def edit
44 @custom_field = CustomField.find(params[:id])
46 @custom_field = CustomField.find(params[:id])
45 if request.post? and @custom_field.update_attributes(params[:custom_field])
47 if request.post? and @custom_field.update_attributes(params[:custom_field])
46 flash[:notice] = l(:notice_successful_update)
48 flash[:notice] = l(:notice_successful_update)
47 call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
49 call_hook(:controller_custom_fields_edit_after_save, :params => params, :custom_field => @custom_field)
48 redirect_to :action => 'index', :tab => @custom_field.class.name
50 redirect_to :action => 'index', :tab => @custom_field.class.name
49 end
51 end
50 @trackers = Tracker.find(:all, :order => 'position')
52 @trackers = Tracker.find(:all, :order => 'position')
51 end
53 end
52
54
53 def destroy
55 def destroy
54 @custom_field = CustomField.find(params[:id]).destroy
56 @custom_field = CustomField.find(params[:id]).destroy
55 redirect_to :action => 'index', :tab => @custom_field.class.name
57 redirect_to :action => 'index', :tab => @custom_field.class.name
56 rescue
58 rescue
57 flash[:error] = "Unable to delete custom field"
59 flash[:error] = "Unable to delete custom field"
58 redirect_to :action => 'index'
60 redirect_to :action => 'index'
59 end
61 end
60 end
62 end
@@ -1,87 +1,89
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class EnumerationsController < ApplicationController
18 class EnumerationsController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 helper :custom_fields
23 helper :custom_fields
22 include CustomFieldsHelper
24 include CustomFieldsHelper
23
25
24 def index
26 def index
25 list
27 list
26 render :action => 'list'
28 render :action => 'list'
27 end
29 end
28
30
29 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
31 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
30 verify :method => :post, :only => [ :destroy, :create, :update ],
32 verify :method => :post, :only => [ :destroy, :create, :update ],
31 :redirect_to => { :action => :list }
33 :redirect_to => { :action => :list }
32
34
33 def list
35 def list
34 end
36 end
35
37
36 def new
38 def new
37 begin
39 begin
38 @enumeration = params[:type].constantize.new
40 @enumeration = params[:type].constantize.new
39 rescue NameError
41 rescue NameError
40 @enumeration = Enumeration.new
42 @enumeration = Enumeration.new
41 end
43 end
42 end
44 end
43
45
44 def create
46 def create
45 @enumeration = Enumeration.new(params[:enumeration])
47 @enumeration = Enumeration.new(params[:enumeration])
46 @enumeration.type = params[:enumeration][:type]
48 @enumeration.type = params[:enumeration][:type]
47 if @enumeration.save
49 if @enumeration.save
48 flash[:notice] = l(:notice_successful_create)
50 flash[:notice] = l(:notice_successful_create)
49 redirect_to :action => 'list', :type => @enumeration.type
51 redirect_to :action => 'list', :type => @enumeration.type
50 else
52 else
51 render :action => 'new'
53 render :action => 'new'
52 end
54 end
53 end
55 end
54
56
55 def edit
57 def edit
56 @enumeration = Enumeration.find(params[:id])
58 @enumeration = Enumeration.find(params[:id])
57 end
59 end
58
60
59 def update
61 def update
60 @enumeration = Enumeration.find(params[:id])
62 @enumeration = Enumeration.find(params[:id])
61 @enumeration.type = params[:enumeration][:type] if params[:enumeration][:type]
63 @enumeration.type = params[:enumeration][:type] if params[:enumeration][:type]
62 if @enumeration.update_attributes(params[:enumeration])
64 if @enumeration.update_attributes(params[:enumeration])
63 flash[:notice] = l(:notice_successful_update)
65 flash[:notice] = l(:notice_successful_update)
64 redirect_to :action => 'list', :type => @enumeration.type
66 redirect_to :action => 'list', :type => @enumeration.type
65 else
67 else
66 render :action => 'edit'
68 render :action => 'edit'
67 end
69 end
68 end
70 end
69
71
70 def destroy
72 def destroy
71 @enumeration = Enumeration.find(params[:id])
73 @enumeration = Enumeration.find(params[:id])
72 if !@enumeration.in_use?
74 if !@enumeration.in_use?
73 # No associated objects
75 # No associated objects
74 @enumeration.destroy
76 @enumeration.destroy
75 redirect_to :action => 'index'
77 redirect_to :action => 'index'
76 elsif params[:reassign_to_id]
78 elsif params[:reassign_to_id]
77 if reassign_to = Enumeration.find_by_type_and_id(@enumeration.type, params[:reassign_to_id])
79 if reassign_to = Enumeration.find_by_type_and_id(@enumeration.type, params[:reassign_to_id])
78 @enumeration.destroy(reassign_to)
80 @enumeration.destroy(reassign_to)
79 redirect_to :action => 'index'
81 redirect_to :action => 'index'
80 end
82 end
81 end
83 end
82 @enumerations = Enumeration.find(:all, :conditions => ['type = (?)', @enumeration.type]) - [@enumeration]
84 @enumerations = Enumeration.find(:all, :conditions => ['type = (?)', @enumeration.type]) - [@enumeration]
83 #rescue
85 #rescue
84 # flash[:error] = 'Unable to delete enumeration'
86 # flash[:error] = 'Unable to delete enumeration'
85 # redirect_to :action => 'index'
87 # redirect_to :action => 'index'
86 end
88 end
87 end
89 end
@@ -1,162 +1,163
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class GroupsController < ApplicationController
18 class GroupsController < ApplicationController
19 layout 'base'
19 layout 'admin'
20
20 before_filter :require_admin
21 before_filter :require_admin
21
22
22 helper :custom_fields
23 helper :custom_fields
23
24
24 # GET /groups
25 # GET /groups
25 # GET /groups.xml
26 # GET /groups.xml
26 def index
27 def index
27 @groups = Group.find(:all, :order => 'lastname')
28 @groups = Group.find(:all, :order => 'lastname')
28
29
29 respond_to do |format|
30 respond_to do |format|
30 format.html # index.html.erb
31 format.html # index.html.erb
31 format.xml { render :xml => @groups }
32 format.xml { render :xml => @groups }
32 end
33 end
33 end
34 end
34
35
35 # GET /groups/1
36 # GET /groups/1
36 # GET /groups/1.xml
37 # GET /groups/1.xml
37 def show
38 def show
38 @group = Group.find(params[:id])
39 @group = Group.find(params[:id])
39
40
40 respond_to do |format|
41 respond_to do |format|
41 format.html # show.html.erb
42 format.html # show.html.erb
42 format.xml { render :xml => @group }
43 format.xml { render :xml => @group }
43 end
44 end
44 end
45 end
45
46
46 # GET /groups/new
47 # GET /groups/new
47 # GET /groups/new.xml
48 # GET /groups/new.xml
48 def new
49 def new
49 @group = Group.new
50 @group = Group.new
50
51
51 respond_to do |format|
52 respond_to do |format|
52 format.html # new.html.erb
53 format.html # new.html.erb
53 format.xml { render :xml => @group }
54 format.xml { render :xml => @group }
54 end
55 end
55 end
56 end
56
57
57 # GET /groups/1/edit
58 # GET /groups/1/edit
58 def edit
59 def edit
59 @group = Group.find(params[:id])
60 @group = Group.find(params[:id])
60 end
61 end
61
62
62 # POST /groups
63 # POST /groups
63 # POST /groups.xml
64 # POST /groups.xml
64 def create
65 def create
65 @group = Group.new(params[:group])
66 @group = Group.new(params[:group])
66
67
67 respond_to do |format|
68 respond_to do |format|
68 if @group.save
69 if @group.save
69 flash[:notice] = l(:notice_successful_create)
70 flash[:notice] = l(:notice_successful_create)
70 format.html { redirect_to(groups_path) }
71 format.html { redirect_to(groups_path) }
71 format.xml { render :xml => @group, :status => :created, :location => @group }
72 format.xml { render :xml => @group, :status => :created, :location => @group }
72 else
73 else
73 format.html { render :action => "new" }
74 format.html { render :action => "new" }
74 format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
75 format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
75 end
76 end
76 end
77 end
77 end
78 end
78
79
79 # PUT /groups/1
80 # PUT /groups/1
80 # PUT /groups/1.xml
81 # PUT /groups/1.xml
81 def update
82 def update
82 @group = Group.find(params[:id])
83 @group = Group.find(params[:id])
83
84
84 respond_to do |format|
85 respond_to do |format|
85 if @group.update_attributes(params[:group])
86 if @group.update_attributes(params[:group])
86 flash[:notice] = l(:notice_successful_update)
87 flash[:notice] = l(:notice_successful_update)
87 format.html { redirect_to(groups_path) }
88 format.html { redirect_to(groups_path) }
88 format.xml { head :ok }
89 format.xml { head :ok }
89 else
90 else
90 format.html { render :action => "edit" }
91 format.html { render :action => "edit" }
91 format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
92 format.xml { render :xml => @group.errors, :status => :unprocessable_entity }
92 end
93 end
93 end
94 end
94 end
95 end
95
96
96 # DELETE /groups/1
97 # DELETE /groups/1
97 # DELETE /groups/1.xml
98 # DELETE /groups/1.xml
98 def destroy
99 def destroy
99 @group = Group.find(params[:id])
100 @group = Group.find(params[:id])
100 @group.destroy
101 @group.destroy
101
102
102 respond_to do |format|
103 respond_to do |format|
103 format.html { redirect_to(groups_url) }
104 format.html { redirect_to(groups_url) }
104 format.xml { head :ok }
105 format.xml { head :ok }
105 end
106 end
106 end
107 end
107
108
108 def add_users
109 def add_users
109 @group = Group.find(params[:id])
110 @group = Group.find(params[:id])
110 users = User.find_all_by_id(params[:user_ids])
111 users = User.find_all_by_id(params[:user_ids])
111 @group.users << users if request.post?
112 @group.users << users if request.post?
112 respond_to do |format|
113 respond_to do |format|
113 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
114 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
114 format.js {
115 format.js {
115 render(:update) {|page|
116 render(:update) {|page|
116 page.replace_html "tab-content-users", :partial => 'groups/users'
117 page.replace_html "tab-content-users", :partial => 'groups/users'
117 users.each {|user| page.visual_effect(:highlight, "user-#{user.id}") }
118 users.each {|user| page.visual_effect(:highlight, "user-#{user.id}") }
118 }
119 }
119 }
120 }
120 end
121 end
121 end
122 end
122
123
123 def remove_user
124 def remove_user
124 @group = Group.find(params[:id])
125 @group = Group.find(params[:id])
125 @group.users.delete(User.find(params[:user_id])) if request.post?
126 @group.users.delete(User.find(params[:user_id])) if request.post?
126 respond_to do |format|
127 respond_to do |format|
127 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
128 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'users' }
128 format.js { render(:update) {|page| page.replace_html "tab-content-users", :partial => 'groups/users'} }
129 format.js { render(:update) {|page| page.replace_html "tab-content-users", :partial => 'groups/users'} }
129 end
130 end
130 end
131 end
131
132
132 def autocomplete_for_user
133 def autocomplete_for_user
133 @group = Group.find(params[:id])
134 @group = Group.find(params[:id])
134 @users = User.active.like(params[:q]).find(:all, :limit => 100) - @group.users
135 @users = User.active.like(params[:q]).find(:all, :limit => 100) - @group.users
135 render :layout => false
136 render :layout => false
136 end
137 end
137
138
138 def edit_membership
139 def edit_membership
139 @group = Group.find(params[:id])
140 @group = Group.find(params[:id])
140 @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @group)
141 @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @group)
141 @membership.attributes = params[:membership]
142 @membership.attributes = params[:membership]
142 @membership.save if request.post?
143 @membership.save if request.post?
143 respond_to do |format|
144 respond_to do |format|
144 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
145 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
145 format.js {
146 format.js {
146 render(:update) {|page|
147 render(:update) {|page|
147 page.replace_html "tab-content-memberships", :partial => 'groups/memberships'
148 page.replace_html "tab-content-memberships", :partial => 'groups/memberships'
148 page.visual_effect(:highlight, "member-#{@membership.id}")
149 page.visual_effect(:highlight, "member-#{@membership.id}")
149 }
150 }
150 }
151 }
151 end
152 end
152 end
153 end
153
154
154 def destroy_membership
155 def destroy_membership
155 @group = Group.find(params[:id])
156 @group = Group.find(params[:id])
156 Member.find(params[:membership_id]).destroy if request.post?
157 Member.find(params[:membership_id]).destroy if request.post?
157 respond_to do |format|
158 respond_to do |format|
158 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
159 format.html { redirect_to :controller => 'groups', :action => 'edit', :id => @group, :tab => 'memberships' }
159 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'groups/memberships'} }
160 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'groups/memberships'} }
160 end
161 end
161 end
162 end
162 end
163 end
@@ -1,78 +1,80
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssueStatusesController < ApplicationController
18 class IssueStatusesController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ],
23 verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ],
22 :redirect_to => { :action => :list }
24 :redirect_to => { :action => :list }
23
25
24 def index
26 def index
25 list
27 list
26 render :action => 'list' unless request.xhr?
28 render :action => 'list' unless request.xhr?
27 end
29 end
28
30
29 def list
31 def list
30 @issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
32 @issue_status_pages, @issue_statuses = paginate :issue_statuses, :per_page => 25, :order => "position"
31 render :action => "list", :layout => false if request.xhr?
33 render :action => "list", :layout => false if request.xhr?
32 end
34 end
33
35
34 def new
36 def new
35 @issue_status = IssueStatus.new
37 @issue_status = IssueStatus.new
36 end
38 end
37
39
38 def create
40 def create
39 @issue_status = IssueStatus.new(params[:issue_status])
41 @issue_status = IssueStatus.new(params[:issue_status])
40 if @issue_status.save
42 if @issue_status.save
41 flash[:notice] = l(:notice_successful_create)
43 flash[:notice] = l(:notice_successful_create)
42 redirect_to :action => 'list'
44 redirect_to :action => 'list'
43 else
45 else
44 render :action => 'new'
46 render :action => 'new'
45 end
47 end
46 end
48 end
47
49
48 def edit
50 def edit
49 @issue_status = IssueStatus.find(params[:id])
51 @issue_status = IssueStatus.find(params[:id])
50 end
52 end
51
53
52 def update
54 def update
53 @issue_status = IssueStatus.find(params[:id])
55 @issue_status = IssueStatus.find(params[:id])
54 if @issue_status.update_attributes(params[:issue_status])
56 if @issue_status.update_attributes(params[:issue_status])
55 flash[:notice] = l(:notice_successful_update)
57 flash[:notice] = l(:notice_successful_update)
56 redirect_to :action => 'list'
58 redirect_to :action => 'list'
57 else
59 else
58 render :action => 'edit'
60 render :action => 'edit'
59 end
61 end
60 end
62 end
61
63
62 def destroy
64 def destroy
63 IssueStatus.find(params[:id]).destroy
65 IssueStatus.find(params[:id]).destroy
64 redirect_to :action => 'list'
66 redirect_to :action => 'list'
65 rescue
67 rescue
66 flash[:error] = "Unable to delete issue status"
68 flash[:error] = "Unable to delete issue status"
67 redirect_to :action => 'list'
69 redirect_to :action => 'list'
68 end
70 end
69
71
70 def update_issue_done_ratio
72 def update_issue_done_ratio
71 if IssueStatus.update_issue_done_ratios
73 if IssueStatus.update_issue_done_ratios
72 flash[:notice] = l(:notice_issue_done_ratios_updated)
74 flash[:notice] = l(:notice_issue_done_ratios_updated)
73 else
75 else
74 flash[:error] = l(:error_issue_done_ratios_not_updated)
76 flash[:error] = l(:error_issue_done_ratios_not_updated)
75 end
77 end
76 redirect_to :action => 'list'
78 redirect_to :action => 'list'
77 end
79 end
78 end
80 end
@@ -1,79 +1,81
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class RolesController < ApplicationController
18 class RolesController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 verify :method => :post, :only => [ :destroy, :move ],
23 verify :method => :post, :only => [ :destroy, :move ],
22 :redirect_to => { :action => :list }
24 :redirect_to => { :action => :list }
23
25
24 def index
26 def index
25 list
27 list
26 render :action => 'list' unless request.xhr?
28 render :action => 'list' unless request.xhr?
27 end
29 end
28
30
29 def list
31 def list
30 @role_pages, @roles = paginate :roles, :per_page => 25, :order => 'builtin, position'
32 @role_pages, @roles = paginate :roles, :per_page => 25, :order => 'builtin, position'
31 render :action => "list", :layout => false if request.xhr?
33 render :action => "list", :layout => false if request.xhr?
32 end
34 end
33
35
34 def new
36 def new
35 # Prefills the form with 'Non member' role permissions
37 # Prefills the form with 'Non member' role permissions
36 @role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
38 @role = Role.new(params[:role] || {:permissions => Role.non_member.permissions})
37 if request.post? && @role.save
39 if request.post? && @role.save
38 # workflow copy
40 # workflow copy
39 if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
41 if !params[:copy_workflow_from].blank? && (copy_from = Role.find_by_id(params[:copy_workflow_from]))
40 @role.workflows.copy(copy_from)
42 @role.workflows.copy(copy_from)
41 end
43 end
42 flash[:notice] = l(:notice_successful_create)
44 flash[:notice] = l(:notice_successful_create)
43 redirect_to :action => 'index'
45 redirect_to :action => 'index'
44 end
46 end
45 @permissions = @role.setable_permissions
47 @permissions = @role.setable_permissions
46 @roles = Role.find :all, :order => 'builtin, position'
48 @roles = Role.find :all, :order => 'builtin, position'
47 end
49 end
48
50
49 def edit
51 def edit
50 @role = Role.find(params[:id])
52 @role = Role.find(params[:id])
51 if request.post? and @role.update_attributes(params[:role])
53 if request.post? and @role.update_attributes(params[:role])
52 flash[:notice] = l(:notice_successful_update)
54 flash[:notice] = l(:notice_successful_update)
53 redirect_to :action => 'index'
55 redirect_to :action => 'index'
54 end
56 end
55 @permissions = @role.setable_permissions
57 @permissions = @role.setable_permissions
56 end
58 end
57
59
58 def destroy
60 def destroy
59 @role = Role.find(params[:id])
61 @role = Role.find(params[:id])
60 @role.destroy
62 @role.destroy
61 redirect_to :action => 'index'
63 redirect_to :action => 'index'
62 rescue
64 rescue
63 flash[:error] = 'This role is in use and can not be deleted.'
65 flash[:error] = 'This role is in use and can not be deleted.'
64 redirect_to :action => 'index'
66 redirect_to :action => 'index'
65 end
67 end
66
68
67 def report
69 def report
68 @roles = Role.find(:all, :order => 'builtin, position')
70 @roles = Role.find(:all, :order => 'builtin, position')
69 @permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
71 @permissions = Redmine::AccessControl.permissions.select { |p| !p.public? }
70 if request.post?
72 if request.post?
71 @roles.each do |role|
73 @roles.each do |role|
72 role.permissions = params[:permissions][role.id.to_s]
74 role.permissions = params[:permissions][role.id.to_s]
73 role.save
75 role.save
74 end
76 end
75 flash[:notice] = l(:notice_successful_update)
77 flash[:notice] = l(:notice_successful_update)
76 redirect_to :action => 'index'
78 redirect_to :action => 'index'
77 end
79 end
78 end
80 end
79 end
81 end
@@ -1,59 +1,61
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class SettingsController < ApplicationController
18 class SettingsController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 def index
23 def index
22 edit
24 edit
23 render :action => 'edit'
25 render :action => 'edit'
24 end
26 end
25
27
26 def edit
28 def edit
27 @notifiables = %w(issue_added issue_updated news_added document_added file_added message_posted wiki_content_added wiki_content_updated)
29 @notifiables = %w(issue_added issue_updated news_added document_added file_added message_posted wiki_content_added wiki_content_updated)
28 if request.post? && params[:settings] && params[:settings].is_a?(Hash)
30 if request.post? && params[:settings] && params[:settings].is_a?(Hash)
29 settings = (params[:settings] || {}).dup.symbolize_keys
31 settings = (params[:settings] || {}).dup.symbolize_keys
30 settings.each do |name, value|
32 settings.each do |name, value|
31 # remove blank values in array settings
33 # remove blank values in array settings
32 value.delete_if {|v| v.blank? } if value.is_a?(Array)
34 value.delete_if {|v| v.blank? } if value.is_a?(Array)
33 Setting[name] = value
35 Setting[name] = value
34 end
36 end
35 flash[:notice] = l(:notice_successful_update)
37 flash[:notice] = l(:notice_successful_update)
36 redirect_to :action => 'edit', :tab => params[:tab]
38 redirect_to :action => 'edit', :tab => params[:tab]
37 return
39 return
38 end
40 end
39 @options = {}
41 @options = {}
40 @options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
42 @options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
41 @deliveries = ActionMailer::Base.perform_deliveries
43 @deliveries = ActionMailer::Base.perform_deliveries
42
44
43 @guessed_host_and_path = request.host_with_port.dup
45 @guessed_host_and_path = request.host_with_port.dup
44 @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
46 @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
45 end
47 end
46
48
47 def plugin
49 def plugin
48 @plugin = Redmine::Plugin.find(params[:id])
50 @plugin = Redmine::Plugin.find(params[:id])
49 if request.post?
51 if request.post?
50 Setting["plugin_#{@plugin.id}"] = params[:settings]
52 Setting["plugin_#{@plugin.id}"] = params[:settings]
51 flash[:notice] = l(:notice_successful_update)
53 flash[:notice] = l(:notice_successful_update)
52 redirect_to :action => 'plugin', :id => @plugin.id
54 redirect_to :action => 'plugin', :id => @plugin.id
53 end
55 end
54 @partial = @plugin.settings[:partial]
56 @partial = @plugin.settings[:partial]
55 @settings = Setting["plugin_#{@plugin.id}"]
57 @settings = Setting["plugin_#{@plugin.id}"]
56 rescue Redmine::PluginNotFound
58 rescue Redmine::PluginNotFound
57 render_404
59 render_404
58 end
60 end
59 end
61 end
@@ -1,67 +1,69
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class TrackersController < ApplicationController
18 class TrackersController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 def index
23 def index
22 list
24 list
23 render :action => 'list' unless request.xhr?
25 render :action => 'list' unless request.xhr?
24 end
26 end
25
27
26 verify :method => :post, :only => :destroy, :redirect_to => { :action => :list }
28 verify :method => :post, :only => :destroy, :redirect_to => { :action => :list }
27
29
28 def list
30 def list
29 @tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
31 @tracker_pages, @trackers = paginate :trackers, :per_page => 10, :order => 'position'
30 render :action => "list", :layout => false if request.xhr?
32 render :action => "list", :layout => false if request.xhr?
31 end
33 end
32
34
33 def new
35 def new
34 @tracker = Tracker.new(params[:tracker])
36 @tracker = Tracker.new(params[:tracker])
35 if request.post? and @tracker.save
37 if request.post? and @tracker.save
36 # workflow copy
38 # workflow copy
37 if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
39 if !params[:copy_workflow_from].blank? && (copy_from = Tracker.find_by_id(params[:copy_workflow_from]))
38 @tracker.workflows.copy(copy_from)
40 @tracker.workflows.copy(copy_from)
39 end
41 end
40 flash[:notice] = l(:notice_successful_create)
42 flash[:notice] = l(:notice_successful_create)
41 redirect_to :action => 'list'
43 redirect_to :action => 'list'
42 return
44 return
43 end
45 end
44 @trackers = Tracker.find :all, :order => 'position'
46 @trackers = Tracker.find :all, :order => 'position'
45 @projects = Project.find(:all)
47 @projects = Project.find(:all)
46 end
48 end
47
49
48 def edit
50 def edit
49 @tracker = Tracker.find(params[:id])
51 @tracker = Tracker.find(params[:id])
50 if request.post? and @tracker.update_attributes(params[:tracker])
52 if request.post? and @tracker.update_attributes(params[:tracker])
51 flash[:notice] = l(:notice_successful_update)
53 flash[:notice] = l(:notice_successful_update)
52 redirect_to :action => 'list'
54 redirect_to :action => 'list'
53 return
55 return
54 end
56 end
55 @projects = Project.find(:all)
57 @projects = Project.find(:all)
56 end
58 end
57
59
58 def destroy
60 def destroy
59 @tracker = Tracker.find(params[:id])
61 @tracker = Tracker.find(params[:id])
60 unless @tracker.issues.empty?
62 unless @tracker.issues.empty?
61 flash[:error] = "This tracker contains issues and can\'t be deleted."
63 flash[:error] = "This tracker contains issues and can\'t be deleted."
62 else
64 else
63 @tracker.destroy
65 @tracker.destroy
64 end
66 end
65 redirect_to :action => 'list'
67 redirect_to :action => 'list'
66 end
68 end
67 end
69 end
@@ -1,140 +1,143
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class UsersController < ApplicationController
18 class UsersController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin, :except => :show
21 before_filter :require_admin, :except => :show
20
22
21 helper :sort
23 helper :sort
22 include SortHelper
24 include SortHelper
23 helper :custom_fields
25 helper :custom_fields
24 include CustomFieldsHelper
26 include CustomFieldsHelper
25
27
26 def index
28 def index
27 sort_init 'login', 'asc'
29 sort_init 'login', 'asc'
28 sort_update %w(login firstname lastname mail admin created_on last_login_on)
30 sort_update %w(login firstname lastname mail admin created_on last_login_on)
29
31
30 @status = params[:status] ? params[:status].to_i : 1
32 @status = params[:status] ? params[:status].to_i : 1
31 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
33 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
32
34
33 unless params[:name].blank?
35 unless params[:name].blank?
34 name = "%#{params[:name].strip.downcase}%"
36 name = "%#{params[:name].strip.downcase}%"
35 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
37 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
36 end
38 end
37
39
38 @user_count = User.count(:conditions => c.conditions)
40 @user_count = User.count(:conditions => c.conditions)
39 @user_pages = Paginator.new self, @user_count,
41 @user_pages = Paginator.new self, @user_count,
40 per_page_option,
42 per_page_option,
41 params['page']
43 params['page']
42 @users = User.find :all,:order => sort_clause,
44 @users = User.find :all,:order => sort_clause,
43 :conditions => c.conditions,
45 :conditions => c.conditions,
44 :limit => @user_pages.items_per_page,
46 :limit => @user_pages.items_per_page,
45 :offset => @user_pages.current.offset
47 :offset => @user_pages.current.offset
46
48
47 render :layout => !request.xhr?
49 render :layout => !request.xhr?
48 end
50 end
49
51
50 def show
52 def show
51 @user = User.active.find(params[:id])
53 @user = User.active.find(params[:id])
52 @custom_values = @user.custom_values
54 @custom_values = @user.custom_values
53
55
54 # show only public projects and private projects that the logged in user is also a member of
56 # show only public projects and private projects that the logged in user is also a member of
55 @memberships = @user.memberships.select do |membership|
57 @memberships = @user.memberships.select do |membership|
56 membership.project.is_public? || (User.current.member_of?(membership.project))
58 membership.project.is_public? || (User.current.member_of?(membership.project))
57 end
59 end
58
60
59 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
61 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
60 @events_by_day = events.group_by(&:event_date)
62 @events_by_day = events.group_by(&:event_date)
61
63
62 if @user != User.current && !User.current.admin? && @memberships.empty? && events.empty?
64 if @user != User.current && !User.current.admin? && @memberships.empty? && events.empty?
63 render_404 and return
65 render_404 and return
64 end
66 end
65
67 render :layout => 'base'
68
66 rescue ActiveRecord::RecordNotFound
69 rescue ActiveRecord::RecordNotFound
67 render_404
70 render_404
68 end
71 end
69
72
70 def add
73 def add
71 if request.get?
74 if request.get?
72 @user = User.new(:language => Setting.default_language)
75 @user = User.new(:language => Setting.default_language)
73 else
76 else
74 @user = User.new(params[:user])
77 @user = User.new(params[:user])
75 @user.admin = params[:user][:admin] || false
78 @user.admin = params[:user][:admin] || false
76 @user.login = params[:user][:login]
79 @user.login = params[:user][:login]
77 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
80 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
78 if @user.save
81 if @user.save
79 Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
82 Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
80 flash[:notice] = l(:notice_successful_create)
83 flash[:notice] = l(:notice_successful_create)
81 redirect_to :controller => 'users', :action => 'edit', :id => @user
84 redirect_to :controller => 'users', :action => 'edit', :id => @user
82 end
85 end
83 end
86 end
84 @auth_sources = AuthSource.find(:all)
87 @auth_sources = AuthSource.find(:all)
85 end
88 end
86
89
87 def edit
90 def edit
88 @user = User.find(params[:id])
91 @user = User.find(params[:id])
89 if request.post?
92 if request.post?
90 @user.admin = params[:user][:admin] if params[:user][:admin]
93 @user.admin = params[:user][:admin] if params[:user][:admin]
91 @user.login = params[:user][:login] if params[:user][:login]
94 @user.login = params[:user][:login] if params[:user][:login]
92 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
95 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
93 @user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
96 @user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
94 @user.attributes = params[:user]
97 @user.attributes = params[:user]
95 # Was the account actived ? (do it before User#save clears the change)
98 # Was the account actived ? (do it before User#save clears the change)
96 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
99 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
97 if @user.save
100 if @user.save
98 if was_activated
101 if was_activated
99 Mailer.deliver_account_activated(@user)
102 Mailer.deliver_account_activated(@user)
100 elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
103 elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
101 Mailer.deliver_account_information(@user, params[:password])
104 Mailer.deliver_account_information(@user, params[:password])
102 end
105 end
103 flash[:notice] = l(:notice_successful_update)
106 flash[:notice] = l(:notice_successful_update)
104 redirect_to :back
107 redirect_to :back
105 end
108 end
106 end
109 end
107 @auth_sources = AuthSource.find(:all)
110 @auth_sources = AuthSource.find(:all)
108 @membership ||= Member.new
111 @membership ||= Member.new
109 rescue ::ActionController::RedirectBackError
112 rescue ::ActionController::RedirectBackError
110 redirect_to :controller => 'users', :action => 'edit', :id => @user
113 redirect_to :controller => 'users', :action => 'edit', :id => @user
111 end
114 end
112
115
113 def edit_membership
116 def edit_membership
114 @user = User.find(params[:id])
117 @user = User.find(params[:id])
115 @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @user)
118 @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @user)
116 @membership.attributes = params[:membership]
119 @membership.attributes = params[:membership]
117 @membership.save if request.post?
120 @membership.save if request.post?
118 respond_to do |format|
121 respond_to do |format|
119 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
122 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
120 format.js {
123 format.js {
121 render(:update) {|page|
124 render(:update) {|page|
122 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
125 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
123 page.visual_effect(:highlight, "member-#{@membership.id}")
126 page.visual_effect(:highlight, "member-#{@membership.id}")
124 }
127 }
125 }
128 }
126 end
129 end
127 end
130 end
128
131
129 def destroy_membership
132 def destroy_membership
130 @user = User.find(params[:id])
133 @user = User.find(params[:id])
131 @membership = Member.find(params[:membership_id])
134 @membership = Member.find(params[:membership_id])
132 if request.post? && @membership.deletable?
135 if request.post? && @membership.deletable?
133 @membership.destroy
136 @membership.destroy
134 end
137 end
135 respond_to do |format|
138 respond_to do |format|
136 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
139 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
137 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
140 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
138 end
141 end
139 end
142 end
140 end
143 end
@@ -1,68 +1,70
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class WorkflowsController < ApplicationController
18 class WorkflowsController < ApplicationController
19 layout 'admin'
20
19 before_filter :require_admin
21 before_filter :require_admin
20
22
21 def index
23 def index
22 @workflow_counts = Workflow.count_by_tracker_and_role
24 @workflow_counts = Workflow.count_by_tracker_and_role
23 end
25 end
24
26
25 def edit
27 def edit
26 @role = Role.find_by_id(params[:role_id])
28 @role = Role.find_by_id(params[:role_id])
27 @tracker = Tracker.find_by_id(params[:tracker_id])
29 @tracker = Tracker.find_by_id(params[:tracker_id])
28
30
29 if request.post?
31 if request.post?
30 Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
32 Workflow.destroy_all( ["role_id=? and tracker_id=?", @role.id, @tracker.id])
31 (params[:issue_status] || []).each { |old, news|
33 (params[:issue_status] || []).each { |old, news|
32 news.each { |new|
34 news.each { |new|
33 @role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
35 @role.workflows.build(:tracker_id => @tracker.id, :old_status_id => old, :new_status_id => new)
34 }
36 }
35 }
37 }
36 if @role.save
38 if @role.save
37 flash[:notice] = l(:notice_successful_update)
39 flash[:notice] = l(:notice_successful_update)
38 redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
40 redirect_to :action => 'edit', :role_id => @role, :tracker_id => @tracker
39 end
41 end
40 end
42 end
41 @roles = Role.find(:all, :order => 'builtin, position')
43 @roles = Role.find(:all, :order => 'builtin, position')
42 @trackers = Tracker.find(:all, :order => 'position')
44 @trackers = Tracker.find(:all, :order => 'position')
43 @statuses = IssueStatus.find(:all, :order => 'position')
45 @statuses = IssueStatus.find(:all, :order => 'position')
44 end
46 end
45
47
46 def copy
48 def copy
47 @trackers = Tracker.find(:all, :order => 'position')
49 @trackers = Tracker.find(:all, :order => 'position')
48 @roles = Role.find(:all, :order => 'builtin, position')
50 @roles = Role.find(:all, :order => 'builtin, position')
49
51
50 @source_tracker = params[:source_tracker_id].blank? ? nil : Tracker.find_by_id(params[:source_tracker_id])
52 @source_tracker = params[:source_tracker_id].blank? ? nil : Tracker.find_by_id(params[:source_tracker_id])
51 @source_role = params[:source_role_id].blank? ? nil : Role.find_by_id(params[:source_role_id])
53 @source_role = params[:source_role_id].blank? ? nil : Role.find_by_id(params[:source_role_id])
52
54
53 @target_trackers = params[:target_tracker_ids].blank? ? nil : Tracker.find_all_by_id(params[:target_tracker_ids])
55 @target_trackers = params[:target_tracker_ids].blank? ? nil : Tracker.find_all_by_id(params[:target_tracker_ids])
54 @target_roles = params[:target_role_ids].blank? ? nil : Role.find_all_by_id(params[:target_role_ids])
56 @target_roles = params[:target_role_ids].blank? ? nil : Role.find_all_by_id(params[:target_role_ids])
55
57
56 if request.post?
58 if request.post?
57 if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
59 if params[:source_tracker_id].blank? || params[:source_role_id].blank? || (@source_tracker.nil? && @source_role.nil?)
58 flash.now[:error] = l(:error_workflow_copy_source)
60 flash.now[:error] = l(:error_workflow_copy_source)
59 elsif @target_trackers.nil? || @target_roles.nil?
61 elsif @target_trackers.nil? || @target_roles.nil?
60 flash.now[:error] = l(:error_workflow_copy_target)
62 flash.now[:error] = l(:error_workflow_copy_target)
61 else
63 else
62 Workflow.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
64 Workflow.copy(@source_tracker, @source_role, @target_trackers, @target_roles)
63 flash[:notice] = l(:notice_successful_update)
65 flash[:notice] = l(:notice_successful_update)
64 redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
66 redirect_to :action => 'copy', :source_tracker_id => @source_tracker, :source_role_id => @source_role
65 end
67 end
66 end
68 end
67 end
69 end
68 end
70 end
@@ -1,25 +1,19
1 <div id="menuAdmin" class="menu" onmouseover="menuMouseover(event)">
1 <div id="admin-menu">
2 <a class="menuItem" href="#" onmouseover="menuItemMouseover(event,'menuProjects');" onclick="this.blur(); return false;"><span class="menuItemText"><%=l(:label_project_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
2 <ul>
3 <a class="menuItem" href="#" onmouseover="menuItemMouseover(event,'menuUsers');" onclick="this.blur(); return false;"><span class="menuItemText"><%=l(:label_user_plural)%></span><span class="menuItemArrow">&#9654;</span></a>
3 <li><%= link_to l(:label_project_plural), {:controller => 'admin', :action => 'projects'}, :class => 'projects' %></li>
4 <%= link_to l(:label_role_and_permissions), {:controller => 'roles' }, :class => "menuItem" %>
4 <li><%= link_to l(:label_user_plural), {:controller => 'users'}, :class => 'users' %></li>
5 <a class="menuItem" href="#" onmouseover="menuItemMouseover(event,'menuTrackers');" onclick="this.blur(); return false;"><span class="menuItemText"><%=l(:label_issue_tracking)%></span><span class="menuItemArrow">&#9654;</span></a>
5 <li><%= link_to l(:label_group_plural), {:controller => 'groups'}, :class => 'groups' %></li>
6 <%= link_to l(:label_custom_field_plural), {:controller => 'custom_fields' }, :class => "menuItem" %>
6 <li><%= link_to l(:label_role_and_permissions), {:controller => 'roles'}, :class => 'roles' %></li>
7 <%= link_to l(:label_enumerations), {:controller => 'enumerations' }, :class => "menuItem" %>
7 <li><%= link_to l(:label_tracker_plural), {:controller => 'trackers'}, :class => 'trackers' %></li>
8 <%= link_to l(:field_mail_notification), {:controller => 'admin', :action => 'mail_options' }, :class => "menuItem" %>
8 <li><%= link_to l(:label_issue_status_plural), {:controller => 'issue_statuses'}, :class => 'issue_statuses' %></li>
9 <%= link_to l(:label_authentication), {:controller => 'auth_sources' }, :class => "menuItem" %>
9 <li><%= link_to l(:label_workflow), {:controller => 'workflows', :action => 'edit'}, :class => 'workflows' %></li>
10 <%= link_to l(:label_settings), {:controller => 'settings' }, :class => "menuItem" %>
10 <li><%= link_to l(:label_custom_field_plural), {:controller => 'custom_fields'}, :class => 'custom_fields' %></li>
11 <%= link_to l(:label_information_plural), {:controller => 'admin', :action => 'info' }, :class => "menuItem" %>
11 <li><%= link_to l(:label_enumerations), {:controller => 'enumerations'}, :class => 'enumerations' %></li>
12 </div>
12 <li><%= link_to l(:label_settings), {:controller => 'settings'}, :class => 'settings' %></li>
13 <div id="menuTrackers" class="menu">
13 <% menu_items_for(:admin_menu) do |item| -%>
14 <%= link_to l(:label_tracker_plural), {:controller => 'trackers' }, :class => "menuItem" %>
14 <li><%= link_to h(item.caption), item.url, item.html_options %></li>
15 <%= link_to l(:label_issue_status_plural), {:controller => 'issue_statuses' }, :class => "menuItem" %>
15 <% end -%>
16 <%= link_to l(:label_workflow), {:controller => 'roles', :action => 'workflow' }, :class => "menuItem" %>
16 <li><%= link_to l(:label_plugins), {:controller => 'admin', :action => 'plugins'}, :class => 'plugins' %></li>
17 </div>
17 <li><%= link_to l(:label_information_plural), {:controller => 'admin', :action => 'info'}, :class => 'info' %></li>
18 <div id="menuProjects" class="menu">
18 </ul>
19 <%= link_to l(:button_list), {:controller => 'admin', :action => 'projects' }, :class => "menuItem" %>
20 <%= link_to l(:label_new), {:controller => 'projects', :action => 'add' }, :class => "menuItem" %>
21 </div>
22 <div id="menuUsers" class="menu">
23 <%= link_to l(:button_list), {:controller => 'users' }, :class => "menuItem" %>
24 <%= link_to l(:label_new), {:controller => 'users', :action => 'add' }, :class => "menuItem" %>
25 </div>
19 </div>
@@ -1,56 +1,8
1 <h2><%=l(:label_administration)%></h2>
1 <h2><%=l(:label_administration)%></h2>
2
2
3 <%= render :partial => 'no_data' if @no_configuration_data %>
3 <div id="admin-index">
4
4 <%= render :partial => 'no_data' if @no_configuration_data %>
5 <p class="icon22 icon22-projects">
5 <%= render :partial => 'menu' %>
6 <%= link_to l(:label_project_plural), :controller => 'admin', :action => 'projects' %> |
6 </div>
7 <%= link_to l(:label_new), :controller => 'projects', :action => 'add' %>
8 </p>
9
10 <p class="icon22 icon22-users">
11 <%= link_to l(:label_user_plural), :controller => 'users' %> |
12 <%= link_to l(:label_new), :controller => 'users', :action => 'add' %>
13 </p>
14
15 <p class="icon22 icon22-groups">
16 <%= link_to l(:label_group_plural), :controller => 'groups' %> |
17 <%= link_to l(:label_new), :controller => 'groups', :action => 'new' %>
18 </p>
19
20 <p class="icon22 icon22-role">
21 <%= link_to l(:label_role_and_permissions), :controller => 'roles' %>
22 </p>
23
24 <p class="icon22 icon22-tracker">
25 <%= link_to l(:label_tracker_plural), :controller => 'trackers' %> |
26 <%= link_to l(:label_issue_status_plural), :controller => 'issue_statuses' %> |
27 <%= link_to l(:label_workflow), :controller => 'workflows', :action => 'edit' %>
28 </p>
29
30 <p class="icon22 icon22-workflow">
31 <%= link_to l(:label_custom_field_plural), :controller => 'custom_fields' %>
32 </p>
33
34 <p class="icon22 icon22-options">
35 <%= link_to l(:label_enumerations), :controller => 'enumerations' %>
36 </p>
37
38 <p class="icon22 icon22-settings">
39 <%= link_to l(:label_settings), :controller => 'settings' %>
40 </p>
41
42 <% menu_items_for(:admin_menu) do |item| -%>
43 <%= content_tag 'p',
44 link_to(h(item.caption), item.url, item.html_options),
45 :class => ["icon22", "icon22-#{item.name}"].join(' ') %>
46 <% end -%>
47
48 <p class="icon22 icon22-plugin">
49 <%= link_to l(:label_plugins), :controller => 'admin', :action => 'plugins' %>
50 </p>
51
52 <p class="icon22 icon22-info">
53 <%= link_to l(:label_information_plural), :controller => 'admin', :action => 'info' %>
54 </p>
55
7
56 <% html_title(l(:label_administration)) -%>
8 <% html_title(l(:label_administration)) -%>
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,844 +1,861
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: 13px;padding: 2px 10px 1px 0px;margin-bottom: 5px; border-bottom: 1px dotted #bbbbbb; color: #444;}
7 h4, .wiki h3 {font-size: 13px;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.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
12 #top-menu {background: #2C4056; color: #fff; height:1.8em; font-size: 0.8em; padding: 2px 2px 0px 6px;}
13 #top-menu ul {margin: 0; padding: 0;}
13 #top-menu ul {margin: 0; padding: 0;}
14 #top-menu li {
14 #top-menu li {
15 float:left;
15 float:left;
16 list-style-type:none;
16 list-style-type:none;
17 margin: 0px 0px 0px 0px;
17 margin: 0px 0px 0px 0px;
18 padding: 0px 0px 0px 0px;
18 padding: 0px 0px 0px 0px;
19 white-space:nowrap;
19 white-space:nowrap;
20 }
20 }
21 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
21 #top-menu a {color: #fff; margin-right: 8px; font-weight: bold;}
22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
22 #top-menu #loggedas { float: right; margin-right: 0.5em; color: #fff; }
23
23
24 #account {float:right;}
24 #account {float:right;}
25
25
26 #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;}
27 #header a {color:#f8f8f8;}
27 #header a {color:#f8f8f8;}
28 #header h1 a.ancestor { font-size: 80%; }
28 #header h1 a.ancestor { font-size: 80%; }
29 #quick-search {float:right;}
29 #quick-search {float:right;}
30
30
31 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
31 #main-menu {position: absolute; bottom: 0px; left:6px; margin-right: -500px;}
32 #main-menu ul {margin: 0; padding: 0;}
32 #main-menu ul {margin: 0; padding: 0;}
33 #main-menu li {
33 #main-menu li {
34 float:left;
34 float:left;
35 list-style-type:none;
35 list-style-type:none;
36 margin: 0px 2px 0px 0px;
36 margin: 0px 2px 0px 0px;
37 padding: 0px 0px 0px 0px;
37 padding: 0px 0px 0px 0px;
38 white-space:nowrap;
38 white-space:nowrap;
39 }
39 }
40 #main-menu li a {
40 #main-menu li a {
41 display: block;
41 display: block;
42 color: #fff;
42 color: #fff;
43 text-decoration: none;
43 text-decoration: none;
44 font-weight: bold;
44 font-weight: bold;
45 margin: 0;
45 margin: 0;
46 padding: 4px 10px 4px 10px;
46 padding: 4px 10px 4px 10px;
47 }
47 }
48 #main-menu li a:hover {background:#759FCF; color:#fff;}
48 #main-menu li a:hover {background:#759FCF; color:#fff;}
49 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
49 #main-menu li a.selected, #main-menu li a.selected:hover {background:#fff; color:#555;}
50
50
51 #admin-menu ul {margin: 0; padding: 0;}
52 #admin-menu li {margin: 0; padding: 0 0 12px 0; list-style-type:none;}
53
54 #admin-menu a { background-position: 0% 40%; background-repeat: no-repeat; padding-left: 20px; padding-top: 2px; padding-bottom: 3px;}
55 #admin-menu a.projects { background-image: url(../images/projects.png); }
56 #admin-menu a.users { background-image: url(../images/user.png); }
57 #admin-menu a.groups { background-image: url(../images/group.png); }
58 #admin-menu a.roles { background-image: url(../images/database_key.png); }
59 #admin-menu a.trackers { background-image: url(../images/ticket.png); }
60 #admin-menu a.issue_statuses { background-image: url(../images/ticket_edit.png); }
61 #admin-menu a.workflows { background-image: url(../images/ticket_go.png); }
62 #admin-menu a.custom_fields { background-image: url(../images/textfield.png); }
63 #admin-menu a.enumerations { background-image: url(../images/text_list_bullets.png); }
64 #admin-menu a.settings { background-image: url(../images/changeset.png); }
65 #admin-menu a.plugins { background-image: url(../images/plugin.png); }
66 #admin-menu a.info { background-image: url(../images/help.png); }
67
51 #main {background-color:#EEEEEE;}
68 #main {background-color:#EEEEEE;}
52
69
53 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
70 #sidebar{ float: right; width: 17%; position: relative; z-index: 9; min-height: 600px; padding: 0; margin: 0;}
54 * html #sidebar{ width: 17%; }
71 * html #sidebar{ width: 17%; }
55 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
72 #sidebar h3{ font-size: 14px; margin-top:14px; color: #666; }
56 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
73 #sidebar hr{ width: 100%; margin: 0 auto; height: 1px; background: #ccc; border: 0; }
57 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
74 * html #sidebar hr{ width: 95%; position: relative; left: -6px; color: #ccc; }
58
75
59 #content { width: 80%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
76 #content { width: 80%; background-color: #fff; margin: 0px; border-right: 1px solid #ddd; padding: 6px 10px 10px 10px; z-index: 10; }
60 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
77 * html #content{ width: 80%; padding-left: 0; margin-top: 0px; padding: 6px 10px 10px 10px;}
61 html>body #content { min-height: 600px; }
78 html>body #content { min-height: 600px; }
62 * html body #content { height: 600px; } /* IE */
79 * html body #content { height: 600px; } /* IE */
63
80
64 #main.nosidebar #sidebar{ display: none; }
81 #main.nosidebar #sidebar{ display: none; }
65 #main.nosidebar #content{ width: auto; border-right: 0; }
82 #main.nosidebar #content{ width: auto; border-right: 0; }
66
83
67 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
84 #footer {clear: both; border-top: 1px solid #bbb; font-size: 0.9em; color: #aaa; padding: 5px; text-align:center; background:#fff;}
68
85
69 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
86 #login-form table {margin-top:5em; padding:1em; margin-left: auto; margin-right: auto; border: 2px solid #FDBF3B; background-color:#FFEBC1; }
70 #login-form table td {padding: 6px;}
87 #login-form table td {padding: 6px;}
71 #login-form label {font-weight: bold;}
88 #login-form label {font-weight: bold;}
72 #login-form input#username, #login-form input#password { width: 300px; }
89 #login-form input#username, #login-form input#password { width: 300px; }
73
90
74 input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; }
91 input#openid_url { background: url(../images/openid-bg.gif) no-repeat; background-color: #fff; background-position: 0 50%; padding-left: 18px; }
75
92
76 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
93 .clear:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }
77
94
78 /***** Links *****/
95 /***** Links *****/
79 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
96 a, a:link, a:visited{ color: #2A5685; text-decoration: none; }
80 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
97 a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
81 a img{ border: 0; }
98 a img{ border: 0; }
82
99
83 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
100 a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; }
84
101
85 /***** Tables *****/
102 /***** Tables *****/
86 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
103 table.list { border: 1px solid #e4e4e4; border-collapse: collapse; width: 100%; margin-bottom: 4px; }
87 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
104 table.list th { background-color:#EEEEEE; padding: 4px; white-space:nowrap; }
88 table.list td { vertical-align: top; }
105 table.list td { vertical-align: top; }
89 table.list td.id { width: 2%; text-align: center;}
106 table.list td.id { width: 2%; text-align: center;}
90 table.list td.checkbox { width: 15px; padding: 0px;}
107 table.list td.checkbox { width: 15px; padding: 0px;}
91 table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; }
108 table.list td.buttons { width: 15%; white-space:nowrap; text-align: right; }
92 table.list td.buttons a { padding-right: 0.6em; }
109 table.list td.buttons a { padding-right: 0.6em; }
93
110
94 tr.project td.name a { padding-left: 16px; white-space:nowrap; }
111 tr.project td.name a { padding-left: 16px; white-space:nowrap; }
95 tr.project.parent td.name a { background: url('../images/bullet_toggle_minus.png') no-repeat; }
112 tr.project.parent td.name a { background: url('../images/bullet_toggle_minus.png') no-repeat; }
96
113
97 tr.issue { text-align: center; white-space: nowrap; }
114 tr.issue { text-align: center; white-space: nowrap; }
98 tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
115 tr.issue td.subject, tr.issue td.category, td.assigned_to { white-space: normal; }
99 tr.issue td.subject { text-align: left; }
116 tr.issue td.subject { text-align: left; }
100 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
117 tr.issue td.done_ratio table.progress { margin-left:auto; margin-right: auto;}
101
118
102 tr.entry { border: 1px solid #f8f8f8; }
119 tr.entry { border: 1px solid #f8f8f8; }
103 tr.entry td { white-space: nowrap; }
120 tr.entry td { white-space: nowrap; }
104 tr.entry td.filename { width: 30%; }
121 tr.entry td.filename { width: 30%; }
105 tr.entry td.size { text-align: right; font-size: 90%; }
122 tr.entry td.size { text-align: right; font-size: 90%; }
106 tr.entry td.revision, tr.entry td.author { text-align: center; }
123 tr.entry td.revision, tr.entry td.author { text-align: center; }
107 tr.entry td.age { text-align: right; }
124 tr.entry td.age { text-align: right; }
108 tr.entry.file td.filename a { margin-left: 16px; }
125 tr.entry.file td.filename a { margin-left: 16px; }
109
126
110 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
127 tr span.expander {background-image: url(../images/bullet_toggle_plus.png); padding-left: 8px; margin-left: 0; cursor: pointer;}
111 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
128 tr.open span.expander {background-image: url(../images/bullet_toggle_minus.png);}
112
129
113 tr.changeset td.author { text-align: center; width: 15%; }
130 tr.changeset td.author { text-align: center; width: 15%; }
114 tr.changeset td.committed_on { text-align: center; width: 15%; }
131 tr.changeset td.committed_on { text-align: center; width: 15%; }
115
132
116 table.files tr.file td { text-align: center; }
133 table.files tr.file td { text-align: center; }
117 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
134 table.files tr.file td.filename { text-align: left; padding-left: 24px; }
118 table.files tr.file td.digest { font-size: 80%; }
135 table.files tr.file td.digest { font-size: 80%; }
119
136
120 table.members td.roles, table.memberships td.roles { width: 45%; }
137 table.members td.roles, table.memberships td.roles { width: 45%; }
121
138
122 tr.message { height: 2.6em; }
139 tr.message { height: 2.6em; }
123 tr.message td.last_message { font-size: 80%; }
140 tr.message td.last_message { font-size: 80%; }
124 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
141 tr.message.locked td.subject a { background-image: url(../images/locked.png); }
125 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
142 tr.message.sticky td.subject a { background-image: url(../images/sticky.png); font-weight: bold; }
126
143
127 tr.version.closed, tr.version.closed a { color: #999; }
144 tr.version.closed, tr.version.closed a { color: #999; }
128 tr.version td.name { padding-left: 20px; }
145 tr.version td.name { padding-left: 20px; }
129 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
146 tr.version.shared td.name { background: url(../images/link.png) no-repeat 0% 70%; }
130 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; }
147 tr.version td.date, tr.version td.status, tr.version td.sharing { text-align: center; }
131
148
132 tr.user td { width:13%; }
149 tr.user td { width:13%; }
133 tr.user td.email { width:18%; }
150 tr.user td.email { width:18%; }
134 tr.user td { white-space: nowrap; }
151 tr.user td { white-space: nowrap; }
135 tr.user.locked, tr.user.registered { color: #aaa; }
152 tr.user.locked, tr.user.registered { color: #aaa; }
136 tr.user.locked a, tr.user.registered a { color: #aaa; }
153 tr.user.locked a, tr.user.registered a { color: #aaa; }
137
154
138 tr.time-entry { text-align: center; white-space: nowrap; }
155 tr.time-entry { text-align: center; white-space: nowrap; }
139 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
156 tr.time-entry td.subject, tr.time-entry td.comments { text-align: left; white-space: normal; }
140 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
157 td.hours { text-align: right; font-weight: bold; padding-right: 0.5em; }
141 td.hours .hours-dec { font-size: 0.9em; }
158 td.hours .hours-dec { font-size: 0.9em; }
142
159
143 table.plugins td { vertical-align: middle; }
160 table.plugins td { vertical-align: middle; }
144 table.plugins td.configure { text-align: right; padding-right: 1em; }
161 table.plugins td.configure { text-align: right; padding-right: 1em; }
145 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
162 table.plugins span.name { font-weight: bold; display: block; margin-bottom: 6px; }
146 table.plugins span.description { display: block; font-size: 0.9em; }
163 table.plugins span.description { display: block; font-size: 0.9em; }
147 table.plugins span.url { display: block; font-size: 0.9em; }
164 table.plugins span.url { display: block; font-size: 0.9em; }
148
165
149 table.list tbody tr.group td { padding: 0.8em 0 0.5em 0.3em; font-weight: bold; border-bottom: 1px solid #ccc; }
166 table.list tbody tr.group td { padding: 0.8em 0 0.5em 0.3em; font-weight: bold; border-bottom: 1px solid #ccc; }
150 table.list tbody tr.group span.count { color: #aaa; font-size: 80%; }
167 table.list tbody tr.group span.count { color: #aaa; font-size: 80%; }
151
168
152 table.list tbody tr:hover { background-color:#ffffdd; }
169 table.list tbody tr:hover { background-color:#ffffdd; }
153 table.list tbody tr.group:hover { background-color:inherit; }
170 table.list tbody tr.group:hover { background-color:inherit; }
154 table td {padding:2px;}
171 table td {padding:2px;}
155 table p {margin:0;}
172 table p {margin:0;}
156 .odd {background-color:#f6f7f8;}
173 .odd {background-color:#f6f7f8;}
157 .even {background-color: #fff;}
174 .even {background-color: #fff;}
158
175
159 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
176 a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; }
160 a.sort.asc { background-image: url(../images/sort_asc.png); }
177 a.sort.asc { background-image: url(../images/sort_asc.png); }
161 a.sort.desc { background-image: url(../images/sort_desc.png); }
178 a.sort.desc { background-image: url(../images/sort_desc.png); }
162
179
163 table.attributes { width: 100% }
180 table.attributes { width: 100% }
164 table.attributes th { vertical-align: top; text-align: left; }
181 table.attributes th { vertical-align: top; text-align: left; }
165 table.attributes td { vertical-align: top; }
182 table.attributes td { vertical-align: top; }
166
183
167 td.center {text-align:center;}
184 td.center {text-align:center;}
168
185
169 .highlight { background-color: #FCFD8D;}
186 .highlight { background-color: #FCFD8D;}
170 .highlight.token-1 { background-color: #faa;}
187 .highlight.token-1 { background-color: #faa;}
171 .highlight.token-2 { background-color: #afa;}
188 .highlight.token-2 { background-color: #afa;}
172 .highlight.token-3 { background-color: #aaf;}
189 .highlight.token-3 { background-color: #aaf;}
173
190
174 .box{
191 .box{
175 padding:6px;
192 padding:6px;
176 margin-bottom: 10px;
193 margin-bottom: 10px;
177 background-color:#f6f6f6;
194 background-color:#f6f6f6;
178 color:#505050;
195 color:#505050;
179 line-height:1.5em;
196 line-height:1.5em;
180 border: 1px solid #e4e4e4;
197 border: 1px solid #e4e4e4;
181 }
198 }
182
199
183 div.square {
200 div.square {
184 border: 1px solid #999;
201 border: 1px solid #999;
185 float: left;
202 float: left;
186 margin: .3em .4em 0 .4em;
203 margin: .3em .4em 0 .4em;
187 overflow: hidden;
204 overflow: hidden;
188 width: .6em; height: .6em;
205 width: .6em; height: .6em;
189 }
206 }
190 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
207 .contextual {float:right; white-space: nowrap; line-height:1.4em;margin-top:5px; padding-left: 10px; font-size:0.9em;}
191 .contextual input, .contextual select {font-size:0.9em;}
208 .contextual input, .contextual select {font-size:0.9em;}
192 .message .contextual { margin-top: 0; }
209 .message .contextual { margin-top: 0; }
193
210
194 .splitcontentleft{float:left; width:49%;}
211 .splitcontentleft{float:left; width:49%;}
195 .splitcontentright{float:right; width:49%;}
212 .splitcontentright{float:right; width:49%;}
196 form {display: inline;}
213 form {display: inline;}
197 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
214 input, select {vertical-align: middle; margin-top: 1px; margin-bottom: 1px;}
198 fieldset {border: 1px solid #e4e4e4; margin:0;}
215 fieldset {border: 1px solid #e4e4e4; margin:0;}
199 legend {color: #484848;}
216 legend {color: #484848;}
200 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
217 hr { width: 100%; height: 1px; background: #ccc; border: 0;}
201 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
218 blockquote { font-style: italic; border-left: 3px solid #e0e0e0; padding-left: 0.6em; margin-left: 2.4em;}
202 blockquote blockquote { margin-left: 0;}
219 blockquote blockquote { margin-left: 0;}
203 acronym { border-bottom: 1px dotted; cursor: help; }
220 acronym { border-bottom: 1px dotted; cursor: help; }
204 textarea.wiki-edit { width: 99%; }
221 textarea.wiki-edit { width: 99%; }
205 li p {margin-top: 0;}
222 li p {margin-top: 0;}
206 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
223 div.issue {background:#ffffdd; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
207 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
224 p.breadcrumb { font-size: 0.9em; margin: 4px 0 4px 0;}
208 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
225 p.subtitle { font-size: 0.9em; margin: -6px 0 12px 0; font-style: italic; }
209 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
226 p.footnote { font-size: 0.9em; margin-top: 0px; margin-bottom: 0px; }
210
227
211 fieldset.collapsible { border-width: 1px 0 0 0; font-size: 0.9em; }
228 fieldset.collapsible { border-width: 1px 0 0 0; font-size: 0.9em; }
212 fieldset.collapsible legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
229 fieldset.collapsible legend { padding-left: 16px; background: url(../images/arrow_expanded.png) no-repeat 0% 40%; cursor:pointer; }
213 fieldset.collapsible.collapsed legend { background-image: url(../images/arrow_collapsed.png); }
230 fieldset.collapsible.collapsed legend { background-image: url(../images/arrow_collapsed.png); }
214
231
215 fieldset#date-range p { margin: 2px 0 2px 0; }
232 fieldset#date-range p { margin: 2px 0 2px 0; }
216 fieldset#filters table { border-collapse: collapse; }
233 fieldset#filters table { border-collapse: collapse; }
217 fieldset#filters table td { padding: 0; vertical-align: middle; }
234 fieldset#filters table td { padding: 0; vertical-align: middle; }
218 fieldset#filters tr.filter { height: 2em; }
235 fieldset#filters tr.filter { height: 2em; }
219 fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
236 fieldset#filters td.add-filter { text-align: right; vertical-align: top; }
220 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
237 .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; }
221
238
222 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
239 div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;}
223 div#issue-changesets .changeset { padding: 4px;}
240 div#issue-changesets .changeset { padding: 4px;}
224 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
241 div#issue-changesets .changeset { border-bottom: 1px solid #ddd; }
225 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
242 div#issue-changesets p { margin-top: 0; margin-bottom: 1em;}
226
243
227 div#activity dl, #search-results { margin-left: 2em; }
244 div#activity dl, #search-results { margin-left: 2em; }
228 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
245 div#activity dd, #search-results dd { margin-bottom: 1em; padding-left: 18px; font-size: 0.9em; }
229 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
246 div#activity dt, #search-results dt { margin-bottom: 0px; padding-left: 20px; line-height: 18px; background-position: 0 50%; background-repeat: no-repeat; }
230 div#activity dt.me .time { border-bottom: 1px solid #999; }
247 div#activity dt.me .time { border-bottom: 1px solid #999; }
231 div#activity dt .time { color: #777; font-size: 80%; }
248 div#activity dt .time { color: #777; font-size: 80%; }
232 div#activity dd .description, #search-results dd .description { font-style: italic; }
249 div#activity dd .description, #search-results dd .description { font-style: italic; }
233 div#activity span.project:after, #search-results span.project:after { content: " -"; }
250 div#activity span.project:after, #search-results span.project:after { content: " -"; }
234 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
251 div#activity dd span.description, #search-results dd span.description { display:block; color: #808080; }
235
252
236 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
253 #search-results dd { margin-bottom: 1em; padding-left: 20px; margin-left:0px; }
237
254
238 div#search-results-counts {float:right;}
255 div#search-results-counts {float:right;}
239 div#search-results-counts ul { margin-top: 0.5em; }
256 div#search-results-counts ul { margin-top: 0.5em; }
240 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
257 div#search-results-counts li { list-style-type:none; float: left; margin-left: 1em; }
241
258
242 dt.issue { background-image: url(../images/ticket.png); }
259 dt.issue { background-image: url(../images/ticket.png); }
243 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
260 dt.issue-edit { background-image: url(../images/ticket_edit.png); }
244 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
261 dt.issue-closed { background-image: url(../images/ticket_checked.png); }
245 dt.issue-note { background-image: url(../images/ticket_note.png); }
262 dt.issue-note { background-image: url(../images/ticket_note.png); }
246 dt.changeset { background-image: url(../images/changeset.png); }
263 dt.changeset { background-image: url(../images/changeset.png); }
247 dt.news { background-image: url(../images/news.png); }
264 dt.news { background-image: url(../images/news.png); }
248 dt.message { background-image: url(../images/message.png); }
265 dt.message { background-image: url(../images/message.png); }
249 dt.reply { background-image: url(../images/comments.png); }
266 dt.reply { background-image: url(../images/comments.png); }
250 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
267 dt.wiki-page { background-image: url(../images/wiki_edit.png); }
251 dt.attachment { background-image: url(../images/attachment.png); }
268 dt.attachment { background-image: url(../images/attachment.png); }
252 dt.document { background-image: url(../images/document.png); }
269 dt.document { background-image: url(../images/document.png); }
253 dt.project { background-image: url(../images/projects.png); }
270 dt.project { background-image: url(../images/projects.png); }
254 dt.time-entry { background-image: url(../images/time.png); }
271 dt.time-entry { background-image: url(../images/time.png); }
255
272
256 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
273 #search-results dt.issue.closed { background-image: url(../images/ticket_checked.png); }
257
274
258 div#roadmap fieldset.related-issues { margin-bottom: 1em; }
275 div#roadmap fieldset.related-issues { margin-bottom: 1em; }
259 div#roadmap fieldset.related-issues ul { margin-top: 0.3em; margin-bottom: 0.3em; }
276 div#roadmap fieldset.related-issues ul { margin-top: 0.3em; margin-bottom: 0.3em; }
260 div#roadmap .wiki h1:first-child { display: none; }
277 div#roadmap .wiki h1:first-child { display: none; }
261 div#roadmap .wiki h1 { font-size: 120%; }
278 div#roadmap .wiki h1 { font-size: 120%; }
262 div#roadmap .wiki h2 { font-size: 110%; }
279 div#roadmap .wiki h2 { font-size: 110%; }
263
280
264 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
281 div#version-summary { float:right; width:380px; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
265 div#version-summary fieldset { margin-bottom: 1em; }
282 div#version-summary fieldset { margin-bottom: 1em; }
266 div#version-summary .total-hours { text-align: right; }
283 div#version-summary .total-hours { text-align: right; }
267
284
268 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
285 table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
269 table#time-report tbody tr { font-style: italic; color: #777; }
286 table#time-report tbody tr { font-style: italic; color: #777; }
270 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
287 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
271 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
288 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
272 table#time-report .hours-dec { font-size: 0.9em; }
289 table#time-report .hours-dec { font-size: 0.9em; }
273
290
274 form#issue-form .attributes { margin-bottom: 8px; }
291 form#issue-form .attributes { margin-bottom: 8px; }
275 form#issue-form .attributes p { padding-top: 1px; padding-bottom: 2px; }
292 form#issue-form .attributes p { padding-top: 1px; padding-bottom: 2px; }
276 form#issue-form .attributes select { min-width: 30%; }
293 form#issue-form .attributes select { min-width: 30%; }
277
294
278 ul.projects { margin: 0; padding-left: 1em; }
295 ul.projects { margin: 0; padding-left: 1em; }
279 ul.projects.root { margin: 0; padding: 0; }
296 ul.projects.root { margin: 0; padding: 0; }
280 ul.projects ul { border-left: 3px solid #e0e0e0; }
297 ul.projects ul { border-left: 3px solid #e0e0e0; }
281 ul.projects li { list-style-type:none; }
298 ul.projects li { list-style-type:none; }
282 ul.projects li.root { margin-bottom: 1em; }
299 ul.projects li.root { margin-bottom: 1em; }
283 ul.projects li.child { margin-top: 1em;}
300 ul.projects li.child { margin-top: 1em;}
284 ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
301 ul.projects div.root a.project { font-family: "Trebuchet MS", Verdana, sans-serif; font-weight: bold; font-size: 16px; margin: 0 0 10px 0; }
285 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
302 .my-project { padding-left: 18px; background: url(../images/fav.png) no-repeat 0 50%; }
286
303
287 #tracker_project_ids ul { margin: 0; padding-left: 1em; }
304 #tracker_project_ids ul { margin: 0; padding-left: 1em; }
288 #tracker_project_ids li { list-style-type:none; }
305 #tracker_project_ids li { list-style-type:none; }
289
306
290 ul.properties {padding:0; font-size: 0.9em; color: #777;}
307 ul.properties {padding:0; font-size: 0.9em; color: #777;}
291 ul.properties li {list-style-type:none;}
308 ul.properties li {list-style-type:none;}
292 ul.properties li span {font-style:italic;}
309 ul.properties li span {font-style:italic;}
293
310
294 .total-hours { font-size: 110%; font-weight: bold; }
311 .total-hours { font-size: 110%; font-weight: bold; }
295 .total-hours span.hours-int { font-size: 120%; }
312 .total-hours span.hours-int { font-size: 120%; }
296
313
297 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
314 .autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em;}
298 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
315 #user_firstname, #user_lastname, #user_mail, #my_account_form select { width: 90%; }
299
316
300 #workflow_copy_form select { width: 200px; }
317 #workflow_copy_form select { width: 200px; }
301
318
302 .pagination {font-size: 90%}
319 .pagination {font-size: 90%}
303 p.pagination {margin-top:8px;}
320 p.pagination {margin-top:8px;}
304
321
305 /***** Tabular forms ******/
322 /***** Tabular forms ******/
306 .tabular p{
323 .tabular p{
307 margin: 0;
324 margin: 0;
308 padding: 5px 0 8px 0;
325 padding: 5px 0 8px 0;
309 padding-left: 180px; /*width of left column containing the label elements*/
326 padding-left: 180px; /*width of left column containing the label elements*/
310 height: 1%;
327 height: 1%;
311 clear:left;
328 clear:left;
312 }
329 }
313
330
314 html>body .tabular p {overflow:hidden;}
331 html>body .tabular p {overflow:hidden;}
315
332
316 .tabular label{
333 .tabular label{
317 font-weight: bold;
334 font-weight: bold;
318 float: left;
335 float: left;
319 text-align: right;
336 text-align: right;
320 margin-left: -180px; /*width of left column*/
337 margin-left: -180px; /*width of left column*/
321 width: 175px; /*width of labels. Should be smaller than left column to create some right
338 width: 175px; /*width of labels. Should be smaller than left column to create some right
322 margin*/
339 margin*/
323 }
340 }
324
341
325 .tabular label.floating{
342 .tabular label.floating{
326 font-weight: normal;
343 font-weight: normal;
327 margin-left: 0px;
344 margin-left: 0px;
328 text-align: left;
345 text-align: left;
329 width: 270px;
346 width: 270px;
330 }
347 }
331
348
332 .tabular label.block{
349 .tabular label.block{
333 font-weight: normal;
350 font-weight: normal;
334 margin-left: 0px !important;
351 margin-left: 0px !important;
335 text-align: left;
352 text-align: left;
336 float: none;
353 float: none;
337 display: block;
354 display: block;
338 width: auto;
355 width: auto;
339 }
356 }
340
357
341 input#time_entry_comments { width: 90%;}
358 input#time_entry_comments { width: 90%;}
342
359
343 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
360 #preview fieldset {margin-top: 1em; background: url(../images/draft.png)}
344
361
345 .tabular.settings p{ padding-left: 300px; }
362 .tabular.settings p{ padding-left: 300px; }
346 .tabular.settings label{ margin-left: -300px; width: 295px; }
363 .tabular.settings label{ margin-left: -300px; width: 295px; }
347
364
348 .required {color: #bb0000;}
365 .required {color: #bb0000;}
349 .summary {font-style: italic;}
366 .summary {font-style: italic;}
350
367
351 #attachments_fields input[type=text] {margin-left: 8px; }
368 #attachments_fields input[type=text] {margin-left: 8px; }
352
369
353 div.attachments { margin-top: 12px; }
370 div.attachments { margin-top: 12px; }
354 div.attachments p { margin:4px 0 2px 0; }
371 div.attachments p { margin:4px 0 2px 0; }
355 div.attachments img { vertical-align: middle; }
372 div.attachments img { vertical-align: middle; }
356 div.attachments span.author { font-size: 0.9em; color: #888; }
373 div.attachments span.author { font-size: 0.9em; color: #888; }
357
374
358 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
375 p.other-formats { text-align: right; font-size:0.9em; color: #666; }
359 .other-formats span + span:before { content: "| "; }
376 .other-formats span + span:before { content: "| "; }
360
377
361 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
378 a.atom { background: url(../images/feed.png) no-repeat 1px 50%; padding: 2px 0px 3px 16px; }
362
379
363 /* Project members tab */
380 /* Project members tab */
364 div#tab-content-members .splitcontentleft, div#tab-content-memberships .splitcontentleft, div#tab-content-users .splitcontentleft { width: 64% }
381 div#tab-content-members .splitcontentleft, div#tab-content-memberships .splitcontentleft, div#tab-content-users .splitcontentleft { width: 64% }
365 div#tab-content-members .splitcontentright, div#tab-content-memberships .splitcontentright, div#tab-content-users .splitcontentright { width: 34% }
382 div#tab-content-members .splitcontentright, div#tab-content-memberships .splitcontentright, div#tab-content-users .splitcontentright { width: 34% }
366 div#tab-content-members fieldset, div#tab-content-memberships fieldset, div#tab-content-users fieldset { padding:1em; margin-bottom: 1em; }
383 div#tab-content-members fieldset, div#tab-content-memberships fieldset, div#tab-content-users fieldset { padding:1em; margin-bottom: 1em; }
367 div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend, div#tab-content-users fieldset legend { font-weight: bold; }
384 div#tab-content-members fieldset legend, div#tab-content-memberships fieldset legend, div#tab-content-users fieldset legend { font-weight: bold; }
368 div#tab-content-members fieldset label, div#tab-content-memberships fieldset label, div#tab-content-users fieldset label { display: block; }
385 div#tab-content-members fieldset label, div#tab-content-memberships fieldset label, div#tab-content-users fieldset label { display: block; }
369 div#tab-content-members fieldset div, div#tab-content-users fieldset div { max-height: 400px; overflow:auto; }
386 div#tab-content-members fieldset div, div#tab-content-users fieldset div { max-height: 400px; overflow:auto; }
370
387
371 table.members td.group { padding-left: 20px; background: url(../images/users.png) no-repeat 0% 0%; }
388 table.members td.group { padding-left: 20px; background: url(../images/users.png) no-repeat 0% 0%; }
372
389
373 * html div#tab-content-members fieldset div { height: 450px; }
390 * html div#tab-content-members fieldset div { height: 450px; }
374
391
375 /***** Flash & error messages ****/
392 /***** Flash & error messages ****/
376 #errorExplanation, div.flash, .nodata, .warning {
393 #errorExplanation, div.flash, .nodata, .warning {
377 padding: 4px 4px 4px 30px;
394 padding: 4px 4px 4px 30px;
378 margin-bottom: 12px;
395 margin-bottom: 12px;
379 font-size: 1.1em;
396 font-size: 1.1em;
380 border: 2px solid;
397 border: 2px solid;
381 }
398 }
382
399
383 div.flash {margin-top: 8px;}
400 div.flash {margin-top: 8px;}
384
401
385 div.flash.error, #errorExplanation {
402 div.flash.error, #errorExplanation {
386 background: url(../images/false.png) 8px 5px no-repeat;
403 background: url(../images/false.png) 8px 5px no-repeat;
387 background-color: #ffe3e3;
404 background-color: #ffe3e3;
388 border-color: #dd0000;
405 border-color: #dd0000;
389 color: #550000;
406 color: #550000;
390 }
407 }
391
408
392 div.flash.notice {
409 div.flash.notice {
393 background: url(../images/true.png) 8px 5px no-repeat;
410 background: url(../images/true.png) 8px 5px no-repeat;
394 background-color: #dfffdf;
411 background-color: #dfffdf;
395 border-color: #9fcf9f;
412 border-color: #9fcf9f;
396 color: #005f00;
413 color: #005f00;
397 }
414 }
398
415
399 div.flash.warning {
416 div.flash.warning {
400 background: url(../images/warning.png) 8px 5px no-repeat;
417 background: url(../images/warning.png) 8px 5px no-repeat;
401 background-color: #FFEBC1;
418 background-color: #FFEBC1;
402 border-color: #FDBF3B;
419 border-color: #FDBF3B;
403 color: #A6750C;
420 color: #A6750C;
404 text-align: left;
421 text-align: left;
405 }
422 }
406
423
407 .nodata, .warning {
424 .nodata, .warning {
408 text-align: center;
425 text-align: center;
409 background-color: #FFEBC1;
426 background-color: #FFEBC1;
410 border-color: #FDBF3B;
427 border-color: #FDBF3B;
411 color: #A6750C;
428 color: #A6750C;
412 }
429 }
413
430
414 #errorExplanation ul { font-size: 0.9em;}
431 #errorExplanation ul { font-size: 0.9em;}
415 #errorExplanation h2, #errorExplanation p { display: none; }
432 #errorExplanation h2, #errorExplanation p { display: none; }
416
433
417 /***** Ajax indicator ******/
434 /***** Ajax indicator ******/
418 #ajax-indicator {
435 #ajax-indicator {
419 position: absolute; /* fixed not supported by IE */
436 position: absolute; /* fixed not supported by IE */
420 background-color:#eee;
437 background-color:#eee;
421 border: 1px solid #bbb;
438 border: 1px solid #bbb;
422 top:35%;
439 top:35%;
423 left:40%;
440 left:40%;
424 width:20%;
441 width:20%;
425 font-weight:bold;
442 font-weight:bold;
426 text-align:center;
443 text-align:center;
427 padding:0.6em;
444 padding:0.6em;
428 z-index:100;
445 z-index:100;
429 filter:alpha(opacity=50);
446 filter:alpha(opacity=50);
430 opacity: 0.5;
447 opacity: 0.5;
431 }
448 }
432
449
433 html>body #ajax-indicator { position: fixed; }
450 html>body #ajax-indicator { position: fixed; }
434
451
435 #ajax-indicator span {
452 #ajax-indicator span {
436 background-position: 0% 40%;
453 background-position: 0% 40%;
437 background-repeat: no-repeat;
454 background-repeat: no-repeat;
438 background-image: url(../images/loading.gif);
455 background-image: url(../images/loading.gif);
439 padding-left: 26px;
456 padding-left: 26px;
440 vertical-align: bottom;
457 vertical-align: bottom;
441 }
458 }
442
459
443 /***** Calendar *****/
460 /***** Calendar *****/
444 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
461 table.cal {border-collapse: collapse; width: 100%; margin: 0px 0 6px 0;border: 1px solid #d7d7d7;}
445 table.cal thead th {width: 14%;}
462 table.cal thead th {width: 14%;}
446 table.cal tbody tr {height: 100px;}
463 table.cal tbody tr {height: 100px;}
447 table.cal th { background-color:#EEEEEE; padding: 4px; }
464 table.cal th { background-color:#EEEEEE; padding: 4px; }
448 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
465 table.cal td {border: 1px solid #d7d7d7; vertical-align: top; font-size: 0.9em;}
449 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
466 table.cal td p.day-num {font-size: 1.1em; text-align:right;}
450 table.cal td.odd p.day-num {color: #bbb;}
467 table.cal td.odd p.day-num {color: #bbb;}
451 table.cal td.today {background:#ffffdd;}
468 table.cal td.today {background:#ffffdd;}
452 table.cal td.today p.day-num {font-weight: bold;}
469 table.cal td.today p.day-num {font-weight: bold;}
453
470
454 /***** Tooltips ******/
471 /***** Tooltips ******/
455 .tooltip{position:relative;z-index:24;}
472 .tooltip{position:relative;z-index:24;}
456 .tooltip:hover{z-index:25;color:#000;}
473 .tooltip:hover{z-index:25;color:#000;}
457 .tooltip span.tip{display: none; text-align:left;}
474 .tooltip span.tip{display: none; text-align:left;}
458
475
459 div.tooltip:hover span.tip{
476 div.tooltip:hover span.tip{
460 display:block;
477 display:block;
461 position:absolute;
478 position:absolute;
462 top:12px; left:24px; width:270px;
479 top:12px; left:24px; width:270px;
463 border:1px solid #555;
480 border:1px solid #555;
464 background-color:#fff;
481 background-color:#fff;
465 padding: 4px;
482 padding: 4px;
466 font-size: 0.8em;
483 font-size: 0.8em;
467 color:#505050;
484 color:#505050;
468 }
485 }
469
486
470 /***** Progress bar *****/
487 /***** Progress bar *****/
471 table.progress {
488 table.progress {
472 border: 1px solid #D7D7D7;
489 border: 1px solid #D7D7D7;
473 border-collapse: collapse;
490 border-collapse: collapse;
474 border-spacing: 0pt;
491 border-spacing: 0pt;
475 empty-cells: show;
492 empty-cells: show;
476 text-align: center;
493 text-align: center;
477 float:left;
494 float:left;
478 margin: 1px 6px 1px 0px;
495 margin: 1px 6px 1px 0px;
479 }
496 }
480
497
481 table.progress td { height: 0.9em; }
498 table.progress td { height: 0.9em; }
482 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
499 table.progress td.closed { background: #BAE0BA none repeat scroll 0%; }
483 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
500 table.progress td.done { background: #DEF0DE none repeat scroll 0%; }
484 table.progress td.open { background: #FFF none repeat scroll 0%; }
501 table.progress td.open { background: #FFF none repeat scroll 0%; }
485 p.pourcent {font-size: 80%;}
502 p.pourcent {font-size: 80%;}
486 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
503 p.progress-info {clear: left; font-style: italic; font-size: 80%;}
487
504
488 /***** Tabs *****/
505 /***** Tabs *****/
489 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative; overflow:hidden;}
506 #content .tabs {height: 2.6em; border-bottom: 1px solid #bbbbbb; margin-bottom:1.2em; position:relative; overflow:hidden;}
490 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em; width: 2000px;}
507 #content .tabs ul {margin:0; position:absolute; bottom:-2px; padding-left:1em; width: 2000px;}
491 #content .tabs>ul { bottom:-1px; } /* others */
508 #content .tabs>ul { bottom:-1px; } /* others */
492 #content .tabs ul li {
509 #content .tabs ul li {
493 float:left;
510 float:left;
494 list-style-type:none;
511 list-style-type:none;
495 white-space:nowrap;
512 white-space:nowrap;
496 margin-right:8px;
513 margin-right:8px;
497 background:#fff;
514 background:#fff;
498 }
515 }
499 #content .tabs ul li a{
516 #content .tabs ul li a{
500 display:block;
517 display:block;
501 font-size: 0.9em;
518 font-size: 0.9em;
502 text-decoration:none;
519 text-decoration:none;
503 line-height:1.3em;
520 line-height:1.3em;
504 padding:4px 6px 4px 6px;
521 padding:4px 6px 4px 6px;
505 border: 1px solid #ccc;
522 border: 1px solid #ccc;
506 border-bottom: 1px solid #bbbbbb;
523 border-bottom: 1px solid #bbbbbb;
507 background-color: #eeeeee;
524 background-color: #eeeeee;
508 color:#777;
525 color:#777;
509 font-weight:bold;
526 font-weight:bold;
510 }
527 }
511
528
512 #content .tabs ul li a:hover {
529 #content .tabs ul li a:hover {
513 background-color: #ffffdd;
530 background-color: #ffffdd;
514 text-decoration:none;
531 text-decoration:none;
515 }
532 }
516
533
517 #content .tabs ul li a.selected {
534 #content .tabs ul li a.selected {
518 background-color: #fff;
535 background-color: #fff;
519 border: 1px solid #bbbbbb;
536 border: 1px solid #bbbbbb;
520 border-bottom: 1px solid #fff;
537 border-bottom: 1px solid #fff;
521 }
538 }
522
539
523 #content .tabs ul li a.selected:hover {
540 #content .tabs ul li a.selected:hover {
524 background-color: #fff;
541 background-color: #fff;
525 }
542 }
526
543
527 div.tabs-buttons { position:absolute; right: 0; width: 48px; height: 24px; background: white; bottom: -1px; }
544 div.tabs-buttons { position:absolute; right: 0; width: 48px; height: 24px; background: white; bottom: -1px; }
528
545
529 button.tab-left, button.tab-right {
546 button.tab-left, button.tab-right {
530 font-size: 0.9em;
547 font-size: 0.9em;
531 cursor: pointer;
548 cursor: pointer;
532 height:24px;
549 height:24px;
533 border: 1px solid #ccc;
550 border: 1px solid #ccc;
534 border-bottom: 1px solid #bbbbbb;
551 border-bottom: 1px solid #bbbbbb;
535 position:absolute;
552 position:absolute;
536 padding:4px;
553 padding:4px;
537 width: 20px;
554 width: 20px;
538 }
555 }
539
556
540 button.tab-left {
557 button.tab-left {
541 right: 20px;
558 right: 20px;
542 bottom: 0;
559 bottom: 0;
543 background: #eeeeee url(../images/bullet_arrow_left.png) no-repeat 50% 50%;
560 background: #eeeeee url(../images/bullet_arrow_left.png) no-repeat 50% 50%;
544 }
561 }
545
562
546 button.tab-right {
563 button.tab-right {
547 right: 0;
564 right: 0;
548 bottom: 0;
565 bottom: 0;
549 background: #eeeeee url(../images/bullet_arrow_right.png) no-repeat 50% 50%;}
566 background: #eeeeee url(../images/bullet_arrow_right.png) no-repeat 50% 50%;}
550 }
567 }
551
568
552 /***** Auto-complete *****/
569 /***** Auto-complete *****/
553 div.autocomplete {
570 div.autocomplete {
554 position:absolute;
571 position:absolute;
555 width:250px;
572 width:250px;
556 background-color:white;
573 background-color:white;
557 margin:0;
574 margin:0;
558 padding:0;
575 padding:0;
559 }
576 }
560 div.autocomplete ul {
577 div.autocomplete ul {
561 list-style-type:none;
578 list-style-type:none;
562 margin:0;
579 margin:0;
563 padding:0;
580 padding:0;
564 }
581 }
565 div.autocomplete ul li.selected { background-color: #ffb;}
582 div.autocomplete ul li.selected { background-color: #ffb;}
566 div.autocomplete ul li {
583 div.autocomplete ul li {
567 list-style-type:none;
584 list-style-type:none;
568 display:block;
585 display:block;
569 margin:0;
586 margin:0;
570 padding:2px;
587 padding:2px;
571 cursor:pointer;
588 cursor:pointer;
572 font-size: 90%;
589 font-size: 90%;
573 border-bottom: 1px solid #ccc;
590 border-bottom: 1px solid #ccc;
574 border-left: 1px solid #ccc;
591 border-left: 1px solid #ccc;
575 border-right: 1px solid #ccc;
592 border-right: 1px solid #ccc;
576 }
593 }
577 div.autocomplete ul li span.informal {
594 div.autocomplete ul li span.informal {
578 font-size: 80%;
595 font-size: 80%;
579 color: #aaa;
596 color: #aaa;
580 }
597 }
581
598
582 /***** Diff *****/
599 /***** Diff *****/
583 .diff_out { background: #fcc; }
600 .diff_out { background: #fcc; }
584 .diff_in { background: #cfc; }
601 .diff_in { background: #cfc; }
585
602
586 /***** Wiki *****/
603 /***** Wiki *****/
587 div.wiki table {
604 div.wiki table {
588 border: 1px solid #505050;
605 border: 1px solid #505050;
589 border-collapse: collapse;
606 border-collapse: collapse;
590 margin-bottom: 1em;
607 margin-bottom: 1em;
591 }
608 }
592
609
593 div.wiki table, div.wiki td, div.wiki th {
610 div.wiki table, div.wiki td, div.wiki th {
594 border: 1px solid #bbb;
611 border: 1px solid #bbb;
595 padding: 4px;
612 padding: 4px;
596 }
613 }
597
614
598 div.wiki .external {
615 div.wiki .external {
599 background-position: 0% 60%;
616 background-position: 0% 60%;
600 background-repeat: no-repeat;
617 background-repeat: no-repeat;
601 padding-left: 12px;
618 padding-left: 12px;
602 background-image: url(../images/external.png);
619 background-image: url(../images/external.png);
603 }
620 }
604
621
605 div.wiki a.new {
622 div.wiki a.new {
606 color: #b73535;
623 color: #b73535;
607 }
624 }
608
625
609 div.wiki pre {
626 div.wiki pre {
610 margin: 1em 1em 1em 1.6em;
627 margin: 1em 1em 1em 1.6em;
611 padding: 2px;
628 padding: 2px;
612 background-color: #fafafa;
629 background-color: #fafafa;
613 border: 1px solid #dadada;
630 border: 1px solid #dadada;
614 width:95%;
631 width:95%;
615 overflow-x: auto;
632 overflow-x: auto;
616 }
633 }
617
634
618 div.wiki ul.toc {
635 div.wiki ul.toc {
619 background-color: #ffffdd;
636 background-color: #ffffdd;
620 border: 1px solid #e4e4e4;
637 border: 1px solid #e4e4e4;
621 padding: 4px;
638 padding: 4px;
622 line-height: 1.2em;
639 line-height: 1.2em;
623 margin-bottom: 12px;
640 margin-bottom: 12px;
624 margin-right: 12px;
641 margin-right: 12px;
625 margin-left: 0;
642 margin-left: 0;
626 display: table
643 display: table
627 }
644 }
628 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
645 * html div.wiki ul.toc { width: 50%; } /* IE6 doesn't autosize div */
629
646
630 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
647 div.wiki ul.toc.right { float: right; margin-left: 12px; margin-right: 0; width: auto; }
631 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
648 div.wiki ul.toc.left { float: left; margin-right: 12px; margin-left: 0; width: auto; }
632 div.wiki ul.toc li { list-style-type:none;}
649 div.wiki ul.toc li { list-style-type:none;}
633 div.wiki ul.toc li.heading2 { margin-left: 6px; }
650 div.wiki ul.toc li.heading2 { margin-left: 6px; }
634 div.wiki ul.toc li.heading3 { margin-left: 12px; font-size: 0.8em; }
651 div.wiki ul.toc li.heading3 { margin-left: 12px; font-size: 0.8em; }
635
652
636 div.wiki ul.toc a {
653 div.wiki ul.toc a {
637 font-size: 0.9em;
654 font-size: 0.9em;
638 font-weight: normal;
655 font-weight: normal;
639 text-decoration: none;
656 text-decoration: none;
640 color: #606060;
657 color: #606060;
641 }
658 }
642 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
659 div.wiki ul.toc a:hover { color: #c61a1a; text-decoration: underline;}
643
660
644 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
661 a.wiki-anchor { display: none; margin-left: 6px; text-decoration: none; }
645 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
662 a.wiki-anchor:hover { color: #aaa !important; text-decoration: none; }
646 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
663 h1:hover a.wiki-anchor, h2:hover a.wiki-anchor, h3:hover a.wiki-anchor { display: inline; color: #ddd; }
647
664
648 /***** My page layout *****/
665 /***** My page layout *****/
649 .block-receiver {
666 .block-receiver {
650 border:1px dashed #c0c0c0;
667 border:1px dashed #c0c0c0;
651 margin-bottom: 20px;
668 margin-bottom: 20px;
652 padding: 15px 0 15px 0;
669 padding: 15px 0 15px 0;
653 }
670 }
654
671
655 .mypage-box {
672 .mypage-box {
656 margin:0 0 20px 0;
673 margin:0 0 20px 0;
657 color:#505050;
674 color:#505050;
658 line-height:1.5em;
675 line-height:1.5em;
659 }
676 }
660
677
661 .handle {
678 .handle {
662 cursor: move;
679 cursor: move;
663 }
680 }
664
681
665 a.close-icon {
682 a.close-icon {
666 display:block;
683 display:block;
667 margin-top:3px;
684 margin-top:3px;
668 overflow:hidden;
685 overflow:hidden;
669 width:12px;
686 width:12px;
670 height:12px;
687 height:12px;
671 background-repeat: no-repeat;
688 background-repeat: no-repeat;
672 cursor:pointer;
689 cursor:pointer;
673 background-image:url('../images/close.png');
690 background-image:url('../images/close.png');
674 }
691 }
675
692
676 a.close-icon:hover {
693 a.close-icon:hover {
677 background-image:url('../images/close_hl.png');
694 background-image:url('../images/close_hl.png');
678 }
695 }
679
696
680 /***** Gantt chart *****/
697 /***** Gantt chart *****/
681 .gantt_hdr {
698 .gantt_hdr {
682 position:absolute;
699 position:absolute;
683 top:0;
700 top:0;
684 height:16px;
701 height:16px;
685 border-top: 1px solid #c0c0c0;
702 border-top: 1px solid #c0c0c0;
686 border-bottom: 1px solid #c0c0c0;
703 border-bottom: 1px solid #c0c0c0;
687 border-right: 1px solid #c0c0c0;
704 border-right: 1px solid #c0c0c0;
688 text-align: center;
705 text-align: center;
689 overflow: hidden;
706 overflow: hidden;
690 }
707 }
691
708
692 .task {
709 .task {
693 position: absolute;
710 position: absolute;
694 height:8px;
711 height:8px;
695 font-size:0.8em;
712 font-size:0.8em;
696 color:#888;
713 color:#888;
697 padding:0;
714 padding:0;
698 margin:0;
715 margin:0;
699 line-height:0.8em;
716 line-height:0.8em;
700 }
717 }
701
718
702 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
719 .task_late { background:#f66 url(../images/task_late.png); border: 1px solid #f66; }
703 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
720 .task_done { background:#66f url(../images/task_done.png); border: 1px solid #66f; }
704 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
721 .task_todo { background:#aaa url(../images/task_todo.png); border: 1px solid #aaa; }
705 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
722 .milestone { background-image:url(../images/milestone.png); background-repeat: no-repeat; border: 0; }
706
723
707 /***** Icons *****/
724 /***** Icons *****/
708 .icon {
725 .icon {
709 background-position: 0% 40%;
726 background-position: 0% 40%;
710 background-repeat: no-repeat;
727 background-repeat: no-repeat;
711 padding-left: 20px;
728 padding-left: 20px;
712 padding-top: 2px;
729 padding-top: 2px;
713 padding-bottom: 3px;
730 padding-bottom: 3px;
714 }
731 }
715
732
716 .icon22 {
733 .icon22 {
717 background-position: 0% 40%;
734 background-position: 0% 40%;
718 background-repeat: no-repeat;
735 background-repeat: no-repeat;
719 padding-left: 26px;
736 padding-left: 26px;
720 line-height: 22px;
737 line-height: 22px;
721 vertical-align: middle;
738 vertical-align: middle;
722 }
739 }
723
740
724 .icon-add { background-image: url(../images/add.png); }
741 .icon-add { background-image: url(../images/add.png); }
725 .icon-edit { background-image: url(../images/edit.png); }
742 .icon-edit { background-image: url(../images/edit.png); }
726 .icon-copy { background-image: url(../images/copy.png); }
743 .icon-copy { background-image: url(../images/copy.png); }
727 .icon-duplicate { background-image: url(../images/duplicate.png); }
744 .icon-duplicate { background-image: url(../images/duplicate.png); }
728 .icon-del { background-image: url(../images/delete.png); }
745 .icon-del { background-image: url(../images/delete.png); }
729 .icon-move { background-image: url(../images/move.png); }
746 .icon-move { background-image: url(../images/move.png); }
730 .icon-save { background-image: url(../images/save.png); }
747 .icon-save { background-image: url(../images/save.png); }
731 .icon-cancel { background-image: url(../images/cancel.png); }
748 .icon-cancel { background-image: url(../images/cancel.png); }
732 .icon-multiple { background-image: url(../images/table_multiple.png); }
749 .icon-multiple { background-image: url(../images/table_multiple.png); }
733 .icon-folder { background-image: url(../images/folder.png); }
750 .icon-folder { background-image: url(../images/folder.png); }
734 .open .icon-folder { background-image: url(../images/folder_open.png); }
751 .open .icon-folder { background-image: url(../images/folder_open.png); }
735 .icon-package { background-image: url(../images/package.png); }
752 .icon-package { background-image: url(../images/package.png); }
736 .icon-home { background-image: url(../images/home.png); }
753 .icon-home { background-image: url(../images/home.png); }
737 .icon-user { background-image: url(../images/user.png); }
754 .icon-user { background-image: url(../images/user.png); }
738 .icon-mypage { background-image: url(../images/user_page.png); }
755 .icon-mypage { background-image: url(../images/user_page.png); }
739 .icon-admin { background-image: url(../images/admin.png); }
756 .icon-admin { background-image: url(../images/admin.png); }
740 .icon-projects { background-image: url(../images/projects.png); }
757 .icon-projects { background-image: url(../images/projects.png); }
741 .icon-help { background-image: url(../images/help.png); }
758 .icon-help { background-image: url(../images/help.png); }
742 .icon-attachment { background-image: url(../images/attachment.png); }
759 .icon-attachment { background-image: url(../images/attachment.png); }
743 .icon-index { background-image: url(../images/index.png); }
760 .icon-index { background-image: url(../images/index.png); }
744 .icon-history { background-image: url(../images/history.png); }
761 .icon-history { background-image: url(../images/history.png); }
745 .icon-time { background-image: url(../images/time.png); }
762 .icon-time { background-image: url(../images/time.png); }
746 .icon-time-add { background-image: url(../images/time_add.png); }
763 .icon-time-add { background-image: url(../images/time_add.png); }
747 .icon-stats { background-image: url(../images/stats.png); }
764 .icon-stats { background-image: url(../images/stats.png); }
748 .icon-warning { background-image: url(../images/warning.png); }
765 .icon-warning { background-image: url(../images/warning.png); }
749 .icon-fav { background-image: url(../images/fav.png); }
766 .icon-fav { background-image: url(../images/fav.png); }
750 .icon-fav-off { background-image: url(../images/fav_off.png); }
767 .icon-fav-off { background-image: url(../images/fav_off.png); }
751 .icon-reload { background-image: url(../images/reload.png); }
768 .icon-reload { background-image: url(../images/reload.png); }
752 .icon-lock { background-image: url(../images/locked.png); }
769 .icon-lock { background-image: url(../images/locked.png); }
753 .icon-unlock { background-image: url(../images/unlock.png); }
770 .icon-unlock { background-image: url(../images/unlock.png); }
754 .icon-checked { background-image: url(../images/true.png); }
771 .icon-checked { background-image: url(../images/true.png); }
755 .icon-details { background-image: url(../images/zoom_in.png); }
772 .icon-details { background-image: url(../images/zoom_in.png); }
756 .icon-report { background-image: url(../images/report.png); }
773 .icon-report { background-image: url(../images/report.png); }
757 .icon-comment { background-image: url(../images/comment.png); }
774 .icon-comment { background-image: url(../images/comment.png); }
758 .icon-summary { background-image: url(../images/lightning.png); }
775 .icon-summary { background-image: url(../images/lightning.png); }
759
776
760 .icon-file { background-image: url(../images/files/default.png); }
777 .icon-file { background-image: url(../images/files/default.png); }
761 .icon-file.text-plain { background-image: url(../images/files/text.png); }
778 .icon-file.text-plain { background-image: url(../images/files/text.png); }
762 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
779 .icon-file.text-x-c { background-image: url(../images/files/c.png); }
763 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
780 .icon-file.text-x-csharp { background-image: url(../images/files/csharp.png); }
764 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
781 .icon-file.text-x-php { background-image: url(../images/files/php.png); }
765 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
782 .icon-file.text-x-ruby { background-image: url(../images/files/ruby.png); }
766 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
783 .icon-file.text-xml { background-image: url(../images/files/xml.png); }
767 .icon-file.image-gif { background-image: url(../images/files/image.png); }
784 .icon-file.image-gif { background-image: url(../images/files/image.png); }
768 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
785 .icon-file.image-jpeg { background-image: url(../images/files/image.png); }
769 .icon-file.image-png { background-image: url(../images/files/image.png); }
786 .icon-file.image-png { background-image: url(../images/files/image.png); }
770 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
787 .icon-file.image-tiff { background-image: url(../images/files/image.png); }
771 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
788 .icon-file.application-pdf { background-image: url(../images/files/pdf.png); }
772 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
789 .icon-file.application-zip { background-image: url(../images/files/zip.png); }
773 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
790 .icon-file.application-x-gzip { background-image: url(../images/files/zip.png); }
774
791
775 .icon22-projects { background-image: url(../images/22x22/projects.png); }
792 .icon22-projects { background-image: url(../images/22x22/projects.png); }
776 .icon22-users { background-image: url(../images/22x22/users.png); }
793 .icon22-users { background-image: url(../images/22x22/users.png); }
777 .icon22-groups { background-image: url(../images/22x22/groups.png); }
794 .icon22-groups { background-image: url(../images/22x22/groups.png); }
778 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
795 .icon22-tracker { background-image: url(../images/22x22/tracker.png); }
779 .icon22-role { background-image: url(../images/22x22/role.png); }
796 .icon22-role { background-image: url(../images/22x22/role.png); }
780 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
797 .icon22-workflow { background-image: url(../images/22x22/workflow.png); }
781 .icon22-options { background-image: url(../images/22x22/options.png); }
798 .icon22-options { background-image: url(../images/22x22/options.png); }
782 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
799 .icon22-notifications { background-image: url(../images/22x22/notifications.png); }
783 .icon22-authent { background-image: url(../images/22x22/authent.png); }
800 .icon22-authent { background-image: url(../images/22x22/authent.png); }
784 .icon22-info { background-image: url(../images/22x22/info.png); }
801 .icon22-info { background-image: url(../images/22x22/info.png); }
785 .icon22-comment { background-image: url(../images/22x22/comment.png); }
802 .icon22-comment { background-image: url(../images/22x22/comment.png); }
786 .icon22-package { background-image: url(../images/22x22/package.png); }
803 .icon22-package { background-image: url(../images/22x22/package.png); }
787 .icon22-settings { background-image: url(../images/22x22/settings.png); }
804 .icon22-settings { background-image: url(../images/22x22/settings.png); }
788 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
805 .icon22-plugin { background-image: url(../images/22x22/plugin.png); }
789
806
790 img.gravatar {
807 img.gravatar {
791 padding: 2px;
808 padding: 2px;
792 border: solid 1px #d5d5d5;
809 border: solid 1px #d5d5d5;
793 background: #fff;
810 background: #fff;
794 }
811 }
795
812
796 div.issue img.gravatar {
813 div.issue img.gravatar {
797 float: right;
814 float: right;
798 margin: 0 0 0 1em;
815 margin: 0 0 0 1em;
799 padding: 5px;
816 padding: 5px;
800 }
817 }
801
818
802 div.issue table img.gravatar {
819 div.issue table img.gravatar {
803 height: 14px;
820 height: 14px;
804 width: 14px;
821 width: 14px;
805 padding: 2px;
822 padding: 2px;
806 float: left;
823 float: left;
807 margin: 0 0.5em 0 0;
824 margin: 0 0.5em 0 0;
808 }
825 }
809
826
810 #history img.gravatar {
827 #history img.gravatar {
811 padding: 3px;
828 padding: 3px;
812 margin: 0 1.5em 1em 0;
829 margin: 0 1.5em 1em 0;
813 float: left;
830 float: left;
814 }
831 }
815
832
816 td.username img.gravatar {
833 td.username img.gravatar {
817 float: left;
834 float: left;
818 margin: 0 1em 0 0;
835 margin: 0 1em 0 0;
819 }
836 }
820
837
821 #activity dt img.gravatar {
838 #activity dt img.gravatar {
822 float: left;
839 float: left;
823 margin: 0 1em 1em 0;
840 margin: 0 1em 1em 0;
824 }
841 }
825
842
826 #activity dt,
843 #activity dt,
827 .journal {
844 .journal {
828 clear: left;
845 clear: left;
829 }
846 }
830
847
831 .gravatar-margin {
848 .gravatar-margin {
832 margin-left: 40px;
849 margin-left: 40px;
833 }
850 }
834
851
835 h2 img { vertical-align:middle; }
852 h2 img { vertical-align:middle; }
836
853
837
854
838 /***** Media print specific styles *****/
855 /***** Media print specific styles *****/
839 @media print {
856 @media print {
840 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
857 #top-menu, #header, #main-menu, #sidebar, #footer, .contextual, .other-formats { display:none; }
841 #main { background: #fff; }
858 #main { background: #fff; }
842 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
859 #content { width: 99%; margin: 0; padding: 0; border: 0; background: #fff; overflow: visible !important;}
843 #wiki_add_attachment { display:none; }
860 #wiki_add_attachment { display:none; }
844 }
861 }
General Comments 0
You need to be logged in to leave comments. Login now